diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml index d23589260fb7..4703909d98af 100644 --- a/.github/workflows/compile_changelogs.yml +++ b/.github/workflows/compile_changelogs.yml @@ -2,7 +2,7 @@ name: Compile changelogs on: schedule: - - cron: "0 0 * * *" + - cron: "0 0 * * 1" workflow_dispatch: jobs: diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 1a8bc4d3e910..7c6f73b3320c 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,8 +18,8 @@ jobs: - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~ 7 days, please address any outstanding review items and ensure your PR is finished, if these are all true and you are auto-staled anyway, you need to actively ask maintainers if your PR will be merged. Once you have done any of the previous actions then you should request a maintainer remove the stale label on your PR, to reset the stale timer. If you feel no maintainer will respond in that time, you may wish to close this PR youself, while you seek maintainer comment, as you will then be able to reopen the PR yourself" - stale-issue-message: "This issue has been marked for cleanup and will be automatically closed in ~ 14 days. If there is evidence that the issue still occurs, leave a comment with it attached and contact a maintainer to have the label removed." + stale-pr-message: "This PR has been inactive for long enough to be automatically marked as stale. This means it is at risk of being auto closed in ~7 days. Please address any outstanding review items and ensure your PR is finished - if both are true, and you have auto-staled anyway, you need to actively ask maintainers (by pinging them in the /tg/station Discord) to (re)review or merge your PR. If no maintainer responds to your request, you may wish to close this PR yourself while you seek maintainer comment, as you will later be able to reopen the PR yourself." + stale-issue-message: "This issue has been marked for cleanup and will be automatically closed in ~14 days. If there is evidence that the issue still occurs, leave a comment with it attached and contact an issue manager or maintainer to have the label removed." days-before-stale: 7 days-before-close: 7 days-before-issue-stale: 14 diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm index a5fa97ec97ba..702ebb0c0b68 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm @@ -18,6 +18,7 @@ /area/ruin/powered/hermit) "dY" = ( /obj/structure/sink/directional/south, +/obj/structure/glowshroom/single, /turf/open/floor/plating, /area/ruin/powered/hermit) "gr" = ( @@ -51,8 +52,7 @@ /turf/closed/wall/mineral/wood, /area/icemoon/underground/explored) "oJ" = ( -/obj/structure/glowshroom/single, -/turf/open/floor/plating, +/turf/closed/wall, /area/ruin/powered/hermit) "ph" = ( /turf/closed/mineral/snowmountain/icemoon/unscrapeable, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_battle_site.dmm b/_maps/RandomRuins/LavaRuins/lavaland_battle_site.dmm index b2c263e7971d..ea41e728846f 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_battle_site.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_battle_site.dmm @@ -105,10 +105,6 @@ /obj/item/stack/sheet/sinew, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"L" = ( -/obj/effect/decal/cleanable/blood/drip, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "M" = ( /obj/structure/statue/bone/skull, /turf/open/misc/asteroid/basalt/lava_land_surface, @@ -253,7 +249,7 @@ I G G q -L +x G s g diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm index 41b4510ea345..c03669ad0b52 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm @@ -361,6 +361,7 @@ /obj/effect/turf_decal/trimline/green/line{ dir = 1 }, +/obj/structure/tank_holder/extinguisher, /turf/open/floor/mineral/plastitanium, /area/ruin/powered/seedvault) "bf" = ( diff --git a/_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm b/_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm index 86d4f3d4c832..b67d0f00080a 100644 --- a/_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm +++ b/_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm @@ -666,7 +666,7 @@ /turf/open/floor/catwalk_floor/iron, /area/ruin/space/has_grav/garbagetruck/medicalwaste) "XD" = ( -/obj/structure/sinkframe, +/obj/item/wallframe/sinkframe, /obj/structure/broken_flooring/pile, /obj/effect/decal/cleanable/blood/gibs/old, /obj/item/rag{ diff --git a/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm b/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm index 627842aa210c..7b000adb8359 100644 --- a/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm +++ b/_maps/RandomRuins/SpaceRuins/garbagetruck4.dmm @@ -600,7 +600,7 @@ "Ix" = ( /obj/structure/closet/cardboard, /obj/item/bodypart/chest/robot, -/obj/item/toy/katana, +/obj/item/storage/belt/sheath/katana/toy, /obj/item/toy/plush/snakeplushie, /turf/open/floor/plating, /area/ruin/space/has_grav/garbagetruck/toystore) diff --git a/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm b/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm index 075260585625..3b1a2f801190 100644 --- a/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm +++ b/_maps/RandomRuins/SpaceRuins/hauntedtradingpost.dmm @@ -4068,9 +4068,7 @@ pixel_y = -2; pixel_x = -8 }, -/obj/structure/sinkframe{ - pixel_x = 2 - }, +/obj/item/wallframe/sinkframe, /turf/open/floor/plating, /area/ruin/space/has_grav/hauntedtradingpost/maint) "Ju" = ( diff --git a/_maps/RandomZLevels/heretic.dmm b/_maps/RandomZLevels/heretic.dmm index 2af0314e0983..768ce5698b65 100644 --- a/_maps/RandomZLevels/heretic.dmm +++ b/_maps/RandomZLevels/heretic.dmm @@ -5808,12 +5808,6 @@ /obj/effect/spawner/random/food_or_drink/seed_rare, /turf/open/misc/grass/jungle/station, /area/awaymission/beach/heretic) -"EH" = ( -/obj/machinery/computer/shuttle/ferry/request{ - dir = 4 - }, -/turf/open/floor/pod/light, -/area/awaymission/beach/heretic) "EI" = ( /turf/open/water/beach, /area/awaymission/caves/heretic_laboratory_clean) @@ -42217,7 +42211,7 @@ wf wf wf bb -EH +BV gT wf wf diff --git a/_maps/catwalkstation.json b/_maps/catwalkstation.json index f4a592346851..9b87000ac55c 100644 --- a/_maps/catwalkstation.json +++ b/_maps/catwalkstation.json @@ -20,5 +20,6 @@ "Baseturf": "/turf/open/openspace/airless", "Linkage": "Cross" } - ] + ], + "bonus_weakpoints": 1 } diff --git a/_maps/deltastation.json b/_maps/deltastation.json index 85684e11a53f..0fff4c1b36e2 100644 --- a/_maps/deltastation.json +++ b/_maps/deltastation.json @@ -14,5 +14,6 @@ "Cook": { "additional_cqc_areas": ["/area/station/service/cafeteria"] } - } + }, + "bonus_weakpoints": 2 } diff --git a/_maps/icebox.json b/_maps/icebox.json index ee56e84000c8..bc31ec9ac81e 100644 --- a/_maps/icebox.json +++ b/_maps/icebox.json @@ -59,5 +59,6 @@ "Cook": { "additional_cqc_areas": ["/area/station/service/bar/atrium"] } - } + }, + "bonus_weakpoints": 2 } diff --git a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm index 9abcee17aa97..9cde40b8d1ad 100644 --- a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm +++ b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm @@ -20666,6 +20666,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, /obj/effect/landmark/navigate_destination/vault, +/obj/effect/mapping_helpers/airlock/access/all/supply/vault, /turf/open/floor/iron/dark, /area/station/command/vault) "fxH" = ( @@ -32494,6 +32495,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/all/supply/vault, /turf/open/floor/iron/dark, /area/station/command/vault) "iEH" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 01062130dac7..393f2d8931b9 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -54829,7 +54829,7 @@ dir = 4 }, /obj/structure/table, -/obj/item/toy/katana, +/obj/item/storage/belt/sheath/katana/toy, /obj/item/gun/ballistic/shotgun/toy/crossbow, /obj/effect/turf_decal/tile/neutral/half/contrasted, /turf/open/floor/iron, diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 8e839ff2e7f2..1c11ddaab68d 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -4054,6 +4054,8 @@ "bec" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "ben" = ( @@ -5123,6 +5125,17 @@ }, /turf/open/floor/iron/checker, /area/station/commons/storage/emergency/port) +"bsq" = ( +/obj/machinery/power/weather_tower, +/obj/structure/cable, +/obj/effect/turf_decal/weather/snow/corner{ + dir = 10 + }, +/obj/effect/turf_decal/weather/snow/corner{ + dir = 9 + }, +/turf/open/floor/plating/snowed/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "bsG" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/research/glass{ @@ -8591,7 +8604,6 @@ /turf/open/floor/iron, /area/station/commons/storage/mining) "coc" = ( -/obj/structure/cable, /obj/machinery/camera/autoname/directional/north, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) @@ -8728,7 +8740,6 @@ /area/station/service/kitchen) "cqa" = ( /obj/item/radio/intercom/directional/north, -/obj/structure/cable, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "cqb" = ( @@ -9333,6 +9344,7 @@ name = "External Access" }, /obj/effect/mapping_helpers/airlock/access/all/engineering/external, +/obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/port/fore) "cyI" = ( @@ -9655,7 +9667,6 @@ /area/station/maintenance/starboard/aft) "cDt" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "cDv" = ( @@ -17659,6 +17670,11 @@ /obj/machinery/telecomms/broadcaster/preset_left, /turf/open/floor/iron/dark/telecomms, /area/station/tcommsat/server) +"eTn" = ( +/obj/machinery/power/weather_tower, +/obj/structure/cable, +/turf/open/floor/plating/snowed/smoothed/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "eTv" = ( /obj/machinery/door/airlock/security{ name = "Permanent Cell 1" @@ -19218,6 +19234,10 @@ /obj/structure/closet/emcloset/anchored, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"foU" = ( +/obj/structure/cable, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/underground/explored) "foW" = ( /obj/effect/turf_decal/stripes/line, /turf/open/floor/engine, @@ -19928,6 +19948,10 @@ /obj/effect/landmark/start/geneticist, /turf/open/floor/iron/white, /area/station/science/genetics) +"fzU" = ( +/obj/structure/cable, +/turf/open/floor/plating/snowed/smoothed/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "fAF" = ( /obj/structure/rack, /obj/item/clothing/gloves/boxing/green, @@ -21977,7 +22001,6 @@ /turf/open/floor/iron/cafeteria, /area/station/commons/storage/art) "gfV" = ( -/obj/structure/cable, /obj/machinery/light/small/directional/north, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) @@ -24289,6 +24312,11 @@ }, /turf/open/openspace, /area/station/cargo/storage) +"gOP" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/starboard/upper) "gOS" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -25431,8 +25459,6 @@ /turf/open/floor/iron/textured, /area/station/maintenance/starboard/fore) "hfo" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/stripes/corner{ dir = 8 }, @@ -28846,6 +28872,7 @@ }, /obj/machinery/door/airlock/external, /obj/effect/mapping_helpers/airlock/access/all/engineering/external, +/obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) "icY" = ( @@ -29668,6 +29695,13 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/medical/central) +"ipB" = ( +/obj/effect/turf_decal/weather/snow/corner{ + dir = 4 + }, +/obj/structure/cable, +/turf/open/floor/plating/snowed/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "ipE" = ( /turf/open/floor/plating, /area/station/maintenance/solars/port/aft) @@ -33137,6 +33171,7 @@ "jrX" = ( /obj/item/cigbutt, /obj/structure/sign/warning/cold_temp/directional/north, +/obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) "jrZ" = ( @@ -34817,7 +34852,7 @@ /area/station/cargo/storage) "jPf" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 + dir = 1 }, /obj/effect/landmark/start/hangover, /turf/open/floor/iron/dark, @@ -39439,7 +39474,6 @@ dir = 4 }, /obj/structure/cable, -/obj/machinery/door/firedoor, /turf/open/floor/plating, /area/station/engineering/lobby) "lch" = ( @@ -39455,13 +39489,13 @@ /turf/open/floor/iron/dark/smooth_large, /area/station/command/gateway) "lco" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/disposalpipe/segment, /obj/structure/railing/corner/end/flip{ dir = 8 }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "lcs" = ( @@ -40388,6 +40422,11 @@ "lpM" = ( /turf/closed/wall/r_wall, /area/station/command/heads_quarters/captain) +"lpQ" = ( +/obj/effect/landmark/start/hangover, +/obj/structure/cable, +/turf/open/floor/iron/smooth, +/area/station/engineering/lobby) "lpS" = ( /obj/machinery/disposal/bin, /obj/structure/disposalpipe/trunk, @@ -42370,12 +42409,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark/smooth_half, /area/station/security/office) -"lRH" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/landmark/start/hangover, -/turf/open/floor/iron/smooth, -/area/station/engineering/lobby) "lRI" = ( /obj/machinery/door/airlock/external{ glass = 1; @@ -44952,6 +44985,14 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/medical/medbay/aft) +"mGR" = ( +/obj/structure/cable, +/obj/effect/turf_decal/weather/snow/corner{ + dir = 1 + }, +/obj/effect/turf_decal/weather/snow/corner, +/turf/open/floor/plating/snowed/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "mHa" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -45878,6 +45919,17 @@ /mob/living/simple_animal/bot/secbot/pingsky, /turf/open/floor/iron/dark, /area/station/ai/satellite/interior) +"mXj" = ( +/obj/machinery/door/airlock/external{ + name = "External Access" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/access/all/engineering/external, +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/port/fore) "mXl" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/camera/directional/east{ @@ -49160,7 +49212,6 @@ /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 1 }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, @@ -54911,6 +54962,8 @@ /obj/effect/decal/cleanable/dirt, /obj/structure/cable, /obj/structure/disposalpipe/segment, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) "prs" = ( @@ -56391,6 +56444,10 @@ }, /turf/open/floor/iron/freezer, /area/station/commons/toilet/locker) +"pLJ" = ( +/obj/structure/cable, +/turf/open/floor/plating, +/area/station/maintenance/starboard/upper) "pLS" = ( /obj/machinery/mineral/stacking_machine{ output_dir = 2; @@ -64979,6 +65036,10 @@ dir = 1 }, /area/station/command/heads_quarters/rd) +"sgM" = ( +/obj/structure/cable, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/surface/outdoors/less_spawns) "sgT" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/blue{ @@ -68491,7 +68552,6 @@ "tfC" = ( /obj/machinery/holopad, /obj/effect/turf_decal/bot, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, /turf/open/floor/iron/dark, @@ -72862,6 +72922,11 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/circuit, /area/station/ai/satellite/hallway) +"urV" = ( +/obj/machinery/power/weather_tower, +/obj/structure/cable, +/turf/open/floor/plating/snowed/smoothed/icemoon, +/area/icemoon/underground/explored) "usm" = ( /obj/machinery/airalarm/directional/east, /turf/open/floor/plating, @@ -82675,7 +82740,6 @@ "xob" = ( /obj/effect/spawner/structure/window/hollow/reinforced/middle, /obj/structure/cable, -/obj/machinery/door/firedoor, /turf/open/floor/plating, /area/station/engineering/lobby) "xow" = ( @@ -84240,8 +84304,6 @@ /turf/open/floor/iron/dark, /area/station/engineering/supermatter/room) "xJY" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/firealarm/directional/north, /turf/open/floor/iron/smooth, /area/station/engineering/lobby) @@ -84284,6 +84346,7 @@ }, /obj/machinery/door/airlock/external, /obj/effect/mapping_helpers/airlock/access/all/engineering/external, +/obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) "xKq" = ( @@ -108814,7 +108877,7 @@ iDt scw scw scw -scw +urV jai wkB scw @@ -174100,7 +174163,7 @@ lvQ sUE rsY jcA -scw +wrV jai iDt iDt @@ -174357,7 +174420,7 @@ rsY rsY rsY scw -scw +wrV qau iDt iDt @@ -174614,7 +174677,7 @@ cCb qQt scw scw -iDt +foU qau iDt iDt @@ -174871,7 +174934,7 @@ scw scw scw iDt -scw +urV jai jwz iDt @@ -182584,7 +182647,7 @@ thA iDt iDt bID -jJI +iWW thq xob gjq @@ -182842,7 +182905,7 @@ iDt scw bID gfV -muJ +lpQ bID bID bID @@ -183099,7 +183162,7 @@ thA ppY bID cqa -iWW +jJI tiJ uSA ayC @@ -183873,8 +183936,8 @@ bID lbF bID xJY -cDt -lRH +iWW +muJ cDt hfo oWy @@ -229802,9 +229865,9 @@ txl utC utC utC -utC -utC -utC +eTn +fzU +sgM dtP mGf lJO @@ -230061,7 +230124,7 @@ vOy utC utC tUm -utC +sgM lJO lJO lJO @@ -230318,11 +230381,11 @@ utC vfr utC fwx -fwx +fzU cyH -hjI -nbj -hjI +anl +mXj +anl uDc xyO eUf @@ -252528,7 +252591,7 @@ tkU utC utC utC -utC +tkU mtW fri utC @@ -252783,8 +252846,8 @@ utC fri tkU utC -tkU utC +bsq utC fuZ utC @@ -253040,8 +253103,8 @@ utC utC utC utC -utC -utC +tkU +mGR utC fuZ tkU @@ -253298,7 +253361,7 @@ oaQ qHg uAL uAL -uAL +aEX hHG liV sby @@ -267108,7 +267171,7 @@ cpT omh kJc ozE -fTW +pLJ dxm sHM elw @@ -267365,7 +267428,7 @@ pnj omh omh omh -dbw +gOP omh sHM elw @@ -268393,9 +268456,9 @@ vbG vbG vbG vbG -vbG -vbG -vbG +ipB +ipB +ipB vbG vbG omh @@ -268652,7 +268715,7 @@ fwx utC utC utC -utC +sgM fwx fwx omh @@ -268909,7 +268972,7 @@ utC utC fwx utC -fwx +eTn fwx utC utC diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index 276d55ba7dba..a1c171b5ebdf 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -1569,6 +1569,12 @@ }, /turf/open/floor/iron/checker, /area/mine/cafeteria) +"iI" = ( +/obj/machinery/power/weather_tower, +/obj/structure/lattice/catwalk, +/obj/structure/cable, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "iN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -3006,7 +3012,10 @@ /area/mine/laborcamp/production) "ps" = ( /obj/structure/cable, -/obj/machinery/power/smes/super/full, +/obj/machinery/power/smes/super/full{ + output_level = 100000; + input_level = 100000 + }, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/mine/maintenance/service) @@ -4222,6 +4231,10 @@ /obj/item/chair/wood, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) +"wG" = ( +/obj/structure/lattice/catwalk, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "wJ" = ( /obj/structure/table/wood/poker, /obj/item/toy/cards/deck{ @@ -35917,7 +35930,7 @@ pU pU pU pU -pU +iI pU pU pU @@ -36171,12 +36184,12 @@ ZY ZY pU pU -pU -pU -pU -pU -pU -pU +aD +aD +aD +aD +wG +wG pU pU pU @@ -36428,7 +36441,7 @@ ZY ZY pU pU -pU +aD AJ AJ AJ @@ -36685,7 +36698,7 @@ ZY pU pU pU -pU +aD TR Fs JM @@ -36942,7 +36955,7 @@ ZY pU pU pU -pU +aD mA su xG @@ -39250,8 +39263,8 @@ ZY ZY pU pU -pU -pU +iI +aD Qz ON ON @@ -40021,9 +40034,9 @@ ZY ZY pU pU -pU -pU -pU +iI +aD +aD ON ON ON diff --git a/_maps/map_files/NebulaStation/NebulaStation.dmm b/_maps/map_files/NebulaStation/NebulaStation.dmm index 38e79d5fbd6d..e927ecbc6bb2 100644 --- a/_maps/map_files/NebulaStation/NebulaStation.dmm +++ b/_maps/map_files/NebulaStation/NebulaStation.dmm @@ -53410,7 +53410,7 @@ /area/station/medical/lower) "hRk" = ( /obj/structure/table, -/obj/item/toy/katana, +/obj/item/storage/belt/sheath/katana/toy, /obj/item/gun/ballistic/shotgun/toy/crossbow, /obj/machinery/power/apc/auto_name/directional/north, /obj/structure/cable, @@ -80632,7 +80632,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/item/toy/katana, +/obj/item/storage/belt/sheath/katana/toy, /turf/open/floor/bamboo/tatami/black, /area/station/maintenance/starboard/central) "lTG" = ( diff --git a/_maps/map_files/Vampire/runtimetown.dmm b/_maps/map_files/Vampire/runtimetown.dmm index 4701ff550db0..8e176ba7fbf2 100644 --- a/_maps/map_files/Vampire/runtimetown.dmm +++ b/_maps/map_files/Vampire/runtimetown.dmm @@ -1,4 +1,16 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"aa" = ( +/obj/structure/guncase, +/obj/item/gun/ballistic/shotgun/vampire, +/obj/item/flashlight/lantern/on{ + pixel_x = -7; + pixel_y = 19 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) +"ag" = ( +/turf/open/misc/dirt, +/area/vtm/graveyard) "av" = ( /obj/structure/table, /obj/item/ammo_box/magazine/darkpack45acp, @@ -7,10 +19,6 @@ /obj/item/gun/ballistic/automatic/pistol/darkpack/m1911, /turf/open/floor/plating/rough, /area/vtm/interior/anarch) -"ax" = ( -/obj/structure/toiletbong, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "aM" = ( /obj/effect/spawner/random/trash/rat, /turf/open/misc/beach/vamp, @@ -27,12 +35,6 @@ /obj/structure/platform/lowwall/brick, /turf/open/floor/plating/rough, /area/vtm/interior/shop) -"bh" = ( -/obj/structure/chair/greyscale{ - dir = 8 - }, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "bj" = ( /obj/structure/closet/secure_closet/freezer, /turf/open/floor/city/toilet, @@ -48,6 +50,10 @@ /obj/structure/roadsign/crosswalk, /turf/open/floor/plating/sidewalk/poor, /area/vtm/outside/pacificheights) +"bA" = ( +/obj/structure/werewolf_totem/generic/weaver, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/techshop) "bB" = ( /obj/structure/chair/wood/darkpack/red, /turf/open/floor/plating/rough, @@ -63,6 +69,14 @@ /obj/structure/closet/secure_closet/medical2, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) +"bD" = ( +/obj/structure/table/wood, +/obj/effect/spawner/random/engineering/toolbox{ + pixel_x = 2; + pixel_y = 2 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "bK" = ( /obj/machinery/fax/admin/camarilla, /obj/structure/table/wood, @@ -127,6 +141,12 @@ }, /turf/open/floor/plating/rough, /area/vtm/interior/techshop) +"cz" = ( +/obj/structure/table, +/obj/lombard/blackmarket, +/obj/structure/platform/lowwall/rich/old, +/turf/open/floor/plating/rough, +/area/vtm/interior/cog) "cQ" = ( /obj/structure/table, /obj/item/storage/medkit/darkpack/ifak, @@ -149,26 +169,16 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) -"dc" = ( -/obj/structure/railing, -/obj/structure/lattice/grate, -/turf/open/openspace, -/area/vtm/outside/pacificheights) -"di" = ( -/obj/structure/lattice/grate, -/turf/open/openspace, -/area/vtm/outside/pacificheights) "dA" = ( /obj/structure/table, /obj/item/storage/medkit/darkpack/doctor, /obj/machinery/light/directional/north, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) -"dL" = ( -/obj/structure/table/wood, -/obj/machinery/light/directional/south, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) +"dE" = ( +/obj/structure/railing, +/turf/open/floor/plating/canal, +/area/vtm/interior/techshop) "dN" = ( /obj/item/clothing/head/cone, /turf/open/misc/dirt, @@ -178,10 +188,6 @@ /obj/machinery/radio_tranceiver/anarch, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"dU" = ( -/obj/structure/railing/corner, -/turf/open/floor/plating/canal, -/area/vtm/outside/pacificheights) "ef" = ( /obj/structure/roofstuff/vent_end{ dir = 4 @@ -201,6 +207,12 @@ /obj/item/vamp/keys/camarilla, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) +"ew" = ( +/obj/structure/vampfence/corner/rich{ + dir = 8 + }, +/turf/open/misc/dirt, +/area/vtm/graveyard) "eG" = ( /obj/machinery/light/directional/north, /turf/open/floor/plating/rough, @@ -274,10 +286,6 @@ }, /turf/open/floor/plating/sidewalk/rich, /area/vtm/outside/pacificheights) -"gl" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "gs" = ( /obj/effect/turf_decal/bordur{ dir = 1 @@ -326,6 +334,10 @@ /obj/machinery/sprinkler/area_managed, /turf/open/floor/plating/rough, /area/vtm/interior/techshop) +"hp" = ( +/obj/structure/tzijelly, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "hu" = ( /obj/effect/decal/pallet, /obj/item/ammo_box/darkpack/arrows, @@ -340,6 +352,9 @@ /obj/structure/table/countertop/beige, /turf/open/floor/plating/rough, /area/vtm/interior/shop) +"hF" = ( +/turf/open/openspace, +/area/vtm/interior/techshop) "hH" = ( /obj/effect/decal/cleanable/trash{ icon_state = "trash5" @@ -389,9 +404,6 @@ "iD" = ( /turf/closed/wall/vampwall/painted, /area/vtm/interior/shop) -"iH" = ( -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "iJ" = ( /obj/effect/spawner/random/occult/artifact, /turf/open/floor/wood/smooth/old, @@ -419,10 +431,6 @@ }, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) -"iZ" = ( -/obj/machinery/hydroponics/simple/plastic, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "jd" = ( /obj/structure/table/wood, /obj/machinery/radio_tranceiver/police, @@ -524,6 +532,16 @@ /obj/item/molotov, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"kx" = ( +/obj/structure/vampfence/corner/rich{ + dir = 4 + }, +/turf/open/misc/dirt, +/area/vtm/graveyard) +"kO" = ( +/obj/structure/toiletbong, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "kS" = ( /turf/closed/wall/vampwall/market, /area/vtm/interior/shop) @@ -533,6 +551,12 @@ }, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) +"lf" = ( +/obj/structure/chair/greyscale{ + dir = 8 + }, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "lk" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -550,6 +574,9 @@ }, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"lx" = ( +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "lM" = ( /obj/effect/decal/cleanable/trash, /obj/effect/spawner/random/occult/artifact, @@ -572,6 +599,15 @@ }, /turf/open/misc/dirt, /area/vtm/outside/supply) +"mb" = ( +/obj/effect/landmark/teleport_mark/alltribes, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) +"mf" = ( +/obj/structure/table/wood, +/obj/item/scythe/vamp, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "mm" = ( /obj/structure/table/reinforced, /obj/machinery/computer/order_console/mining/restricted/police, @@ -582,10 +618,23 @@ /obj/structure/barrels/rand, /turf/open/misc/dirt, /area/vtm/outside/supply) +"mr" = ( +/turf/open/floor/plating/canal, +/area/vtm/interior/techshop) "mw" = ( /obj/structure/ladder/manhole/up, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"mT" = ( +/obj/structure/table/wood, +/obj/item/seeds/cannabis, +/obj/item/seeds/cannabis, +/obj/item/seeds/cannabis, +/obj/item/seeds/cannabis, +/obj/item/seeds/cannabis, +/obj/item/seeds/cannabis, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "mW" = ( /obj/item/storage/box/syringes, /obj/item/storage/box/masks, @@ -651,12 +700,6 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) -"nw" = ( -/obj/structure/table, -/obj/lombard, -/obj/structure/platform/lowwall/rich/old, -/turf/open/floor/plating/rough, -/area/vtm/outside/pacificheights) "nA" = ( /obj/effect/decal/cleanable/garbage, /obj/effect/decal/cleanable/cardboard, @@ -674,6 +717,14 @@ /obj/structure/sign/city/store/bacotell/directional/north, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) +"nZ" = ( +/obj/structure/vampfence/corner/rich, +/turf/open/misc/dirt, +/area/vtm/graveyard) +"od" = ( +/obj/structure/sign/flag/pride/trans/directional/north, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "of" = ( /obj/item/storage/medkit/darkpack/standard, /obj/item/storage/medkit/darkpack/standard, @@ -681,10 +732,6 @@ /obj/structure/table, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) -"oi" = ( -/obj/structure/closet/crate/wooden/communitygardens/seeds, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "ok" = ( /obj/effect/decal/cleanable/litter, /turf/open/misc/beach/vamp, @@ -734,6 +781,10 @@ /obj/structure/platform/lowwall/brick_old, /turf/open/floor/plating/rough, /area/vtm/outside/pacificheights) +"pv" = ( +/obj/machinery/light/directional/west, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "pz" = ( /obj/darkpack_car/rand{ locked = 0; @@ -741,11 +792,6 @@ }, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) -"pD" = ( -/obj/machinery/light/directional/north, -/obj/structure/lattice/grate, -/turf/open/openspace, -/area/vtm/outside/pacificheights) "pG" = ( /obj/effect/landmark/npcbeacon, /obj/effect/turf_decal/bordur/corner{ @@ -753,6 +799,10 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"pI" = ( +/obj/structure/lattice/grate, +/turf/open/openspace, +/area/vtm/interior/techshop) "pN" = ( /turf/open/floor/plating/rough, /area/vtm/interior/clinic) @@ -847,6 +897,12 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"rI" = ( +/obj/effect/vip_barrier/endron/endron_lab{ + dir = 8 + }, +/turf/open/floor/plating/rough, +/area/vtm/interior/techshop) "rK" = ( /obj/item/storage/pill_bottle/ephedrine, /obj/item/storage/pill_bottle/ephedrine, @@ -877,16 +933,6 @@ /obj/structure/stairs/north, /turf/open/floor/plating/rough, /area/vtm/interior/techshop) -"rO" = ( -/obj/structure/table/wood, -/obj/item/seeds/cannabis, -/obj/item/seeds/cannabis, -/obj/item/seeds/cannabis, -/obj/item/seeds/cannabis, -/obj/item/seeds/cannabis, -/obj/item/seeds/cannabis, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "rW" = ( /obj/effect/turf_decal/crosswalk, /turf/open/floor/plating/asphalt, @@ -937,10 +983,6 @@ }, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) -"sQ" = ( -/obj/machinery/hydroponics/simple/wooden, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "sR" = ( /obj/effect/decal/pallet, /turf/open/misc/dirt, @@ -968,10 +1010,6 @@ /obj/structure/table/countertop/black, /turf/open/floor/plating/rough, /area/vtm/interior/shop) -"sZ" = ( -/obj/structure/sink/directional/east, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "tb" = ( /obj/effect/landmark/npcbeacon, /obj/effect/turf_decal/bordur, @@ -984,10 +1022,10 @@ /obj/structure/lamppost/four, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) -"tr" = ( -/obj/structure/sign/flag/pride/lesbian/directional/north, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) +"to" = ( +/obj/structure/vampgate, +/turf/open/misc/dirt, +/area/vtm/graveyard) "tu" = ( /obj/darkpack_car/track/volkswagen{ dir = 4; @@ -1005,6 +1043,15 @@ }, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) +"tJ" = ( +/obj/machinery/smartfridge/drying/rack, +/obj/machinery/light/directional/north, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) +"tL" = ( +/obj/structure/vampfence/rich, +/turf/open/misc/grass, +/area/vtm/graveyard) "tO" = ( /obj/effect/gibspawner/human, /obj/structure/sign/city/store/gummaguts/directional/north, @@ -1018,6 +1065,9 @@ }, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"tR" = ( +/turf/closed/wall/vampwall/rich/old, +/area/vtm/interior/cog) "tW" = ( /obj/darkpack_car/police/ranger, /turf/open/floor/plating/asphalt, @@ -1032,6 +1082,10 @@ }, /turf/open/floor/iron/smooth_large, /area/vtm/outside/pacificheights) +"uh" = ( +/obj/structure/vampfence/corner/rich, +/turf/open/misc/grass, +/area/vtm/graveyard) "ut" = ( /obj/structure/chair/comfy/darkpack/green{ dir = 1 @@ -1074,10 +1128,6 @@ }, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) -"uP" = ( -/obj/structure/sign/flag/pride/trans/directional/north, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "uX" = ( /obj/effect/landmark/navigate_destination, /turf/open/floor/plating/rough, @@ -1087,6 +1137,16 @@ /obj/item/vtm_artifact/fae_charm, /turf/open/floor/wood/smooth/old, /area/vtm/interior/sewer) +"vf" = ( +/turf/open/floor/wood/rough, +/area/vtm/interior/techshop) +"vi" = ( +/obj/structure/vampdoor/wood{ + dir = 4 + }, +/obj/effect/mapping_helpers/door/access/graveyard, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "vl" = ( /obj/effect/turf_decal/bordur{ dir = 8 @@ -1105,22 +1165,19 @@ /mob/living/carbon/human/npc/shop, /turf/open/floor/city/toilet, /area/vtm/interior/shop) +"vG" = ( +/obj/machinery/smartfridge/drying/rack, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "vH" = ( /obj/structure/sign/city/police_department/directional/north{ icon_state = "police3" }, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"vW" = ( -/obj/structure/table/wood, -/obj/item/soil_sack, -/obj/item/soil_sack/coir, -/obj/item/soil_sack/gel, -/obj/item/soil_sack/rich, -/obj/item/soil_sack/vermaculite, -/obj/item/soil_sack/worm, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "wb" = ( /obj/structure/table, /obj/item/storage/medkit/darkpack/brute, @@ -1128,6 +1185,24 @@ /obj/item/storage/medkit/darkpack/brute, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) +"wh" = ( +/obj/structure/table/wood, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/food/grown/cannabis, +/obj/item/bong{ + pixel_y = 10; + pixel_x = 10 + }, +/obj/item/bong{ + pixel_y = 2; + pixel_x = 10 + }, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "wj" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -1166,6 +1241,10 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"xt" = ( +/obj/machinery/hydroponics/soil, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "xC" = ( /obj/effect/decal/cleanable/litter, /turf/open/floor/plating/concrete, @@ -1189,6 +1268,16 @@ /obj/item/gun/ballistic/automatic/darkpack/thompson, /turf/open/floor/plating/rough, /area/vtm/interior/anarch) +"xY" = ( +/obj/structure/table/wood, +/obj/item/soil_sack, +/obj/item/soil_sack/coir, +/obj/item/soil_sack/gel, +/obj/item/soil_sack/rich, +/obj/item/soil_sack/vermaculite, +/obj/item/soil_sack/worm, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "xZ" = ( /obj/machinery/light/floor, /turf/open/floor/plating/rough, @@ -1200,6 +1289,10 @@ "yk" = ( /turf/closed/wall/vampwall/brick, /area/vtm/interior/supply) +"yo" = ( +/obj/structure/table/wood, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "yq" = ( /obj/structure/barrels/rand, /obj/structure/sign/city/chinese/alt2/directional/west, @@ -1222,10 +1315,6 @@ /obj/item/storage/medkit/darkpack/burn, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) -"yK" = ( -/obj/structure/vampdoor, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "yW" = ( /obj/machinery/button/elevator/directional/north{ id = "DebugElevator4"; @@ -1239,6 +1328,15 @@ }, /turf/open/misc/grass, /area/vtm/outside/pacificheights) +"yX" = ( +/obj/structure/railing, +/obj/structure/ladder/manhole/up{ + connect_down = 1; + connect_up = 0 + }, +/obj/structure/lattice/grate, +/turf/open/openspace, +/area/vtm/interior/techshop) "zd" = ( /turf/open/floor/wood/smooth/old, /area/vtm/interior/sewer) @@ -1269,6 +1367,9 @@ /obj/effect/spawner/random/stray_animal, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"zD" = ( +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "zF" = ( /obj/structure/table/wood, /obj/item/vtm_artifact/weekapaug_thistle, @@ -1299,6 +1400,13 @@ }, /turf/open/floor/plating/asphalt, /area/vtm/outside/pacificheights) +"Ay" = ( +/obj/structure/vampdoor/wood{ + dir = 1 + }, +/obj/effect/mapping_helpers/door/access/graveyard, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "AE" = ( /obj/structure/bookcase/random/adult, /turf/open/floor/plating/concrete, @@ -1312,6 +1420,12 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"Bb" = ( +/obj/structure/vampfence/rich{ + dir = 4 + }, +/turf/open/misc/grass, +/area/vtm/graveyard) "Be" = ( /obj/structure/vampdoor/simple, /obj/effect/mapping_helpers/door/access/supply, @@ -1357,6 +1471,18 @@ }, /turf/open/floor/plating/elevatorshaft, /area/vtm/outside/pacificheights) +"BH" = ( +/obj/structure/table/wood, +/obj/item/ammo_box/darkpack/c12g{ + pixel_x = 6; + pixel_y = 8 + }, +/obj/item/ammo_box/darkpack/c12g{ + pixel_x = -6; + pixel_y = 11 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "BL" = ( /mob/living/carbon/human/npc/shop, /turf/open/floor/plating/concrete, @@ -1370,10 +1496,6 @@ /obj/structure/platform/lowwall/market, /turf/open/floor/plating/rough, /area/vtm/interior/shop) -"BZ" = ( -/obj/structure/railing, -/turf/open/floor/plating/canal, -/area/vtm/outside/pacificheights) "Ca" = ( /obj/structure/table, /obj/item/gun/ballistic/automatic/darkpack/sniper, @@ -1384,15 +1506,6 @@ /obj/structure/table/countertop/red, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) -"Cj" = ( -/obj/structure/railing, -/obj/structure/ladder/manhole/up{ - connect_down = 1; - connect_up = 0 - }, -/obj/structure/lattice/grate, -/turf/open/openspace, -/area/vtm/outside/pacificheights) "Ct" = ( /obj/structure/lamppost/one{ dir = 1 @@ -1412,6 +1525,10 @@ /obj/structure/extinguisher_cabinet/directional/north, /turf/open/floor/plating/rough, /area/vtm/interior/techshop) +"CO" = ( +/obj/structure/platform/lowwall/brick/window/reinforced, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Df" = ( /obj/structure/closet/crate/freezer/blood{ name = "vampire blood freezer" @@ -1432,6 +1549,9 @@ }, /turf/open/floor/plating/sidewalk/poor, /area/vtm/outside/pacificheights) +"Dj" = ( +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Dp" = ( /obj/structure/table, /obj/item/storage/medkit/darkpack/oxy, @@ -1482,6 +1602,11 @@ }, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) +"DS" = ( +/obj/machinery/light/dim/directional/east, +/obj/effect/spawner/random/decoration/flower, +/turf/open/misc/dirt, +/area/vtm/graveyard) "DT" = ( /obj/machinery/fax/endron, /obj/structure/table/wood, @@ -1502,10 +1627,27 @@ "Ef" = ( /turf/closed/wall/vampwall/rich/old, /area/vtm/interior/sewer) +"Eg" = ( +/obj/structure/table/wood, +/obj/item/storage/fancy/rollingpapers{ + pixel_x = -6 + }, +/obj/item/storage/box/matches{ + pixel_x = 6; + pixel_y = 10 + }, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "Em" = ( /obj/structure/cargo_put, /turf/open/misc/dirt, /area/vtm/outside/supply) +"Eq" = ( +/obj/structure/vampfence/rich{ + dir = 4 + }, +/turf/open/misc/dirt, +/area/vtm/graveyard) "Et" = ( /obj/structure/table/optable, /obj/machinery/defibrillator_mount/loaded{ @@ -1529,14 +1671,14 @@ /obj/machinery/light/directional/west, /turf/open/misc/dirt, /area/vtm/outside/supply) +"EI" = ( +/obj/structure/vampfence/rich, +/turf/open/misc/dirt, +/area/vtm/graveyard) "EL" = ( /obj/structure/platform/lowwall/rich/old/window, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) -"EN" = ( -/obj/effect/landmark/error, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "EY" = ( /turf/closed/wall/vampwall/rich, /area/vtm/outside/pacificheights) @@ -1610,10 +1752,6 @@ /obj/structure/platform/lowwall/brick/window, /turf/open/floor/plating/rough, /area/vtm/interior/shop) -"Gw" = ( -/obj/machinery/light/directional/west, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "GF" = ( /turf/cordon, /area/misc/cordon) @@ -1675,12 +1813,6 @@ /obj/effect/landmark/npcwall, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) -"Hg" = ( -/obj/item/paper{ - default_raw_text = "Error room!!" - }, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "Hh" = ( /obj/darkpack_car/rand{ locked = 0 @@ -1713,6 +1845,14 @@ }, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"Hv" = ( +/obj/structure/table/wood, +/obj/effect/spawner/random/bureaucracy/paper{ + pixel_x = 7; + pixel_y = -2 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Hy" = ( /obj/machinery/atm{ dir = 8; @@ -1768,16 +1908,33 @@ "ID" = ( /turf/closed/wall/vampwall/market, /area/vtm/outside/pacificheights) +"IE" = ( +/obj/structure/sign/flag/pride/lesbian/directional/north, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "IL" = ( /obj/structure/ladder/manhole/up, /turf/open/floor/plating/rough, /area/vtm/interior/techshop) +"IQ" = ( +/obj/machinery/light/dim/directional/east, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) +"IU" = ( +/obj/machinery/light/directional/north, +/turf/open/floor/plating/canal, +/area/vtm/interior/techshop) "IZ" = ( /obj/machinery/fax/aasimites, /obj/structure/table/wood, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"Jf" = ( +"Je" = ( +/obj/machinery/light/directional/north, +/obj/structure/werewolf_totem/children_of_gaia, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) +"Jf" = ( /obj/machinery/door/airlock/elevator/left{ dir = 8; pixel_y = 15; @@ -1807,10 +1964,6 @@ /obj/item/reagent_containers/blood, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) -"Jq" = ( -/obj/structure/sign/flag/pride/directional/north, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) "Ju" = ( /obj/effect/gibspawner/human, /obj/machinery/light/directional/west, @@ -1844,10 +1997,9 @@ /obj/effect/spawner/random/trash/rat, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"JZ" = ( -/obj/item/clothing/head/collectable/pirate, -/turf/open/floor/wood/rough, -/area/vtm/outside/pacificheights) +"JX" = ( +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/techshop) "Kc" = ( /obj/effect/turf_decal/bordur, /obj/effect/landmark/npc_spawn_point, @@ -1863,12 +2015,25 @@ "Kk" = ( /turf/closed/wall/vampwall/brick_old, /area/vtm/outside/ghetto) +"Kn" = ( +/obj/structure/table, +/obj/lombard, +/obj/structure/platform/lowwall/rich/old, +/turf/open/floor/plating/rough, +/area/vtm/interior/cog) "Ko" = ( /obj/effect/landmark/npcbeacon/directed{ dir = 8 }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"Kq" = ( +/obj/item/flashlight/lantern/on{ + pixel_x = -8; + pixel_y = 8 + }, +/turf/open/misc/dirt, +/area/vtm/graveyard) "Kr" = ( /obj/structure/table, /obj/item/stack/dollar/thousand, @@ -1957,6 +2122,12 @@ /obj/item/vtm_artifact/daimonori, /turf/open/floor/wood/smooth/old, /area/vtm/interior/sewer) +"LN" = ( +/obj/structure/toilet{ + dir = 8 + }, +/turf/open/floor/plating/rough, +/area/vtm/interior/techshop) "LP" = ( /obj/structure/vampdoor/simple, /obj/effect/mapping_helpers/door/access/police, @@ -2020,17 +2191,10 @@ /obj/structure/platform/lowwall/brick, /turf/open/floor/plating/rough, /area/vtm/interior/shop) -"Mz" = ( -/obj/structure/table/wood, -/obj/item/storage/fancy/rollingpapers{ - pixel_x = -6 - }, -/obj/item/storage/box/matches{ - pixel_x = 6; - pixel_y = 10 - }, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) +"MD" = ( +/obj/machinery/light/directional/east, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "MF" = ( /obj/structure/table/wood, /obj/machinery/radio_tranceiver/camarilla, @@ -2091,13 +2255,6 @@ /obj/structure/table/countertop/bacotell, /turf/open/floor/plating/concrete, /area/vtm/interior/shop) -"NK" = ( -/obj/machinery/smartfridge/drying/rack, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "NO" = ( /obj/structure/table/wood/fancy/red, /obj/item/storage/bag/books, @@ -2116,6 +2273,10 @@ "NR" = ( /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"NW" = ( +/obj/structure/sign/flag/pride/directional/north, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "NX" = ( /obj/effect/decal/pallet, /obj/machinery/light/directional/west, @@ -2148,6 +2309,10 @@ /obj/structure/extinguisher_cabinet/directional/east, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"Oz" = ( +/obj/structure/sink/directional/east, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "OB" = ( /obj/darkpack_car/police{ locked = 0 @@ -2188,10 +2353,6 @@ /obj/structure/vampdoor, /turf/open/floor/wood/smooth/old, /area/vtm/interior/sewer) -"OQ" = ( -/obj/machinery/light/directional/north, -/turf/open/floor/plating/canal, -/area/vtm/outside/pacificheights) "OU" = ( /obj/effect/spawner/random/trash/grime{ spawn_loot_count = 20; @@ -2199,24 +2360,6 @@ }, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"OV" = ( -/obj/structure/table/wood, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/food/grown/cannabis, -/obj/item/bong{ - pixel_y = 10; - pixel_x = 10 - }, -/obj/item/bong{ - pixel_y = 2; - pixel_x = 10 - }, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "OZ" = ( /obj/structure/table, /obj/item/ammo_box/magazine/darkpackaug, @@ -2243,10 +2386,10 @@ /obj/machinery/computer/operating, /turf/open/floor/plating/rough, /area/vtm/interior/clinic) -"Py" = ( -/obj/machinery/light/directional/east, -/turf/open/floor/plating/rough/cave, -/area/vtm/outside/pacificheights) +"Pw" = ( +/obj/structure/closet/crate/wooden/communitygardens/tools, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "Pz" = ( /obj/structure/transport/linear/public, /obj/machinery/elevator_control_panel/directional/north{ @@ -2258,10 +2401,20 @@ }, /turf/open/floor/plating/elevatorshaft, /area/vtm/outside/pacificheights) +"PA" = ( +/mob/living/carbon/human/npc/bouncer/endron/endron_lab, +/turf/open/floor/plating/rough, +/area/vtm/interior/clinic) "PC" = ( /obj/structure/roofstuff/vent, /turf/open/misc/grass, /area/vtm/outside/pacificheights) +"PK" = ( +/obj/structure/vampdoor{ + dir = 4 + }, +/turf/open/floor/plating/canal, +/area/vtm/interior/techshop) "PL" = ( /obj/effect/landmark/latejoin, /turf/open/misc/beach/vamp, @@ -2269,12 +2422,6 @@ "PO" = ( /turf/open/floor/plating/rough, /area/vtm/interior/anarch) -"PP" = ( -/obj/structure/table, -/obj/lombard/blackmarket, -/obj/structure/platform/lowwall/rich/old, -/turf/open/floor/plating/rough, -/area/vtm/outside/pacificheights) "PR" = ( /obj/structure/vampdoor{ dir = 4 @@ -2358,6 +2505,10 @@ }, /turf/open/misc/dirt/rails, /area/vtm/outside/supply) +"Rf" = ( +/obj/item/clothing/head/collectable/pirate, +/turf/open/floor/wood/rough, +/area/vtm/interior/techshop) "Rg" = ( /obj/structure/table/wood, /obj/structure/retail/flower_shop, @@ -2382,6 +2533,14 @@ /obj/structure/platform/lowwall/market, /turf/open/floor/plating/rough, /area/vtm/interior/shop) +"Rs" = ( +/turf/closed/wall/vampwall/brick, +/area/vtm/graveyard/interior) +"Rv" = ( +/obj/structure/table/wood, +/obj/machinery/light/directional/south, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "Rx" = ( /obj/effect/landmark/npcbeacon, /obj/effect/turf_decal/bordur/corner{ @@ -2430,15 +2589,6 @@ "SK" = ( /turf/closed/wall/vampwall/rich/old, /area/vtm/interior/clinic) -"SL" = ( -/turf/open/floor/wood/rough, -/area/vtm/outside/pacificheights) -"SM" = ( -/obj/structure/vampdoor{ - dir = 4 - }, -/turf/open/floor/plating/canal, -/area/vtm/outside/pacificheights) "SP" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -2497,11 +2647,10 @@ /obj/item/gun/ballistic/automatic/pistol/darkpack/deagle, /turf/open/floor/plating/rough, /area/vtm/interior/anarch) -"TU" = ( -/obj/machinery/smartfridge/drying/rack, -/obj/machinery/light/directional/north, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) +"TT" = ( +/obj/structure/railing/corner, +/turf/open/floor/plating/canal, +/area/vtm/interior/techshop) "TY" = ( /obj/structure/rack/food{ dir = 4; @@ -2514,6 +2663,11 @@ /obj/structure/table/wood, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) +"Us" = ( +/obj/machinery/light/directional/north, +/obj/structure/lattice/grate, +/turf/open/openspace, +/area/vtm/interior/techshop) "Ut" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -2552,10 +2706,6 @@ /obj/structure/transport/linear/public, /turf/open/floor/plating/elevatorshaft, /area/vtm/outside/pacificheights) -"UN" = ( -/obj/structure/closet/crate/wooden/communitygardens/tools, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) "Va" = ( /obj/machinery/computer/order_console/mining/restricted/hospital, /obj/structure/table/reinforced, @@ -2642,6 +2792,20 @@ }, /turf/open/floor/plating/sidewalk/rich, /area/vtm/outside/pacificheights) +"Wh" = ( +/obj/vampgrave{ + spawn_interval = 300; + max_zombies_per_grave = 1; + name = "upturned grave" + }, +/turf/open/misc/dirt, +/area/vtm/graveyard) +"Wk" = ( +/obj/structure/chair/wood/darkpack/red{ + dir = 1 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Wl" = ( /obj/structure/table, /obj/item/ammo_box/magazine/darkpack556, @@ -2674,6 +2838,10 @@ /obj/structure/table/reinforced, /turf/open/floor/wood/smooth/old, /area/vtm/interior/sewer) +"WH" = ( +/obj/structure/vampdoor, +/turf/open/floor/plating/rough/cave, +/area/vtm/interior/techshop) "WJ" = ( /obj/structure/transport/linear/public, /turf/open/floor/plating/elevatorshaft, @@ -2770,20 +2938,31 @@ /mob/living/carbon/human/npc/incel, /turf/open/misc/beach/vamp, /area/vtm/outside/pacificheights) -"Yh" = ( +"Yb" = ( /obj/structure/table/wood, -/obj/machinery/light/directional/north, -/turf/open/floor/plating/sidewalk, -/area/vtm/outside/pacificheights) +/obj/item/storage/toolbox/mechanical{ + pixel_x = -4; + pixel_y = 14 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Ys" = ( /turf/open/openspace, /area/vtm/outside/pacificheights) +"Yw" = ( +/obj/structure/railing, +/obj/structure/lattice/grate, +/turf/open/openspace, +/area/vtm/interior/techshop) "Yz" = ( /obj/structure/chair/office{ dir = 8 }, /turf/open/floor/plating/sidewalk/poor, /area/vtm/outside/supply) +"YA" = ( +/turf/closed/wall/vampwall/brick_old, +/area/vtm/interior/techshop) "YB" = ( /obj/machinery/fax/admin/endron, /obj/structure/table/wood, @@ -2803,6 +2982,10 @@ /obj/structure/roadsign/speedlimit, /turf/open/floor/plating/sidewalk/poor, /area/vtm/outside/pacificheights) +"YM" = ( +/obj/machinery/hydroponics/simple/wooden, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "YY" = ( /obj/structure/table/reinforced, /obj/item/ritual_tome/arcane, @@ -2814,12 +2997,36 @@ }, /turf/open/floor/plating/sidewalk, /area/vtm/outside/pacificheights) +"Zk" = ( +/obj/structure/table/wood, +/obj/item/shovel/vamp{ + pixel_x = 2; + pixel_y = 1 + }, +/obj/item/shovel/vamp{ + pixel_x = -4; + pixel_y = 7 + }, +/obj/item/melee/vamp/tire{ + pixel_x = 1; + pixel_y = 14 + }, +/turf/open/floor/plating/rough, +/area/vtm/graveyard/interior) "Zs" = ( /turf/closed/wall/vampwall/brick_old, /area/vtm/outside/pacificheights) +"Zt" = ( +/obj/structure/closet/crate/wooden/communitygardens/seeds, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "Zz" = ( /turf/open/misc/grass/random/grass, /area/vtm/outside/pacificheights) +"ZH" = ( +/obj/machinery/hydroponics/simple/plastic, +/turf/open/floor/plating/sidewalk, +/area/vtm/interior/cog) "ZS" = ( /obj/structure/table, /obj/item/gun/ballistic/shotgun/toy/crossbow/vampire{ @@ -4209,7 +4416,7 @@ Tb pN pN pN -pN +PA Id sw Id @@ -4278,7 +4485,7 @@ SG pN pN SK -ZU +rI ZU rN Id @@ -4346,7 +4553,7 @@ Tb pN pN SK -Id +LN ZU rN Id @@ -6530,11 +6737,11 @@ oD oD oD NR -NR -NR -NR -NR -NR +nZ +EI +EI +EI +ew cj ik VQ @@ -6595,14 +6802,14 @@ Zz oD oD Zz -Zz -Zz -NR -NR -NR -NR -NR -NR +uh +tL +EI +kx +ag +ag +ag +Eq cj ik VQ @@ -6663,14 +6870,14 @@ Oi Zz oD Oi -Oi -Oi -NR -NR -NR -NR -NR -NR +Bb +Wh +ag +ag +ag +ag +ag +ag cj FB VQ @@ -6731,14 +6938,14 @@ Oi oD oD Oi -Oi -Oi -Oi -NR -NR -NR -NR -NR +Eq +ag +ag +Wh +ag +ag +ag +to cj ik VQ @@ -6799,14 +7006,14 @@ Oi oD oD Oi -Oi -Oi -Oi -Oi -Oi -Oi -NR -NR +Eq +Wh +DS +ag +ag +ag +ag +ag Bq ik VQ @@ -6866,15 +7073,15 @@ oD Oi oD oD -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi +Rs +CO +CO +Rs +Kq +ag +ag +ag +Eq cj ik VQ @@ -6934,15 +7141,15 @@ oD Oi oD Zz -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi +Rs +Hv +mf +Rs +ag +ag +ag +ag +Eq Ig ik VQ @@ -7002,15 +7209,15 @@ oD Zs yW oD -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi +Rs +Zk +Dj +Rs +CO +vi +Rs +Rs +kx cj kc Cz @@ -7070,15 +7277,15 @@ oD Zs XD Jf -Zs -Oi -Oi -Oi -Oi -Oi -Oi -Oi -Oi +Rs +BH +Wk +Dj +Dj +Dj +Dj +Ay +ag cj ik VQ @@ -7138,14 +7345,14 @@ oD Zs Pz WJ -Zs -Oi -Oi -Oi -Oi -Oi -Oi -Ny +Rs +aa +Dj +IQ +Dj +Dj +Dj +Rs Ny Ny rB @@ -7206,14 +7413,14 @@ Ny Ny Ny Ny -Ny -Ny -Ny -Ny -Ny -Ny -Ny -Ny +Rs +Rs +Rs +Rs +bD +Yb +yo +Rs GF Ny rB @@ -7401,12 +7608,12 @@ GJ "} (2,1,2) = {" GJ -MS -MS -MS -MS -MS -MS +ZU +ZU +ZU +ZU +ZU +ZU MS MS MS @@ -7469,12 +7676,12 @@ GJ "} (3,1,2) = {" GJ -MS -tr -Gw -iH -iH -MS +ZU +IE +pv +zD +zD +ZU Mm Ys Ys @@ -7537,12 +7744,12 @@ GJ "} (4,1,2) = {" GJ -MS -uP -EN -Hg -iH -MS +ZU +od +hp +mb +zD +ZU tY Ys Ys @@ -7605,12 +7812,12 @@ GJ "} (5,1,2) = {" GJ -MS -Jq -Py -bh -iH -yK +ZU +NW +MD +lf +zD +WH tY Ys Ys @@ -7673,20 +7880,20 @@ GJ "} (6,1,2) = {" GJ -MS -MS -MS -MS -MS -MS -SM -MS -MS -MS -MS -MS -MS -MS +ZU +ZU +ZU +ZU +ZU +ZU +PK +ZU +ZU +ZU +ZU +ZU +ZU +ZU GN lX lX @@ -7741,20 +7948,20 @@ GJ "} (7,1,2) = {" GJ -MS -eQ -eQ -eQ -eQ -MS -tY -BZ -Ys -Ys -Ys -Ys -Ys -di +ZU +JX +JX +JX +JX +ZU +mr +dE +hF +hF +hF +hF +hF +pI GN lX lX @@ -7809,20 +8016,20 @@ GJ "} (8,1,2) = {" GJ -MS -eQ -eQ -eQ -eQ -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +ZU +JX +bA +JX +JX +ZU +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -7877,20 +8084,20 @@ GJ "} (9,1,2) = {" GJ -MS -eQ -eQ -eQ -eQ -MS -pD -dc -Ys -Ys -Ys -Ys -Ys -di +ZU +JX +JX +JX +JX +ZU +Us +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -7945,20 +8152,20 @@ GJ "} (10,1,2) = {" GJ -MS -eQ -eQ -eQ -eQ -eQ -di -dc -Ys -Ys -Ys -Ys -Ys -di +ZU +JX +JX +JX +JX +JX +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8013,20 +8220,20 @@ GJ "} (11,1,2) = {" GJ -MS -MS -MS -MS -MS -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +tR +tR +tR +tR +tR +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8081,20 +8288,20 @@ GJ "} (12,1,2) = {" GJ -MS -ax -sZ -sZ -vW -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +kO +Oz +Oz +xY +tR +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8149,20 +8356,20 @@ GJ "} (13,1,2) = {" GJ -MS -sQ -eQ -eQ -gl -MS -tY -BZ -Ys -Ys -Ys -Ys -Ys -di +tR +YM +lx +lx +xt +tR +mr +dE +hF +hF +hF +hF +hF +pI GN lX lX @@ -8217,20 +8424,20 @@ GJ "} (14,1,2) = {" GJ -MS -sQ -eQ -eQ -gl -MS -tY -dU -SL -JZ -Ys -Ys -Ys -di +tR +YM +lx +lx +xt +tR +mr +TT +vf +Rf +hF +hF +hF +pI LU lX lX @@ -8285,20 +8492,20 @@ GJ "} (15,1,2) = {" GJ -MS -Yh -eQ -eQ -dL -MS -OQ -BZ -Ys -Ys -Ys -Ys -Ys -di +tR +Je +lx +lx +Rv +tR +IU +dE +hF +hF +hF +hF +hF +pI LU lX lX @@ -8353,20 +8560,20 @@ GJ "} (16,1,2) = {" GJ -MS -sQ -eQ -eQ -iZ -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +YM +lx +lx +ZH +tR +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8421,20 +8628,20 @@ GJ "} (17,1,2) = {" GJ -MS -sQ -eQ -eQ -iZ -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +YM +lx +lx +ZH +tR +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8489,20 +8696,20 @@ GJ "} (18,1,2) = {" GJ -MS -rO -eQ -eQ -oi -MS -di -Cj -Ys -Ys -Ys -Ys -Ys -di +tR +mT +lx +lx +Zt +tR +pI +yX +hF +hF +hF +hF +hF +pI GN lX lX @@ -8557,20 +8764,20 @@ GJ "} (19,1,2) = {" GJ -MS -rO -eQ -eQ -oi -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +mT +lx +lx +Zt +tR +pI +Yw +hF +hF +hF +hF +hF +pI GN lX lX @@ -8625,20 +8832,20 @@ GJ "} (20,1,2) = {" GJ -MS -NK -eQ -eQ -UN -MS -di -dc -Ys -Ys -Ys -Ys -Ys -di +tR +vG +lx +lx +Pw +tR +pI +Yw +hF +hF +hF +hF +hF +pI LU lX lX @@ -8693,20 +8900,20 @@ GJ "} (21,1,2) = {" GJ -MS -TU -eQ -eQ -UN -MS -OQ -BZ -Ys -Ys -Ys -Ys -Ys -di +tR +tJ +lx +lx +Pw +tR +IU +dE +hF +hF +hF +hF +hF +pI LU lX lX @@ -8761,20 +8968,20 @@ GJ "} (22,1,2) = {" GJ -MS -OV -eQ -eQ -eQ -eQ -tY -tY -Ys -Ys -Ys -Ys -Ys -di +tR +wh +lx +lx +lx +lx +mr +mr +hF +hF +hF +hF +hF +pI GN lX lX @@ -8829,20 +9036,20 @@ GJ "} (23,1,2) = {" GJ -MS -Mz -eQ -eQ -eQ -eQ -tY -tY -Ys -Ys -Ys -Ys -Ys -di +tR +Eg +lx +lx +lx +lx +mr +mr +hF +hF +hF +hF +hF +pI GN lX lX @@ -8897,20 +9104,20 @@ GJ "} (24,1,2) = {" GJ -MS -nw -PP -MS -MS -MS -SM -SM -MS -MS -Zs -Zs -Zs -Zs +tR +Kn +cz +tR +tR +tR +PK +PK +ZU +ZU +YA +YA +YA +YA Kk Mg SJ diff --git a/_maps/map_files/Vampire/westfield_mall/modules/old_train_rail_3.dmm b/_maps/map_files/Vampire/westfield_mall/modules/old_train_rail_3.dmm index cd9f6d355f60..82c5d420f329 100644 --- a/_maps/map_files/Vampire/westfield_mall/modules/old_train_rail_3.dmm +++ b/_maps/map_files/Vampire/westfield_mall/modules/old_train_rail_3.dmm @@ -112,7 +112,9 @@ /turf/open/floor/plating/rough/cave, /area/vtm/interior/sewer) "N" = ( -/obj/structure/ladder/manhole/up, +/obj/structure/ladder/manhole/up{ + requires_friend = 0 + }, /turf/open/floor/plating/rough/cave, /area/vtm/interior/sewer) "O" = ( diff --git a/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm b/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm index 9943af2f7e82..54712988dedc 100644 --- a/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm +++ b/_maps/map_files/Vampire/westfield_mall/westfield_mall.dmm @@ -75,9 +75,8 @@ /turf/open/floor/plating/concrete, /area/vtm/westfield_mall/interior/anarch) "aan" = ( -/obj/item/bong, -/turf/open/floor/plating/rough/cave, -/area/vtm/westfield_mall/interior/cave) +/turf/open/water/hot_spring, +/area/vtm/westfield_mall/forest) "aao" = ( /obj/structure/punching_bag, /obj/effect/decal/rugs, @@ -2889,9 +2888,9 @@ /turf/open/floor/wood/old, /area/vtm/westfield_mall/interior/chantry) "ajK" = ( -/obj/structure/glowshroom/glowcap, -/turf/open/misc/grass, -/area/vtm/westfield_mall/forest) +/obj/item/flashlight/lantern/on, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "ajL" = ( /obj/structure/rack/food/rand, /turf/open/floor/city/plating, @@ -3765,7 +3764,7 @@ /turf/open/floor/carpet/darkpack/bluegold, /area/vtm/westfield_mall/interior/theatre) "amG" = ( -/obj/structure/glowshroom, +/obj/structure/glowshroom/single, /turf/open/floor/plating/rough/cave, /area/vtm/westfield_mall/interior/cave) "amH" = ( @@ -3884,8 +3883,9 @@ /turf/open/floor/city/plating, /area/vtm/westfield_mall/interior) "ana" = ( -/obj/item/seeds/cannabis, -/turf/open/floor/plating/rough, +/obj/structure/flora/rock/stalagmite, +/obj/effect/realistic_fog/dense/strong, +/turf/open/floor/plating/rough/cave, /area/vtm/westfield_mall/interior/cave) "anb" = ( /obj/effect/turf_decal/bordur{ @@ -3927,8 +3927,12 @@ /turf/open/floor/carpet/darkpack/bluegold, /area/vtm/westfield_mall/interior/theatre) "ani" = ( -/obj/structure/glowshroom/glowcap, -/turf/open/floor/plating/rough, +/obj/structure/platform/lowwall/wood, +/obj/item/flashlight/lantern/on{ + pixel_x = 9; + pixel_y = 2 + }, +/turf/open/misc/dirt, /area/vtm/westfield_mall/interior/cave) "anj" = ( /obj/item/kirbyplants/random/dead{ @@ -4726,7 +4730,7 @@ /turf/open/floor/city/plating, /area/vtm/westfield_mall/interior/anarch) "apL" = ( -/obj/machinery/hydroponics/simple/plastic, +/obj/machinery/hydroponics/simple/wooden, /turf/open/floor/plating/rough/cave, /area/vtm/westfield_mall/interior/cave) "apN" = ( @@ -6158,7 +6162,7 @@ /turf/open/floor/plating/concrete, /area/vtm/westfield_mall/interior/supply) "auj" = ( -/obj/structure/glowshroom, +/obj/structure/glowshroom/single, /turf/open/floor/plating/rough, /area/vtm/westfield_mall/interior/cave) "auk" = ( @@ -6872,9 +6876,11 @@ /turf/open/floor/wood/smooth, /area/vtm/interior/clinic) "awD" = ( -/obj/structure/glowshroom/shadowshroom, -/turf/open/misc/grass, -/area/vtm/westfield_mall/forest) +/obj/structure/chair/wood/darkpack{ + dir = 1 + }, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "awE" = ( /obj/structure/guncase, /obj/item/gun/ballistic/automatic/darkpack/huntrifle, @@ -8165,12 +8171,9 @@ /turf/open/floor/wood/smooth/old, /area/vtm/westfield_mall/interior/skatepark) "aBa" = ( -/obj/structure/flora/bush/sparsegrass/style_random, -/obj/effect/turf_decal/bordur{ - dir = 9 - }, -/turf/open/floor/plating/sidewalk/old, -/area/vtm/westfield_mall/forest) +/obj/structure/platform/lowwall/wood, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "aBb" = ( /obj/structure/vampipe{ pixel_y = 32; @@ -9960,7 +9963,7 @@ /turf/open/misc/dirt, /area/vtm/westfield_mall) "aGX" = ( -/obj/structure/glowshroom, +/obj/structure/glowshroom/single, /turf/open/misc/dirt, /area/vtm/westfield_mall/interior/cave) "aGY" = ( @@ -13913,6 +13916,10 @@ /obj/effect/decal/cleanable/blood/splatter/oil, /turf/open/floor/plating/stone, /area/vtm/westfield_mall/interior/endron_logging) +"aUa" = ( +/obj/structure/flora/rock/stalagmite, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "aUb" = ( /obj/effect/landmark/start, /turf/open/floor/city/plating, @@ -15721,7 +15728,7 @@ /turf/open/floor/wood/smooth/old, /area/vtm/interior/shop) "aZU" = ( -/obj/structure/glowshroom, +/obj/structure/glowshroom/single, /turf/open/misc/grass, /area/vtm/westfield_mall/forest) "aZV" = ( @@ -15771,6 +15778,39 @@ }, /turf/open/floor/iron/grimy, /area/vtm/westfield_mall/interior/art_store) +"bfc" = ( +/obj/structure/table/wood, +/obj/effect/decal/pallet, +/obj/item/flashlight/lantern/on{ + pixel_x = -9; + pixel_y = 10 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 9; + pixel_y = 10 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 8; + pixel_y = 11 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 8; + pixel_y = 12 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 5; + pixel_y = 10 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 8; + pixel_y = 11 + }, +/obj/item/chair/wood/darkpack{ + pixel_x = 9; + pixel_y = 13 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "bfr" = ( /obj/structure/desk_bell{ pixel_x = -7; @@ -15804,6 +15844,10 @@ /obj/effect/mapping_helpers/door/autoname, /turf/open/floor/carpet/darkpack/purplegold, /area/vtm/westfield_mall/interior/law_office) +"bxv" = ( +/obj/effect/spawner/random/flora/flowers, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "bzR" = ( /obj/machinery/sprinkler, /turf/open/floor/carpet/darkpack/purplegold, @@ -15985,6 +16029,10 @@ "cLU" = ( /turf/open/floor/plating/rough/cave, /area/vtm/interior/caves) +"cND" = ( +/obj/effect/spawner/random/flora/grass, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "cTO" = ( /obj/transfer_point_vamp/umbral/exit{ id = "umbra_forest" @@ -16099,6 +16147,9 @@ /obj/machinery/light/directional/east, /turf/open/floor/city/toilet, /area/vtm/westfield_mall/interior/bathroom) +"edJ" = ( +/turf/open/water/hot_spring, +/area/vtm/westfield_mall/interior/cave) "eel" = ( /obj/machinery/light/floor, /turf/open/floor/plating/concrete, @@ -16276,6 +16327,9 @@ }, /turf/open/floor/plating/concrete, /area/vtm/westfield_mall/interior/groundskeeper_garage) +"fgE" = ( +/turf/closed/indestructible/the_matrix, +/area/vtm/westfield_mall/interior/cave) "fgJ" = ( /obj/effect/turf_decal/bordur/inverse{ dir = 8 @@ -16351,6 +16405,10 @@ /obj/effect/decal/wallpaper/light, /turf/closed/wall/vampwall/painted, /area/vtm/westfield_mall/interior/games_store) +"fIO" = ( +/obj/effect/decal/pallet, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "fJW" = ( /obj/effect/turf_decal/bordur/inverse{ dir = 1 @@ -16376,6 +16434,17 @@ /obj/item/flashlight/seclite, /turf/open/floor/iron/dark, /area/vtm/westfield_mall/interior/art_store) +"fOL" = ( +/obj/item/bong{ + pixel_x = -9; + pixel_y = -1 + }, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) +"fRW" = ( +/obj/structure/closet/crate/wooden/communitygardens/tools, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "fSa" = ( /obj/structure/filingcabinet/chestdrawer, /obj/item/flashlight/lamp{ @@ -16437,6 +16506,10 @@ /obj/structure/retail/gun_store, /turf/open/floor/wood/smooth/old, /area/vtm/westfield_mall/interior) +"gra" = ( +/obj/effect/spawner/random/flora/rocks, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "gsP" = ( /obj/structure/closet/secure_closet/freezer/fridge, /obj/item/food/vampire/icecream/chocolate, @@ -16493,6 +16566,19 @@ }, /turf/open/floor/iron/grimy, /area/vtm/westfield_mall/interior/art_store) +"gBa" = ( +/obj/structure/stone_tile/slab, +/obj/item/food/meat/rawcutlet/bear, +/obj/item/food/meat/rawcutlet/bear{ + pixel_x = 6; + pixel_y = 5 + }, +/obj/item/food/meat/rawcutlet/bear{ + pixel_x = -7; + pixel_y = 6 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "gKJ" = ( /obj/structure/vampdoor/simple{ dir = 4 @@ -16565,6 +16651,10 @@ /obj/effect/decal/wallpaper/padded, /turf/closed/wall/vampwall/painted, /area/vtm/westfield_mall/interior/theatre) +"her" = ( +/obj/structure/bonfire/dense/prelit, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "heG" = ( /obj/transfer_point_vamp/umbral/exit{ id = "umbra_street" @@ -16606,6 +16696,14 @@ }, /turf/open/floor/plating/concrete, /area/vtm/westfield_mall/interior/groundskeeper_garage) +"hqp" = ( +/obj/effect/realistic_fog, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) +"hxC" = ( +/obj/effect/realistic_fog/dense/strong, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "hyM" = ( /obj/machinery/icecream_vat, /turf/open/floor/wood/smooth, @@ -16723,6 +16821,10 @@ }, /turf/open/floor/wood/smooth/old, /area/vtm/westfield_mall/interior/games_store) +"ieU" = ( +/obj/effect/realistic_fog/dense/strong, +/turf/open/water/beach/vamp, +/area/vtm/westfield_mall/interior/cave) "ifk" = ( /turf/open/floor/iron/grimy, /area/vtm/westfield_mall/interior/art_store) @@ -16787,6 +16889,12 @@ /obj/effect/decal/pallet, /turf/open/water/vamp_sewer, /area/vtm/interior/sewer) +"jdm" = ( +/obj/effect/decal/wallpaper/papers/random{ + pixel_y = 25 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/anarch) "jdP" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -16827,6 +16935,34 @@ }, /turf/open/floor/plating/sidewalk/poor, /area/vtm/interior/sewer) +"jpq" = ( +/obj/effect/decal/wallpaper/papers/random{ + pixel_x = 1; + pixel_y = 26 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/anarch) +"jrT" = ( +/obj/structure/table/wood, +/obj/item/pool_cue{ + pixel_x = 10; + pixel_y = 6 + }, +/obj/item/pool_cue{ + pixel_x = 3; + pixel_y = 5 + }, +/obj/item/pool_cue{ + pixel_x = 7; + pixel_y = 5 + }, +/obj/item/flashlight/lamp/green{ + pixel_x = -9; + pixel_y = 5 + }, +/obj/structure/sign/poster/city/ministry/directional/north, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/anarch) "jtP" = ( /obj/item/toy/crayon/spraycan{ pixel_x = 7; @@ -16842,6 +16978,16 @@ }, /turf/open/floor/iron/grimy, /area/vtm/westfield_mall/interior/art_store) +"jwu" = ( +/obj/machinery/hydroponics/soil, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/forest) +"jwH" = ( +/obj/structure/chair/wood/darkpack{ + dir = 8 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "jxp" = ( /obj/structure/closet/crate/bin, /obj/effect/spawner/random/trash/garbage, @@ -16879,6 +17025,10 @@ }, /turf/open/floor/iron/dark, /area/vtm/westfield_mall/interior/art_store) +"jNL" = ( +/obj/structure/table/wood, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "jQE" = ( /obj/structure/railing{ dir = 1 @@ -17077,6 +17227,10 @@ /obj/machinery/sprinkler, /turf/open/floor/wood/smooth, /area/vtm/westfield_mall/interior/candy_store) +"mgz" = ( +/obj/effect/realistic_fog, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "mjJ" = ( /obj/item/kirbyplants/darkpack/random, /obj/effect/turf_decal/siding/white, @@ -17173,6 +17327,18 @@ /obj/effect/mapping_helpers/mob_buckler, /turf/open/floor/carpet/darkpack/bluegold, /area/vtm/westfield_mall/interior/theatre) +"mMR" = ( +/obj/structure/table/wood, +/obj/item/seeds/cannabis{ + pixel_x = 7; + pixel_y = 3 + }, +/obj/item/seeds/cannabis{ + pixel_x = -6; + pixel_y = 4 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "mPi" = ( /obj/structure/safe, /obj/item/stack/dollar/hundred{ @@ -17276,6 +17442,17 @@ /obj/machinery/light/directional/east, /turf/open/floor/carpet/darkpack/blacksilver, /area/vtm/westfield_mall/interior/skatepark) +"nNk" = ( +/obj/item/flashlight/lantern/on, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) +"nNq" = ( +/obj/effect/spawner/random/flora/rocks{ + pixel_y = -2; + pixel_x = -1 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "nRx" = ( /obj/structure/vampdoor/simple, /obj/effect/mapping_helpers/door/access/cleaning, @@ -17308,6 +17485,11 @@ /obj/effect/decal/wallpaper/light, /turf/closed/wall/vampwall/painted, /area/vtm/westfield_mall/interior/law_office) +"ogy" = ( +/obj/effect/spawner/random/flora/grass, +/obj/effect/spawner/random/flora/flowers, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "okz" = ( /obj/structure/sign/city/skateshop/three/directional/north, /turf/open/floor/plating/sidewalk/poor, @@ -17330,7 +17512,9 @@ /turf/open/misc/dirt, /area/vtm/interior/caves) "oqv" = ( -/obj/structure/coclock, +/obj/structure/coclock{ + pixel_y = -2 + }, /obj/machinery/light_switch/directional/north{ pixel_x = -12 }, @@ -17376,10 +17560,19 @@ /obj/effect/mapping_helpers/door/autoname, /turf/open/floor/wood/herring, /area/vtm/westfield_mall/interior/hot_ishu) +"oFh" = ( +/obj/effect/decal/garou_glyph/caern, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "oGX" = ( /obj/effect/spawner/random/trash/box, /turf/open/floor/city/plating, /area/vtm/interior/shop) +"oMG" = ( +/obj/structure/stone_tile/slab, +/obj/effect/spawner/random/food_or_drink/plant_produce, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "oQx" = ( /obj/structure/table/countertop/beige, /obj/effect/turf_decal/siding/wood{ @@ -17433,6 +17626,10 @@ }, /turf/open/floor/plating/sidewalk/old, /area/vtm/westfield_mall/parking_lot) +"pfj" = ( +/obj/structure/werewolf_totem/generic/alltribes, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "pmd" = ( /obj/structure/table/countertop/beige, /obj/effect/turf_decal/siding/wood{ @@ -17528,10 +17725,18 @@ /obj/machinery/light/directional/north, /turf/open/floor/wood/smooth/old, /area/vtm/westfield_mall/interior/skatepark) +"qiO" = ( +/turf/closed/wall/vampwall/wood, +/area/vtm/westfield_mall/interior/cave) "qtc" = ( /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood/smooth, /area/vtm/westfield_mall/interior/art_store) +"qCk" = ( +/obj/structure/table/wood, +/obj/effect/decal/pallet, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "qNv" = ( /obj/effect/turf_decal/siding/wood/dark{ dir = 8 @@ -17567,6 +17772,13 @@ /obj/effect/mapping_helpers/door/autoname, /turf/open/floor/city/plating, /area/vtm/westfield_mall/interior/arcade) +"qYz" = ( +/obj/structure/coclock{ + pixel_y = -2 + }, +/obj/structure/table/countertop, +/turf/open/floor/carpet/darkpack/orangesilver, +/area/vtm/westfield_mall/interior/supply) "rfA" = ( /obj/effect/decal/carpet{ pixel_x = 10; @@ -17580,6 +17792,10 @@ }, /turf/open/floor/carpet/darkpack/purplegold, /area/vtm/westfield_mall/interior/theatre) +"rle" = ( +/obj/machinery/light_switch/directional/north, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/anarch) "rne" = ( /obj/effect/decal/rugs, /obj/effect/decal/rugs, @@ -17607,6 +17823,10 @@ /obj/machinery/light/dim/directional/east, /turf/open/floor/carpet/darkpack/purplegold, /area/vtm/westfield_mall/interior/law_office) +"rCh" = ( +/obj/effect/realistic_fog, +/turf/open/water/hot_spring, +/area/vtm/westfield_mall/forest) "rCr" = ( /obj/effect/decal/cleanable/trash, /obj/transfer_point_vamp/umbral/exit{ @@ -17637,6 +17857,11 @@ }, /turf/open/floor/plating/roofwalk, /area/vtm/westfield_mall) +"rIi" = ( +/obj/structure/table/wood, +/obj/item/flashlight/lantern/on, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "rJe" = ( /obj/transfer_point_vamp/umbral/exit{ id = "umbra_church" @@ -17661,6 +17886,10 @@ }, /turf/open/floor/wood/herring, /area/vtm/westfield_mall/interior/law_office) +"rZU" = ( +/obj/structure/closet/crate/wooden/communitygardens/seeds, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "seO" = ( /obj/machinery/sprinkler, /turf/open/floor/plating/concrete, @@ -17717,6 +17946,12 @@ /obj/structure/sign/city/skateshop/two/directional/north, /turf/open/floor/plating/sidewalk/poor, /area/vtm/westfield_mall/skatepark) +"sXa" = ( +/obj/structure/table/wood/billiard{ + start_with_cues = 0 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/anarch) "taO" = ( /obj/machinery/button{ id = "keepershutters" @@ -17845,6 +18080,10 @@ }, /turf/open/floor/iron/dark, /area/vtm/westfield_mall/interior/art_store) +"tSo" = ( +/obj/item/flashlight/flare/candle/infinite, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "tTU" = ( /obj/structure/chair/darkpack/blue{ dir = 1 @@ -17943,6 +18182,11 @@ /obj/effect/mapping_helpers/door/autoname, /turf/open/floor/wood/smooth, /area/vtm/interior/shop) +"uEx" = ( +/obj/effect/spawner/random/flora/flowers, +/obj/effect/spawner/random/flora/flowers, +/turf/open/misc/dirt, +/area/vtm/westfield_mall/interior/cave) "uGu" = ( /obj/structure/table/countertop/beige, /obj/effect/turf_decal/siding/wood, @@ -17984,6 +18228,20 @@ }, /turf/open/floor/plating/sidewalk/poor, /area/vtm/interior/sewer) +"uNn" = ( +/obj/structure/table/wood, +/obj/effect/decal/pallet, +/obj/item/veil_contract, +/obj/item/veil_contract{ + pixel_x = 5; + pixel_y = 5 + }, +/obj/item/veil_contract{ + pixel_x = 8; + pixel_y = 1 + }, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "uPd" = ( /obj/item/kirbyplants/random, /turf/open/floor/carpet/darkpack/blacksilver, @@ -18069,6 +18327,10 @@ /obj/structure/lattice/grate, /turf/open/water/vamp_sewer, /area/vtm/interior/sewer) +"vts" = ( +/obj/effect/spawner/random/flora/rocks, +/turf/open/floor/plating/rough/cave, +/area/vtm/westfield_mall/interior/cave) "vxJ" = ( /obj/structure/table/wood, /obj/item/paper_bin{ @@ -18319,6 +18581,33 @@ }, /turf/open/floor/plating/rough, /area/vtm/westfield_mall/interior/anarch) +"wZb" = ( +/obj/structure/table/wood, +/obj/effect/decal/pallet, +/obj/item/darkpack/spear{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/darkpack/spear{ + pixel_x = -5; + pixel_y = -6 + }, +/obj/item/darkpack/spear{ + pixel_x = -1; + pixel_y = -4 + }, +/obj/item/gun/ballistic/automatic/darkpack/huntrifle{ + pixel_y = 10 + }, +/obj/item/ammo_box/darkpack/c556{ + pixel_x = 8; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/darkpack556/hunt{ + pixel_x = 12 + }, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "xjy" = ( /obj/effect/landmark/start/darkpack/hospital/clinic_director, /turf/open/floor/wood/smooth, @@ -18378,6 +18667,10 @@ /obj/structure/table/modern, /turf/open/floor/carpet/green, /area/vtm/westfield_mall/interior/law_office) +"xRi" = ( +/obj/structure/flora/rock/stalagmite, +/turf/open/floor/plating/rough, +/area/vtm/westfield_mall/interior/cave) "xRE" = ( /obj/structure/sign/painting{ pixel_y = 32; @@ -19577,7 +19870,7 @@ asT azz aco aco -aco +aRF aco aco azz @@ -24067,7 +24360,7 @@ aPP adV azz aBJ -aco +aRF azz aeW aeW @@ -32747,7 +33040,7 @@ ajc aeH aeH aeH -aeH +aUG aUG atx aft @@ -32899,8 +33192,8 @@ ajc aeH aeH aeH -aeH aUG +rle aHF aft aft @@ -33051,9 +33344,9 @@ ajc aeH aeH aeH -aeH aUG -atx +jdm +sXa aft aft aHF @@ -33203,8 +33496,8 @@ ajc aeH aeH aeH -aeH aUG +jpq atx aft aft @@ -33355,8 +33648,8 @@ ajc aeH aeH aeH -aeH aUG +jrT atx aft aft @@ -33507,7 +33800,7 @@ ajc aeH aeH aeH -aeH +aUG aUG aME aft @@ -50192,7 +50485,7 @@ aQQ adg adg auY -atC +aSM atC atC atC @@ -50344,7 +50637,7 @@ aQQ adg adg auY -atC +qYz aAQ aAQ atC @@ -109851,13 +110144,13 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc avR avR avR @@ -109998,18 +110291,18 @@ avR avR avR avR -aFc -aFc -aFc -aFc -aFc -avR -avR avR avR avR avR avR +aFc +adj +adj +fgE +fgE +adj +aFc avR avR avR @@ -110146,17 +110439,6 @@ avR avR avR avR -aFc -aFc -aFc -aFc -aFc -arI -arI -arI -aFc -aFc -aFc avR avR avR @@ -110166,6 +110448,17 @@ avR avR avR avR +aFc +adj +ana +hxC +hxC +adj +aFc +avR +avR +avR +avR avR avR aFc @@ -110296,20 +110589,8 @@ avR avR avR avR -aFc -aFc -aFc -arI -arI -arI -arI -arI -aLD -arI -arI -arI -aFc -aFc +avR +avR avR avR avR @@ -110320,6 +110601,18 @@ avR avR avR aFc +adj +hxC +hxC +hxC +adj +aFc +aFc +avR +avR +avR +avR +aFc aFc arI arI @@ -110446,22 +110739,6 @@ avR avR avR avR -aFc -aFc -aFc -arI -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc avR avR avR @@ -110471,6 +110748,22 @@ avR avR avR avR +avR +avR +avR +avR +aFc +adj +amG +aWd +aWd +adj +adj +aFc +avR +avR +avR +avR aFc arI arI @@ -110597,24 +110890,6 @@ avR avR avR avR -aFc -aFc -arI -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc avR avR avR @@ -110623,13 +110898,31 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +aFc +adj +xRi +aTe +aWd +amG +adj +aFc +avR +avR +avR +avR aFc arI +ieU aXX +ieU aXX -aXX -aXX -aXX +ieU aXX aXX aWd @@ -110748,25 +111041,6 @@ avR avR avR avR -aFc -aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc avR avR avR @@ -110775,21 +111049,40 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +aFc +adj +adj +aTe +aWd +auj +adj +aFc +avR +avR +avR +avR aFc arI aXX +ieU aXX +ieU aXX -aXX -aXX -aXX +ieU aXX aXX aWd aWd aWd auj -ani +auj arI arI aFc @@ -110899,27 +111192,6 @@ avR avR avR avR -aFc -aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc avR avR avR @@ -110927,13 +111199,34 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +adj +aUa +aWd +auj +adj +aFc +avR +avR +avR +avR aFc arI +ieU aXX +ieU aXX -aXX -aXX -aXX +ieU adj adj aXX @@ -111050,43 +111343,43 @@ avR avR avR avR -aFc -aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc -aFc avR avR avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +adj +adj +aWd +aTe +adj +aFc +aFc +aFc +avR +avR aFc arI arI +ieU aXX +ieU aXX -aXX -aXX -aXX +ieU adj adj aXX @@ -111201,43 +111494,43 @@ avR avR avR avR -aFc -aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc -aFc avR avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +aFc +adj +adj +adj +aWd +aWd +adj +adj +adj +aFc +aFc +aFc aFc aFc arI aXX +ieU aXX -aXX -aXX +ieU aXX aXX adj @@ -111353,37 +111646,37 @@ avR avR avR avR -aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc +adj +adj +adj +gra +amG +aWd +aWd +amG +aUa +adj +adj +adj aFc aFc -avR aFc arI arI @@ -111504,36 +111797,36 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -arI -arI -arI -arI +adj +adj +aTe +nNk +aTe +aTe +aWd +aWd +aWd +aWd +amG +vts +adj aFc aFc aFc @@ -111656,37 +111949,37 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI +adj +adj +xRi +auj +aTe +aTe +aTe +aWd +mgz +aWd +aWd +aWd +aWd +adj +adj aFc aFc aFc @@ -111703,7 +111996,7 @@ aWd aWd adj adj -aTe +oFh aWd aTe aTe @@ -111807,39 +112100,39 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -aFc -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI +adj +aTe +aTe +aWd +gra +aTe +aTe +aWd +aWd +aWd +amG +aWd +aWd +aWd +adj +adj aFc aFc aFc @@ -111958,41 +112251,41 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -arI -arI +adj +gra +aTe +aWd +aTe +aTe +aTe +aTe +aTe +aTe +aWd +auj +aTe +aWd +aUa +adj +adj aFc aFc aFc @@ -112110,45 +112403,45 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -arI -arI -arI -arI -arI +adj +adj +aTe +aWd +aWd +aTe +auj +aTe +xRi +aTe +aTe +xRi +auj +aTe +mgz +atM +aGX +adj +adj +adj +adj +adj aFc aFc aFc @@ -112262,48 +112555,48 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -atM -atM +adj +vts +aWd +aWd +mgz +aTe +qiO +aTe +aTe +aTe +aTe +gra +qiO +aTe +aTe atM atM -arI -arI -arI -arI +aBa +ani +awD +aGX +adj +adj +adj +adj arI atM aTe @@ -112414,39 +112707,39 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN +aFc +adj +auj +aWd +aWd +aWd +aWd +xRi +aTe +aTe +aTe +aTe +aTe +aTe +aTe +aTe +atM atM atM atM @@ -112566,39 +112859,39 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -aLD -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN +adj +adj +aTe +aTe +aWd +aWd +aWd +aTe +aTe +edJ +aan +aan +jwu +uEx +cND +atM +atM atM aGX atM @@ -112718,39 +113011,39 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN +adj +aTe +tSo +oMG +tSo +aWd +aWd +aWd +edJ +aan +aan +aan +aan +jwu +uEx +cND +atM atM atM atM @@ -112870,39 +113163,39 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN +adj +auj +aTe +pfj +gBa +aWd +aWd +aWd +aan +aan +rCh +aan +aan +cND +atM +atM +atM atM atM atM @@ -112919,7 +113212,7 @@ aWd aWd amG adj -ani +auj aTe arI arI @@ -113022,40 +113315,40 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -arI -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN +adj +xRi +tSo +oMG +tSo +aWd +aWd +aWd +aan +aan +aan +aan +aan +cND +bxv +atM +atM +atM atM atM atM @@ -113166,50 +113459,50 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -aFc -aFc -aFc -aFc -aFc -aFc -aFc -aFc -arI -aBu -aBu -aRN -aRN -aRN -aRN -aOx -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -auM -auM +adj +adj +aTe +aTe +aTe +aTe +aTe +aWd +edJ +aan +aan +aan +aan +jwu +ogy +atM +atM +atM +her +aTe atM atM atM @@ -113317,57 +113610,57 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc -abS -abS -abS -abS -abS -abS -abS -abS -arI -aRN -aRN -aRN -aRN -azA -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -auM -auM -auM -auM -auM -auM -auM -auM -arI +adj +aTe +aTe +hqp +aTe +aTe +aTe +aTe +aTe +aan +aan +jwu +bxv +uEx +atM +aGX +aTe +aTe +aTe +aTe +aWd +aWd +aTe +aTe +adj arI arI arI @@ -113469,57 +113762,57 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -abS -abS -aHj -aDR -aQc -aQc -aQc -aDR -abS -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -aRN -auM -auM -auM -auM -auM -auM -auM -auM -auM -arI -arI +adj +aUa +aWd +aWd +aTe +auj +qiO +xRi +aTe +aTe +aTe +xRi +qiO +uEx +cND +atM +aTe +qCk +jNL +rIi +fIO +aWd +aTe +adj +adj aFc aFc aFc @@ -113620,57 +113913,57 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -aFc -abS -aHj -aHj -aHj -aHj -aHj -aHj -aHj -abS -aRN -aRN -akj -akj -akj -akj -akj -add -add -add -akj -aRN -aRN -aRN -aRN -aRN -aOx -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -auM -auM -auM -auM -auM -auM -auM -auM -auM -auM -arI +adj +adj +amG +aWd +aTe +aTe +gra +aTe +xRi +aTe +aTe +aTe +aTe +aTe +aTe +aTe +aTe +aTe +aTe +aWd +aWd +aWd +aWd +adj aFc aFc avR @@ -113771,58 +114064,58 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc -akj -akj -aDR -aHj -aTZ -aDR -aHj -aHj -aHj -abS -aRN -aRN -akj -aoa -aMm -awp -akj -akj -aRZ -aeu -acF -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -auM -auM -auM -auM -auM -auM -auM -auM -auM -auM -arI -arI +adj +aWd +aWd +aWd +aWd +aTe +aTe +auj +aTe +aTe +aTe +aTe +aTe +aTe +hqp +aTe +aTe +jwH +aWd +aWd +aWd +adj +adj aFc avR avR @@ -113923,77 +114216,77 @@ avR avR avR avR -aFc -akj -akj -aHj -aHj -aHj -aHj -aty -aHJ -aHJ -aHJ -abS -aRN -aRN -akj -aIJ -aSO -aSO -aSO -akj -aah -aeu -axp -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -auM -auM -auM -auM -auM -auM -auM -auM -auM -arI -arI -aFc -aFc avR avR -aFc -arI -aWd -aWd -aWd -aMa -axu -asJ -asJ -aMa -aGC -asJ -asJ -asJ -asJ -aDV +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +adj +aUa +aWd +aWd +aWd +aWd +aTe +hqp +aTe +aTe +aTe +aTe +aTe +aTe +aWd +aWd +bfc +wZb +uNn +fRW +rZU +adj +aFc +aFc +avR +avR +aFc +arI +aWd +aWd +aWd +aMa +axu +asJ +asJ +aMa +aGC +asJ +asJ +asJ +asJ +aDV aSx aSx aRN @@ -114075,57 +114368,57 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc -avk -aUd -aHj -aHj -aHj -aHj -aEx -aBs -aSO -axJ -akj -akj -akj -akj -aaH -aSO -aSO -aSO -akj -amp -aoQ -awb -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -aRN -arI -arI -arI -arI -arI -arI -arI -arI -arI -arI -aFc +adj +adj +aWd +aWd +aWd +aWd +aTe +aTe +aTe +aTe +aTe +aWd +amG +aWd +aWd +adj +adj +adj +adj +adj +adj +adj aFc avR avR @@ -114227,50 +114520,51 @@ avR avR avR avR -aFc -avk -aUd -aHj -aHj -aHj -aHj -aOC -aUL -aSO -aSO -aSO -aSO -aSO -afP -aSO -aSO -aSO -aSO -aPB -aNB -aeu -abI -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -aRN -aRN -aRN -arI -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc +adj +gra +aTe +amG +aTe +nNq +aTe +aTe +aTe +aWd +aWd +aWd +vts +adj +adj aFc aFc aFc @@ -114281,7 +114575,6 @@ aFc avR avR avR -avR aFc arI aWd @@ -114379,48 +114672,6 @@ avR avR avR avR -aFc -avk -aUd -aHj -aHj -aHj -aHj -aEx -aUL -aSO -aSO -akj -akj -akj -akj -alW -aKd -aKd -aKd -akj -amp -aeu -awb -aRN -aRN -aRN -aOx -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -arI -arI -arI -aFc -aFc avR avR avR @@ -114434,12 +114685,54 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +adj +adj +aTe +auj +aTe +aTe +aTe +aTe +aWd +aWd +amG +amG +adj +adj +aFc +aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc arI arI arI aWd -aWd +fOL aBB apL apL @@ -114531,47 +114824,6 @@ avR avR avR avR -aFc -avk -aHj -aDR -agl -agl -agl -apo -aAz -aAz -aAz -abS -aRN -aRN -avk -acP -ahA -ahA -aKd -akj -aaj -aah -acF -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc -aFc -aFc -aFc avR avR avR @@ -114586,15 +114838,56 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +adj +adj +adj +auj +aTe +gra +amG +aWd +ajK +adj +adj +adj +aFc +aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aFc aFc aZY aqW aWd -aan -ana -ana +aWd +aTe +aTe aMa ain asJ @@ -114683,43 +114976,6 @@ avR avR avR avR -aFc -akj -akj -aHj -aOv -aOv -agl -agl -aOv -aug -agl -abS -aRN -aRN -avk -aGf -aYT -aYT -aKd -akj -aih -aeu -acF -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc avR avR avR @@ -114740,6 +114996,43 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +aFc +adj +adj +adj +adj +adj +adj +adj +adj +aFc +aFc +aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc aZY aWd @@ -114765,7 +115058,7 @@ aSx aSx aSx aZU -ajK +aZU aZU aSx aSx @@ -114835,43 +115128,6 @@ avR avR avR avR -aFc -aFc -abS -abS -aOv -aTk -aOv -aOv -aOv -agl -agl -abS -aRN -aRN -avk -aVY -aVY -aVY -akj -akj -aYC -aeu -acF -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc avR avR avR @@ -114892,13 +115148,50 @@ avR avR avR avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR aFc arI aWd aWd aWd aTe -aTe +mMR aMa aru aXi @@ -114915,7 +115208,7 @@ aSx aSx aSE aSx -awD +aZU aKB aKB aKB @@ -114988,42 +115281,42 @@ avR avR avR avR -aFc -aFc -abS -abS -abS -agl -agl -agl -abS -abS -abS -aRN -aRN -akj -akj -akj -akj -akj -add -add -add -akj -aRN -aOx -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115072,7 +115365,7 @@ aKB aKB aKB aKB -awD +aZU aSx aRN aRN @@ -115141,40 +115434,40 @@ avR avR avR avR -aFc -aFc -aFc -abS -agl -agl -agl -abS -aFc -arI -aRN -aRN -aRN -aGY -ayd -aGY -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115218,7 +115511,7 @@ aSx aSx aSx aSx -ajK +aZU aKB aKB akH @@ -115295,38 +115588,38 @@ avR avR avR avR -aFc -abS -agl -agl -agl -abS -aFc -arI -aRN -aRN -aRN -aGY -aSL -aGY -aRN -aBh -aRN -aRN -aRN -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115447,38 +115740,38 @@ avR avR avR avR -aFc -abS -agl -agl -agl -abS -aFc -arI -arI -aRN -aRN -aGY -aSL -aGY -aRN -aRN -aBu -aBu -aBu -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115523,11 +115816,11 @@ aSx aSx aSx aBu -awD +aZU aKB aKB aZd -ajK +aZU aSx aSx aSx @@ -115599,37 +115892,37 @@ avR avR avR avR -aFc -abS -abS -abS -abS -abS -aFc -aFc -arI -aRN -aRN -aGY -aSL -aGY -aRN -aBu -aBu -aBu -aBu -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -arI -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115751,37 +116044,37 @@ avR avR avR avR -aFc -aFc -aFc -aFc -aFc -aFc -aFc -aFc -arI -aRN -aRN -aha -aSL -aSL -aRN -aBu -aBu -aBu -aBu -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -aLD -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -115910,29 +116203,29 @@ avR avR avR avR -aFc -arI -aRN -azA -aSL -aSL -aSL -aRN -aBu -aBu -aBu -aBu -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -arI -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116062,29 +116355,29 @@ avR avR avR avR -aFc -arI -aRN -aio -aSL -aRN -aSL -aRN -aRN -aio -aBu -aBu -aRN -aOx -aRN -aRN -aLD -aLD -aLD -aLD -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116214,28 +116507,28 @@ avR avR avR avR -aFc -arI -aRN -aSL -aSL -aRN -aha -aSL -azA -aBu -aRN -aNE -aRN -aRN -aRN -aRN -aLD -aLD -aLD -aLD -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116366,28 +116659,28 @@ avR avR avR avR -aFc -arI -aRN -aSL -aRN -aRN -aio -aSL -aRN -aPF -aPF -aPF -aRN -aRN -aRN -aRN -aRN -aRN -aRN -arI -arI -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116518,28 +116811,28 @@ avR avR avR avR -aFc -arI -aSL -aSL -aRN -aRN -aPF -aPF -aPF -aPF -aPF -aPF -aPF -aRN -aRN -aOx -aRN -aRN -arI -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116670,27 +116963,27 @@ avR avR avR avR -aFc -arI -aPF -aPF -aPF -aPF -aPF -aRN -aPF -aPF -aPF -aPF -aPF -aRN -aRN -aRN -aRN -arI -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116822,26 +117115,26 @@ avR avR avR avR -aFc -arI -arI -arI -arI -arI -arI -arI -aPF -aPF -aPF -aPF -aPF -arI -arI -arI -arI -arI -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -116974,25 +117267,25 @@ avR avR avR avR -aFc -aFc -aFc -aFc -aFc -aFc -aFc -arI -arI -arI -arI -arI -arI -arI -aFc -aFc -aFc -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -117132,15 +117425,15 @@ avR avR avR avR -aFc -aFc -aFc -aFc -aFc -aFc -aFc -aFc -aFc +avR +avR +avR +avR +avR +avR +avR +avR +avR avR avR avR @@ -117745,11 +118038,11 @@ avR avR avR avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc avR avR avR @@ -117893,17 +118186,17 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +arI +arI +arI +aFc +aFc +aFc avR avR avR @@ -118043,20 +118336,20 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +arI +arI +arI +arI +arI +aLD +arI +arI +arI +aFc +aFc avR avR avR @@ -118193,22 +118486,22 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +arI +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -118344,24 +118637,24 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -118495,25 +118788,25 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -118646,27 +118939,27 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -118797,29 +119090,29 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc +aFc avR avR avR @@ -118948,31 +119241,31 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc +aFc avR avR avR @@ -119100,36 +119393,36 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc +aFc +aFc +aFc +aFc +aFc avR avR avR @@ -119251,38 +119544,38 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +arI +arI +arI +arI +aFc +aFc avR avR avR @@ -119403,39 +119696,39 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc +aFc avR avR avR @@ -119554,42 +119847,42 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc +aFc +aFc avR avR avR @@ -119705,45 +119998,45 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +aFc +aFc +aFc avR avR avR @@ -119857,47 +120150,47 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +aFc +aFc +aFc avR avR avR @@ -120009,49 +120302,49 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +aFc +aFc +aFc avR avR aFc @@ -120161,51 +120454,51 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +aFc +aFc +aFc aFc aFc arI @@ -120313,52 +120606,52 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aLD +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +arI +arI aFc arI arI @@ -120465,52 +120758,52 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI aFc aFc arI @@ -120617,53 +120910,53 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI aFc arI arI @@ -120769,57 +121062,57 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -aFc aFc arI aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +arI +arI +aLD aas aas aas @@ -120913,64 +121206,64 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc aFc arI +aBu +aBu +aRN +aRN +aRN +aRN +aOx +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aBu +aBu +aBu +aas +arI arI aVw aas @@ -121064,66 +121357,66 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR aFc +aFc +abS +abS +abS +abS +abS +abS +abS +abS arI -arI +aRN +aRN +aRN +aRN +azA +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aBu +aBu +aBu +aBu +aBu +aas aas aas aas @@ -121216,64 +121509,64 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR aFc +abS +abS +aHj +aDR +aQc +aQc +aQc +aDR +abS +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +aRN +auM +auM +auM +auM +auM +auM +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aBu +aBu +aas +aas arI arI aVw @@ -121367,65 +121660,65 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR aFc +aFc +abS +aHj +aHj +aHj +aHj +aHj +aHj +aHj +abS +aRN +aRN +akj +akj +akj +akj +akj +add +add +add +akj +aRN +aRN +aRN +aRN +aRN +aOx +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +auM +auM +auM +auM +auM +auM +auM +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI arI aLD aLD @@ -121518,65 +121811,65 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +akj +akj +aDR +aHj +aTZ +aDR +aHj +aHj +aHj +abS +aRN +aRN +akj +aoa +aMm +awp +akj +akj +aRZ +aeu +acF +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +auM +auM +auM +auM +auM +auM +auM +auM +auM +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI aFc arI aLD @@ -121670,64 +121963,64 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +akj +akj +aHj +aHj +aHj +aHj +aty +aHJ +aHJ +aHJ +abS +aRN +aRN +akj +aIJ +aSO +aSO +aSO +akj +aah +aeu +axp +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +auM +auM +auM +auM +auM +auM +auM +auM +auM +aRN +aRN +arI +arI +arI +arI +arI +arI +arI aFc aFc arI @@ -121822,64 +122115,64 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +avk +aUd +aHj +aHj +aHj +aHj +aEx +aBs +aSO +axJ +akj +akj +akj +akj +aaH +aSO +aSO +aSO +akj +amp +aoQ +awb +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +aRN +arI +arI +arI +arI +arI +arI +arI +arI +arI +arI +arI +arI +aFc +aFc +aFc +aFc +aFc +aFc aFc arI arI @@ -121974,51 +122267,51 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +avk +aUd +aHj +aHj +aHj +aHj +aOC +aUL +aSO +aSO +aSO +aSO +aSO +afP +aSO +aSO +aSO +aSO +aPB +aNB +aeu +abI +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +aRN +aRN +aRN +arI +arI +aFc +aFc +aFc +aFc aFc aFc aFc @@ -122126,48 +122419,48 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +avk +aUd +aHj +aHj +aHj +aHj +aEx +aUL +aSO +aSO +akj +akj +akj +akj +alW +aKd +aKd +aKd +akj +amp +aeu +awb +aRN +aRN +aRN +aOx +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +arI +arI +arI +aFc +aFc avR avR avR @@ -122278,47 +122571,47 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +avk +aHj +aDR +agl +agl +agl +apo +aAz +aAz +aAz +abS +aRN +aRN +avk +acP +ahA +ahA +aKd +akj +aaj +aah +acF +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc +aFc +aFc +aFc avR avR avR @@ -122430,43 +122723,43 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +akj +akj +aHj +aOv +aOv +agl +agl +aOv +aug +agl +abS +aRN +aRN +avk +aGf +aYT +aYT +aKd +akj +aih +aeu +acF +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc avR avR avR @@ -122582,43 +122875,43 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +abS +abS +aOv +aTk +aOv +aOv +aOv +agl +agl +abS +aRN +aRN +avk +aVY +aVY +aVY +akj +akj +aYC +aeu +acF +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -122735,42 +123028,42 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +abS +abS +abS +agl +agl +agl +abS +abS +abS +aRN +aRN +akj +akj +akj +akj +akj +add +add +add +akj +aRN +aOx +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -122888,40 +123181,40 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +abS +agl +agl +agl +abS +aFc +arI +aRN +aRN +aRN +aGY +ayd +aGY +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc avR avR avR @@ -123042,38 +123335,38 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +abS +agl +agl +agl +abS +aFc +arI +aRN +aRN +aRN +aGY +aSL +aGY +aRN +aBh +aRN +aRN +aRN +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -123194,38 +123487,38 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +abS +agl +agl +agl +abS +aFc +arI +arI +aRN +aRN +aGY +aSL +aGY +aRN +aRN +aBu +aBu +aBu +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -123346,37 +123639,37 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +abS +abS +abS +abS +abS +aFc +aFc +arI +aRN +aRN +aGY +aSL +aGY +aRN +aBu +aBu +aBu +aBu +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -123410,7 +123703,7 @@ aVo aVo aVo ati -aBa +aIc aVQ aas auM @@ -123498,37 +123791,37 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc +arI +aRN +aRN +aha +aSL +aSL +aRN +aBu +aBu +aBu +aBu +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -123657,29 +123950,29 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aRN +azA +aSL +aSL +aSL +aRN +aBu +aBu +aBu +aBu +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +arI +arI +aFc avR avR avR @@ -123809,29 +124102,29 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aRN +aio +aSL +aRN +aSL +aRN +aRN +aio +aBu +aBu +aRN +aOx +aRN +aRN +aLD +aLD +aLD +aLD +arI +aFc +aFc avR avR avR @@ -123961,28 +124254,28 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aRN +aSL +aSL +aRN +aha +aSL +azA +aBu +aRN +aNE +aRN +aRN +aRN +aRN +aLD +aLD +aLD +aLD +arI +aFc avR avR avR @@ -124113,28 +124406,28 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aRN +aSL +aRN +aRN +aio +aSL +aRN +aPF +aPF +aPF +aRN +aRN +aRN +aRN +aRN +aRN +aRN +arI +arI +aFc avR avR avR @@ -124265,28 +124558,28 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aSL +aSL +aRN +aRN +aPF +aPF +aPF +aPF +aPF +aPF +aPF +aRN +aRN +aOx +aRN +aRN +arI +arI +aFc +aFc avR avR avR @@ -124417,27 +124710,27 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +aPF +aPF +aPF +aPF +aPF +aRN +aPF +aPF +aPF +aPF +aPF +aRN +aRN +aRN +aRN +arI +arI +aFc +aFc avR avR avR @@ -124569,26 +124862,26 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +arI +arI +arI +arI +arI +arI +arI +aPF +aPF +aPF +aPF +aPF +arI +arI +arI +arI +arI +aFc +aFc avR avR avR @@ -124721,25 +125014,25 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc +arI +arI +arI +arI +arI +arI +arI +aFc +aFc +aFc +aFc +aFc avR avR avR @@ -124879,15 +125172,15 @@ avR avR avR avR -avR -avR -avR -avR -avR -avR -avR -avR -avR +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc +aFc avR avR avR diff --git a/_maps/map_files/Vampire/westfield_mall/westfield_umbra.dmm b/_maps/map_files/Vampire/westfield_mall/westfield_umbra.dmm index 06428b278e4f..4b3101117890 100644 --- a/_maps/map_files/Vampire/westfield_mall/westfield_umbra.dmm +++ b/_maps/map_files/Vampire/westfield_mall/westfield_umbra.dmm @@ -1995,9 +1995,7 @@ /turf/open/floor/plating/concrete, /area/vtm/outside/penumbra) "Cp" = ( -/obj/effect/landmark/teleport_mark{ - tribe = "Black Spiral Dancers" - }, +/obj/effect/landmark/teleport_mark/wyrm, /turf/open/indestructible/necropolis/air, /area/vtm/outside/penumbra) "Cq" = ( @@ -2593,9 +2591,7 @@ /turf/open/misc/dirt, /area/vtm/outside/penumbra) "Kn" = ( -/obj/effect/landmark/teleport_mark{ - tribe = "Bone Gnawers" - }, +/obj/effect/landmark/teleport_mark/weaver, /turf/open/floor/plating/rough/cave, /area/vtm/outside/penumbra) "Ks" = ( @@ -2657,9 +2653,7 @@ /turf/open/floor/plating/granite, /area/vtm/outside/penumbra) "Lv" = ( -/obj/effect/landmark/teleport_mark{ - tribe = "Glass Walkers" - }, +/obj/effect/landmark/teleport_mark/weaver, /turf/open/floor/plating/canal, /area/vtm/outside/penumbra) "Ly" = ( @@ -3230,9 +3224,7 @@ /turf/open/floor/plating/canal, /area/vtm/outside/penumbra) "UX" = ( -/obj/effect/landmark/teleport_mark{ - tribe = "Children of Gaia" - }, +/obj/effect/landmark/teleport_mark/gaia, /turf/open/misc/grass, /area/vtm/outside/penumbra) "Vb" = ( @@ -3291,9 +3283,7 @@ /turf/open/floor/city/plating_mono, /area/vtm/outside/penumbra) "VD" = ( -/obj/effect/landmark/teleport_mark{ - tribe = "Wendigo" - }, +/obj/effect/landmark/teleport_mark/wyld, /turf/open/misc/dirt, /area/vtm/outside/penumbra) "VE" = ( diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index ab2590e6901a..a7879a5a0731 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -4256,7 +4256,7 @@ /area/centcom/central_command_areas/evacuation) "sS" = ( /obj/structure/table, -/obj/item/toy/katana{ +/obj/item/storage/belt/sheath/katana/toy{ pixel_y = 8 }, /obj/item/toy/plush/carpplushie, diff --git a/_maps/nebulastation.json b/_maps/nebulastation.json index 348949db8c9a..647e63e4e09b 100644 --- a/_maps/nebulastation.json +++ b/_maps/nebulastation.json @@ -21,5 +21,6 @@ "Baseturf": "/turf/open/openspace", "Linkage": "Cross" } - ] + ], + "bonus_weakpoints": 2 } diff --git a/_maps/runtimestation.json b/_maps/runtimestation.json index dcbcc6774174..8e782eabf87d 100644 --- a/_maps/runtimestation.json +++ b/_maps/runtimestation.json @@ -13,5 +13,6 @@ ], "shuttles": { "cargo": "cargo_delta" - } + }, + "bonus_weakpoints": -3 } diff --git a/_maps/templates/holodeck_animeschool.dmm b/_maps/templates/holodeck_animeschool.dmm index 5f81d5744317..d66d08e99bd8 100644 --- a/_maps/templates/holodeck_animeschool.dmm +++ b/_maps/templates/holodeck_animeschool.dmm @@ -13,7 +13,7 @@ /obj/item/paper, /obj/item/pen, /obj/item/clothing/under/costume/seifuku, -/obj/item/toy/katana, +/obj/item/storage/belt/sheath/katana/toy, /turf/open/floor/holofloor, /area/template_noop) "s" = ( diff --git a/_maps/templates/lazy_templates/nukie_base.dmm b/_maps/templates/lazy_templates/nukie_base.dmm index 9782a18ab57c..7d87dc7d4e6f 100644 --- a/_maps/templates/lazy_templates/nukie_base.dmm +++ b/_maps/templates/lazy_templates/nukie_base.dmm @@ -202,9 +202,15 @@ /area/centcom/syndicate_mothership/control) "cT" = ( /obj/structure/rack, -/obj/item/katana/cursed{ - desc = "A gift from your benefactors."; - force = 20 +/obj/item/storage/belt/sheath/katana/empty{ + pixel_y = 3; + pixel_x = 7 + }, +/obj/item/katana{ + pixel_x = -3; + pixel_y = 2; + force = 20; + desc = "A gift from your benefactors." }, /turf/open/floor/catwalk_floor/iron_dark, /area/centcom/syndicate_mothership/control) diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm index 575efae41862..69741ef4bff0 100644 --- a/code/__DEFINES/achievements.dm +++ b/code/__DEFINES/achievements.dm @@ -62,6 +62,7 @@ #define MEDAL_CIGARETTES "Cigarettes" #define MEDAL_SHARKDRAGON "Sharkdragon" #define MEDAL_THEORETICAL_LIMITS "All Within Theoretical Limits" +#define MEDAL_DESENSITIZED "In Flanders Fields" //Skill medal hub IDs #define MEDAL_LEGENDARY_MINER "Legendary Miner" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 0eb7232a8af9..d6f317507c55 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -462,6 +462,8 @@ #define COLOR_AMMO_ARMORPIERCE "#d9d9d9" #define COLOR_AMMO_HOLLOWPOINT "#ff9900" +#define COLOR_DMI_MASK "#a0a0a000" + GLOBAL_LIST_INIT(cable_colors, list( CABLE_COLOR_BLUE = CABLE_HEX_COLOR_BLUE, CABLE_COLOR_CYAN = CABLE_HEX_COLOR_CYAN, diff --git a/code/__DEFINES/dcs/signals/signals_datum.dm b/code/__DEFINES/dcs/signals/signals_datum.dm index 19e28e359849..3668d202a7f0 100644 --- a/code/__DEFINES/dcs/signals/signals_datum.dm +++ b/code/__DEFINES/dcs/signals/signals_datum.dm @@ -22,7 +22,9 @@ #define COMSIG_VV_TOPIC "vv_topic" #define COMPONENT_VV_HANDLED (1<<0) /// from datum ui_act (usr, action) -#define COMSIG_UI_ACT "COMSIG_UI_ACT" +#define COMSIG_UI_ACT "ui_act" +/// from datum/tgui/get_payload(user, list/data) +#define COMSIG_UI_DATA "ui_data" /// fires on the target datum when an element is attached to it (/datum/element) #define COMSIG_ELEMENT_ATTACH "element_attach" @@ -53,3 +55,7 @@ ///from /datum/component/bubble_icon_override/get_bubble_icon(): (list/holder) #define COMSIG_GET_BUBBLE_ICON "get_bubble_icon" + +///from /datum/sprite_editor_workspace/is_valid_color(): (color) +#define COMSIG_SPRITE_EDITOR_VALIDATE_COLOR "sprite_editor_validate_color" + #define COLOR_IS_INVALID (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index 0da9c83b5887..d666a93f0d67 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -221,7 +221,7 @@ #define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting]: #define COMSIG_SUPPLYPOD_RETURNING "supplypodgohome" -///from [/obj/structure/closet/supplypod/proc/preOpen]: +///from [/obj/structure/closet/supplypod/proc/pre_open]: #define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom" /// from [/obj/item/stack/proc/can_merge]: (obj/item/stack/merge_with, in_hand) @@ -429,6 +429,10 @@ #define COMSIG_PROJECTILE_RANGE_OUT "projectile_range_out" ///from the base of /obj/projectile/process(): () #define COMSIG_PROJECTILE_BEFORE_MOVE "projectile_before_move" +///sent to firer at the end of /mob/living/apply_projectile_effects(): (mob/living/target, hit_limb, blocked) +#define COMSIG_PROJECTILE_POST_HIT_LIVING "projectile_post_hit_living" +///sent to projectile at the end of /mob/living/apply_projectile_effects(): (mob/living/target, hit_limb, blocked) +#define COMSIG_PROJECTILE_SELF_POST_HIT_LIVING "projectile_post_hit_living" // FROM [/obj/item/proc/set_embed] sent when an item's embedding properties are changed : () #define COMSIG_ITEM_EMBEDDING_UPDATE "item_embedding_update" @@ -634,3 +638,7 @@ /// Sent from /obj/item/mob_holder/purple_raptor/proc/toggle_wings() : (mob/living/carbon/human/user) #define COMSIG_RAPTOR_WINGS_CLOSED "raptor_wings_closed" + +/// Sent from /obj/effect/rune/convert/try_sacrifice_item(obj/effect/rune/convert/rune) +#define COMSIG_ITEM_CULT_SACRIFICE "item_cult_sacrifice" + #define COMPONENT_SACRIFICE_SUCCESSFUL (1<<0) diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm index cba3fbbd3861..0176504e8112 100644 --- a/code/__DEFINES/inventory.dm +++ b/code/__DEFINES/inventory.dm @@ -163,14 +163,12 @@ DEFINE_BITFIELD(no_equip_flags, list( #define DIGITIGRADE_STYLE 2 //Flags (actual flags, fucker ^) for /obj/item/var/supports_variations_flags -/// No alternative sprites or handling based on bodytype -#define CLOTHING_NO_VARIATION (1<<0) /// Has a sprite for digitigrade legs specifically. -#define CLOTHING_DIGITIGRADE_VARIATION (1<<1) +#define CLOTHING_DIGITIGRADE_VARIATION (1<<0) /// The sprite works fine for digitigrade legs as-is. -#define CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON (1<<2) +#define CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON (1<<1) /// Auto-generates the leg portion of the sprite with GAGS -#define CLOTHING_DIGITIGRADE_MASK (1<<3) +#define CLOTHING_DIGITIGRADE_MASK (1<<2) /// All variation flags which render "correctly" on a digitigrade leg setup #define DIGITIGRADE_VARIATIONS (CLOTHING_DIGITIGRADE_VARIATION|CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON|CLOTHING_DIGITIGRADE_MASK) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index c3c451fcc69c..a5a3319b53e7 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -304,8 +304,6 @@ DEFINE_BITFIELD(job_flags, list( /// Mind traits that should be shared by every head of staff. has to be this way cause byond lists lol #define HEAD_OF_STAFF_MIND_TRAITS TRAIT_FAST_TYING, TRAIT_HIGH_VALUE_RANSOM -#define MEDICAL_MIND_TRAITS TRAIT_DESENSITIZED -#define SECURITY_MIND_TRAITS TRAIT_DESENSITIZED /// Statistically average probability of a random cop or cop-adjacent person consisting of meat of sus domesticus of suidae family, /// also known as swine or hogs, calculated by the university of random numbers diff --git a/code/__DEFINES/lights.dm b/code/__DEFINES/lights.dm index 48d210fe0316..73522f5de388 100644 --- a/code/__DEFINES/lights.dm +++ b/code/__DEFINES/lights.dm @@ -1,5 +1,5 @@ ///How much power emergency lights will consume per tick -#define LIGHT_EMERGENCY_POWER_USE (0.0001 * STANDARD_CELL_RATE) +#define LIGHT_EMERGENCY_POWER_USE (0.0002 * STANDARD_CELL_RATE) // status values shared between lighting fixtures and items #define LIGHT_OK 0 #define LIGHT_EMPTY 1 diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 719a089264bb..b8f437b2e15e 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -51,7 +51,7 @@ #define LOG_RADIO_EMOTE (1 << 20) #define LOG_SPEECH_INDICATORS (1 << 21) #define LOG_TRANSPORT (1 << 22) -#define LOG_STATS (1 << 23) // DARKPACK EDIT ADD - logging stats to prevent abuse +#define LOG_STATS (1 << 23) // DARKPACK EDIT ADD - STORYTELLER_STATS - (logging stats to prevent abuse) // DARKPACK EDIT ADD START - SUBTLE #define LOG_SUBTLE (1 << 24) // DARKPACK EDIT ADD END @@ -63,8 +63,8 @@ #define INDIVIDUAL_EMOTE_LOG (LOG_EMOTE | LOG_RADIO_EMOTE) #define INDIVIDUAL_COMMS_LOG (LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS) #define INDIVIDUAL_OOC_LOG (LOG_OOC | LOG_ADMIN) -#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_RADIO_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_ADMIN_PRIVATE | LOG_ASAY | LOG_MECHA | LOG_VIRUS | LOG_SHUTTLE | LOG_ECON | LOG_VICTIM | LOG_SPEECH_INDICATORS) #define INDIVIDUAL_SUBTLE_LOG (LOG_SUBTLE) // DARKPACK EDIT ADD - SUBTLE +#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_RADIO_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_ADMIN_PRIVATE | LOG_ASAY | LOG_MECHA | LOG_VIRUS | LOG_SHUTTLE | LOG_ECON | LOG_VICTIM | LOG_SPEECH_INDICATORS | LOG_STATS) // DARKPACK EDIT CHANGE - STORYTELLER_STATS - (Added LOG_STATS) #define LOGSRC_CKEY "Ckey" #define LOGSRC_MOB "Mob" diff --git a/code/__DEFINES/mood.dm b/code/__DEFINES/mood.dm index 0dcdc9340d6b..80994ef59072 100644 --- a/code/__DEFINES/mood.dm +++ b/code/__DEFINES/mood.dm @@ -112,3 +112,13 @@ #define BLOCK_NEW_MOOD FALSE /// Return from /be_replaced or /be_refreshed to actually go through and allow the new mood event to be added #define ALLOW_NEW_MOOD TRUE + +/// Threshold for desensitization effects to start applying +#define DESENSITIZED_THRESHOLD 0.5 +/// Minimum level for desensitization multiplier +#define DESENSITIZED_MINIMUM 0.1 +/// Reduction in desensitization level per death witnessed +#define DESENSITIZED_REDUCTION_PER_DEATH 0.025 + +/// Check if a mob is desensitized (passes the threshold) +#define IS_DESENSITIZED(mob) ((mob.mind?.desensitized_level || 1) <= DESENSITIZED_THRESHOLD) diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index 804130804624..5652653b79dc 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -310,3 +310,9 @@ /// Valid values to be returned from reagent holder's spark_act #define SPARK_ACT_RETURNS (SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL) + +// spark_flags values +/// We're reacting in an enclosed container +#define SPARK_ACT_ENCLOSED (1 << 0) +/// We're in a large container or something, so decrease the power of bootleg explosives like welding fuel +#define SPARK_ACT_WEAKEN_COMMON (1 << 1) diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm index b2b39d4d08ce..b9c93bce7c15 100644 --- a/code/__DEFINES/research/anomalies.dm +++ b/code/__DEFINES/research/anomalies.dm @@ -8,7 +8,7 @@ #define MAX_CORES_BIOSCRAMBLER 8 #define MAX_CORES_DIMENSIONAL 8 #define MAX_CORES_ECTOPLASMIC 8 -#define MAX_CORES_WEATHER 8 +#define MAX_CORES_WEATHER 5 ///Defines for the different types of explosion a flux anomaly can have #define FLUX_NO_EMP 0 diff --git a/code/__DEFINES/research/slimes.dm b/code/__DEFINES/research/slimes.dm index 03671ee24d2a..d4c2bddb53b4 100644 --- a/code/__DEFINES/research/slimes.dm +++ b/code/__DEFINES/research/slimes.dm @@ -62,5 +62,8 @@ #define SLIME_TYPE_SILVER "silver" #define SLIME_TYPE_YELLOW "yellow" +// Not a real slime type, used to create random slimes +#define SLIME_TYPE_RANDOM "random" + // The alpha value of transperent slime types #define SLIME_TRANSPARENCY_ALPHA 180 diff --git a/code/__DEFINES/sprite_editor.dm b/code/__DEFINES/sprite_editor.dm new file mode 100644 index 000000000000..30076b7e2cff --- /dev/null +++ b/code/__DEFINES/sprite_editor.dm @@ -0,0 +1,17 @@ +// Color modes +/// Full RGBA color picker +#define SPRITE_EDITOR_COLOR_MODE_RGBA "rgba" +/// RGB color picker without alpha +#define SPRITE_EDITOR_COLOR_MODE_RGB "rgb" +/// Greyscale color picker with a single value +#define SPRITE_EDITOR_COLOR_MODE_GREYSCALE "greyscale" + +// Config flags +#define SPRITE_EDITOR_ALLOW_LAYERS (1<<0) +#define SPRITE_EDITOR_ALLOW_UNDO (1<<1) + +// Tool flags +#define SPRITE_EDITOR_TOOL_PENCIL (1<<0) +#define SPRITE_EDITOR_TOOL_ERASER (1<<1) +#define SPRITE_EDITOR_TOOL_DROPPER (1<<2) +#define SPRITE_EDITOR_TOOL_BUCKET (1<<3) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 9521983acd8f..739dfd190677 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -20,7 +20,7 @@ * * make sure you add an update to the schema_version stable in the db changelog */ -#define DB_MINOR_VERSION 32 +#define DB_MINOR_VERSION 35 // DARKPACK EDIT CHANGE //! ## Timing subsystem diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index ecd01276432b..210b5fc65b68 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -10,6 +10,9 @@ /// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) #define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) +/// Used in the GLOB year and tgui PreInit +#define UTC_YEAR time2text(world.realtime,"YYYY",NO_TIMEZONE) + /// In-universe, SS13 is set 540 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. #define STATION_YEAR_OFFSET -17 // DARKPACK EDIT diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 3401fdf9cf03..6d9cb8e22551 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -47,6 +47,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define STATION_TRAIT_UNIQUE_AI "station_trait_unique_ai" #define STATION_TRAIT_UNNATURAL_ATMOSPHERE "station_trait_unnatural_atmosphere" #define STATION_TRAIT_SPIKED_DRINKS "station_trait_spiked_drinks" +#define STATION_TRAIT_SPAWN_WEAKPOINTS "station_trait_spawn_weakpoints" // Hud traits /// This hud is owned by a client with an open escape menu @@ -136,6 +137,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_STASIS "in_stasis" /// Makes the owner appear as dead to most forms of medical examination #define TRAIT_FAKEDEATH "fakedeath" +/// When applied to a mob's head, their face will be unrecognizable and get displayed as Unknown #define TRAIT_DISFIGURED "disfigured" /// "Magic" trait that blocks the mob from moving or interacting with anything. Used for transient stuff like mob transformations or incorporality in special cases. /// Will block movement, `Life()` (!!!), and other stuff based on the mob. @@ -1610,9 +1612,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait that allows an item to perform holy rites akin to a nullrod #define TRAIT_NULLROD_ITEM "nullrod_item" -/// Mob gets far less severe negative moodlets from seeing death / blood -#define TRAIT_DESENSITIZED "desensitized" - /// Trait specifying that an AI has a remote connection to an integrated circuit #define TRAIT_CONNECTED_TO_CIRCUIT "connected_to_circuit" @@ -1641,4 +1640,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Object is dangerous to mobs buckled to it #define TRAIT_DANGEROUS_BUCKLE "dangerous_buckle" +/// Makes the owner desensetized to death, but happy whenever someone gets blown to pieces (as a sacrifice to the necropolis) unless its another worshipper +#define TRAIT_NECROPOLIS_WORSHIP "necropolis_worship" + // END TRAIT DEFINES diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 8cbe3049d0f0..e0cd34d80656 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -123,6 +123,7 @@ #define GENETICS_SPELL "genetics_spell" #define EYES_COVERED "eyes_covered" #define NO_EYES "no_eyes" +#define NO_EARS "no_ears" #define HYPNOCHAIR_TRAIT "hypnochair" #define FLASHLIGHT_EYES "flashlight_eyes" #define IMPURE_OCULINE "impure_oculine" @@ -344,3 +345,6 @@ /// Trait gained by a guardian who is recalled #define GUARDIAN_RECALLED "guardian_recalled" + +/// Trait granted as a result of a mob being husked +#define HUSK_TRAIT "husk" diff --git a/code/__DEFINES/~darkpack/auras.dm b/code/__DEFINES/~darkpack/auras.dm index 514c44e06e60..6c7e04a137c3 100644 --- a/code/__DEFINES/~darkpack/auras.dm +++ b/code/__DEFINES/~darkpack/auras.dm @@ -35,7 +35,7 @@ #define AURA_VAMPIRE (FALSE) // Aura colors are pale #define AURA_GHOUL (FALSE) // Pale blotches in the aura #define AURA_MAGIC_USE (FALSE) // Myriad sparkles in aura -- Not implemented. Mages. -#define AURA_WEREBEAST (FALSE) // Bright, vibrant aura -- DARKPACK TODO - GAROU +#define AURA_WEREBEAST (FALSE) // Bright, vibrant aura // WEREWOLF #define AURA_GHOST (FALSE) // Weak, intermittent aura #define AURA_FAERIE (FALSE) // Rainbow highlights in aura -- Not implemented. Changelings. diff --git a/code/__DEFINES/~darkpack/combat.dm b/code/__DEFINES/~darkpack/combat.dm index 629e884fd051..164bad9dfd99 100644 --- a/code/__DEFINES/~darkpack/combat.dm +++ b/code/__DEFINES/~darkpack/combat.dm @@ -1,11 +1,16 @@ //normal duration defines +/// W20 p. 239, V20 p. 254: lists turns as taking between 3 seconds (the norm for combat) to three minutes, depending on the pace of the scene ///Duration of one "turn", which is 5 seconds according to us #define TURNS * 5 SECONDS +// Scenes have 0 hard defined rules for length. ///Duration of one "scene", which is 3 minutes according to us #define SCENES * 3 MINUTES +#define TURNS_PER_SCENE ((1 SCENES) / (1 TURNS)) // To eyeball damage as its calcuated in the ttrpg -#define TTRPG_DAMAGE * 5 +#define TTRPG_DAMAGE * 10 +// Heavy placeholder to represent that lethal is ... twice as bad as bashing (brute basiclly) +#define LETHAL_TTRPG_DAMAGE * 20 // Unused for now #define BASHING "bashing" diff --git a/code/__DEFINES/~darkpack/fera/fera.dm b/code/__DEFINES/~darkpack/fera/fera.dm new file mode 100644 index 000000000000..7a93866a0454 --- /dev/null +++ b/code/__DEFINES/~darkpack/fera/fera.dm @@ -0,0 +1,28 @@ +/// How long it takes for a werewolf's Veil to naturally repair +// #define UMBRA_VEIL_COOLDOWN 15 MINUTES +/// How long it takes for a werewolf to regenerate a blood point +// #define FERA_BP_REGEN 60 SECONDS // I cant find a source to this. +/// Cooldown between Veil violations +// #define VEIL_COOLDOWN 20 SECONDS + +#define MAX_RAGE 10 +#define MAX_GNOSIS 10 +#define PRIMAL_URGE_PLACEHOLDER 3 + +#define BREED_HOMID "Homid" +#define BREED_LUPUS "Lupus" +#define BREED_CRINOS "Metis" //Its called metis because anything player facing should only show "Metis" instead of "Crinos", despite it being a crinos form. Blame gadabout. + +#define FEATURE_FUR_COLOR "fera_fur" + +// fera will likely have a seperate list of colors but uses the same feature or var as other fera +GLOBAL_LIST_INIT(garou_fur_colors, list( + "black" = "black", + "gray" = "gray", + "red" = "red", + "white" = "white", + "ginger" = "ginger", + "brown" = "brown" +)) + +#define STATUS_EFFECT_SILVER_BULLLET_STACKS /datum/status_effect/stacking/silver_bullets diff --git a/code/__DEFINES/~darkpack/fera/werewolf_auspice.dm b/code/__DEFINES/~darkpack/fera/werewolf_auspice.dm new file mode 100644 index 000000000000..d4363c0da788 --- /dev/null +++ b/code/__DEFINES/~darkpack/fera/werewolf_auspice.dm @@ -0,0 +1,18 @@ +// Auspices + +#define AUSPICE_RAGABASH "Ragabash" +#define AUSPICE_THEURGE "Theurge" +#define AUSPICE_PHILODOX "Philodox" +#define AUSPICE_AHROUN "Ahroun" +#define AUSPICE_GALLIARD "Galliard" + +#define AUSPICE_NONE "Stolen Moon" + +#define MOON_NEW "new moon" +#define MOON_WAXING_CRESENT "waxing cresent" +#define MOON_FIRST_QUARTER "first quarter" +#define MOON_WAXING_GIBBOUS "waxing gibbous" +#define MOON_FULL "full moon" +#define MOON_WANING_GIBBOUS "waning gibbous" +#define MOON_LAST_QUARTER "last quarter" +#define MOON_WANING_CRESCENT "waning crescent" diff --git a/code/__DEFINES/~darkpack/fera/werewolf_renown.dm b/code/__DEFINES/~darkpack/fera/werewolf_renown.dm new file mode 100644 index 000000000000..9b961c382f56 --- /dev/null +++ b/code/__DEFINES/~darkpack/fera/werewolf_renown.dm @@ -0,0 +1,13 @@ +#define RENOWN_HONOR "honor" +#define RENOWN_GLORY "glory" +#define RENOWN_WISDOM "wisdom" + +#define ALL_RENOWNS list(RENOWN_HONOR, RENOWN_GLORY, RENOWN_WISDOM) + +#define RANK_CUB 0 +#define RANK_CLIATH 1 +#define RANK_FOSTERN 2 +#define RANK_ADREN 3 +#define RANK_ATHRO 4 +#define RANK_ELDER 5 +#define RANK_LEGEND 6 diff --git a/code/__DEFINES/~darkpack/fera/werewolf_tribe.dm b/code/__DEFINES/~darkpack/fera/werewolf_tribe.dm new file mode 100644 index 000000000000..6c040cf46c96 --- /dev/null +++ b/code/__DEFINES/~darkpack/fera/werewolf_tribe.dm @@ -0,0 +1,25 @@ +/// No Tribe. +#define TRIBE_RONIN "Ronin" + +#define TRIBE_BLACK_FURIES "Black Furies" +#define TRIBE_BONE_GNAWERS "Bone Gnawers" +#define TRIBE_CHILDREN_OF_GAIA "Children of Gaia" +#define TRIBE_FIANNA "Fianna" +#define TRIBE_GALESTALKERS "Galestalkers" +#define TRIBE_GET_OF_FENRIS "Get of Fenris" +#define TRIBE_UKTENA "Uktena" +#define TRIBE_GLASS_WALKERS "Glass Walkers" +#define TRIBE_RED_TALONS "Red Talons" +#define TRIBE_SHADOW_LORDS "Shadow Lords" +#define TRIBE_SILENT_STRIDERS "Silent Striders" +#define TRIBE_SILVER_FANGS "Silver Fangs" +#define TRIBE_STARGAZERS "Stargazers" +#define TRIBE_BLACK_SPIRAL_DANCERS "Black Spiral Dancers" + +#define TRIBE_CORAX "Corax" // DARKPACK TODO - CORAX + +#define TRIBE_ALL list(TRIBE_RONIN, TRIBE_GALESTALKERS, TRIBE_CHILDREN_OF_GAIA, TRIBE_UKTENA, TRIBE_FIANNA, TRIBE_GET_OF_FENRIS, TRIBE_BLACK_FURIES, TRIBE_SILVER_FANGS, TRIBE_SILENT_STRIDERS, TRIBE_RED_TALONS, TRIBE_STARGAZERS, TRIBE_GLASS_WALKERS, TRIBE_BONE_GNAWERS, TRIBE_SHADOW_LORDS, TRIBE_BLACK_SPIRAL_DANCERS, TRIBE_CORAX) +#define TRIBE_GAIA list(TRIBE_GALESTALKERS, TRIBE_CHILDREN_OF_GAIA, TRIBE_UKTENA, TRIBE_FIANNA, TRIBE_GET_OF_FENRIS, TRIBE_BLACK_FURIES, TRIBE_SILVER_FANGS, TRIBE_SILENT_STRIDERS, TRIBE_RED_TALONS, TRIBE_STARGAZERS, TRIBE_GLASS_WALKERS, TRIBE_BONE_GNAWERS, TRIBE_SHADOW_LORDS, TRIBE_CORAX) +#define TRIBE_WYLD list(TRIBE_GALESTALKERS, TRIBE_CHILDREN_OF_GAIA, TRIBE_UKTENA, TRIBE_FIANNA, TRIBE_GET_OF_FENRIS, TRIBE_BLACK_FURIES, TRIBE_SILVER_FANGS, TRIBE_SILENT_STRIDERS, TRIBE_RED_TALONS, TRIBE_STARGAZERS, TRIBE_SHADOW_LORDS, TRIBE_CORAX) +#define TRIBE_WEAVER list(TRIBE_GLASS_WALKERS, TRIBE_BONE_GNAWERS) +#define TRIBE_WYRM list(TRIBE_BLACK_SPIRAL_DANCERS) diff --git a/code/__DEFINES/~darkpack/generations.dm b/code/__DEFINES/~darkpack/generations.dm index d8b48a78e502..1a63f8a851f2 100644 --- a/code/__DEFINES/~darkpack/generations.dm +++ b/code/__DEFINES/~darkpack/generations.dm @@ -4,7 +4,7 @@ * Lower = stronger */ -/// Limit for highest generation possible, Based off v20 Beckket's Jyhad Diary +/// Limit for highest generation possible, Based off V20 Beckket's Jyhad Diary #define HIGHEST_GENERATION_LIMIT 16 /// Limit for lowest generation possible #define LOWEST_GENERATION_LIMIT 1 diff --git a/code/__DEFINES/~darkpack/lockpick_difficulty.dm b/code/__DEFINES/~darkpack/lockpick_difficulty.dm deleted file mode 100644 index 0cfae7600b88..000000000000 --- a/code/__DEFINES/~darkpack/lockpick_difficulty.dm +++ /dev/null @@ -1,14 +0,0 @@ -#define LOCKDIFFICULTY_1 2 -#define LOCKTIMER_1 1.7 SECONDS -#define LOCKDIFFICULTY_2 3 -#define LOCKTIMER_2 2.0 SECONDS -#define LOCKDIFFICULTY_3 4 -#define LOCKTIMER_3 2.2 SECONDS -#define LOCKDIFFICULTY_4 6 -#define LOCKTIMER_4 2.4 SECONDS -#define LOCKDIFFICULTY_5 7 -#define LOCKTIMER_5 2.6 SECONDS -#define LOCKDIFFICULTY_6 8 //originally should have been 10, but that wouldn't work unless locktimer is explicitly declared beforehand, which it won't be -#define LOCKTIMER_6 2.8 SECONDS -#define LOCKDIFFICULTY_7 10 -#define LOCKTIMER_7 3 SECONDS diff --git a/code/__DEFINES/~darkpack/mobs.dm b/code/__DEFINES/~darkpack/mobs.dm index 06f3ad530bbc..2a2dab3f5abd 100644 --- a/code/__DEFINES/~darkpack/mobs.dm +++ b/code/__DEFINES/~darkpack/mobs.dm @@ -4,16 +4,27 @@ #define isavatar(A) (istype(A, /mob/living/basic/avatar)) #define iszomboid(A) (istype(A, /mob/living/basic/zombie) || (istype(A, /mob/living/basic/beastmaster/giovanni_zombie))) -// DARKPACK TODO - implement other splats -#define isgarou(A) (FALSE) +#define SPECIES_FERA "fera" -#define iswerewolf(A) (FALSE) -#define iscrinos(A) (FALSE) -#define islupus(A) (FALSE) +#define SPECIES_FERA_HOMID "homid" +#define SPECIES_FERA_BESTIAL "bestial" +#define SPECIES_FERA_WAR "war" +#define SPECIES_FERA_DIRE "dire" +#define SPECIES_FERA_FERAL "feral" -#define iscorax(A) (FALSE) -#define iscorvid(A) (FALSE) -#define iscoraxcrinos(A) (FALSE) +// #define SPECIES_GAROU "garou" + +// #define isshifted(A) (istype(A, /mob/living/carbon/human/fera)) +//homid +#define ishomid(A) (is_species(A, /datum/species/human/shifter/homid)) +//bestial e.g glabro +#define isglabro(A) (is_species(A, /datum/species/human/shifter/bestial)) +//war e.g crinos +#define iscrinos(A) (is_species(A, /datum/species/human/shifter/war)) +//dire e.g hispo +#define ishispo(A) (is_species(A, /datum/species/human/shifter/dire)) +//feral e.g lupus +#define islupus(A) (is_species(A, /datum/species/human/shifter/feral)) #define isnpc(A) (istype(A, /mob/living/carbon/human/npc)) diff --git a/code/__DEFINES/~darkpack/splats.dm b/code/__DEFINES/~darkpack/splats.dm index 2122041178bb..bdc75565c439 100644 --- a/code/__DEFINES/~darkpack/splats.dm +++ b/code/__DEFINES/~darkpack/splats.dm @@ -3,5 +3,11 @@ #define SPLAT_KINDRED "splat_kindred" #define SPLAT_GHOUL "splat_ghoul" -// Presently unimplmented. Used for wolf jobs. + +#define SPLAT_KINFOLK "splat_kinfolk" +/// Parent type for shifters. Not player facing. Shouldnt be needed but put here for clarity. +//#define SPLAT_FERA "splat_fera" #define SPLAT_GAROU "splat_garou" +#define SPLAT_CORAX "splat_corax" // DARKPACK TODO - CORAX + +#define SPLAT_ALL list(SPLAT_NONE, SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_KINFOLK, SPLAT_GAROU) diff --git a/code/__DEFINES/~darkpack/status_effects_debuffs.dm b/code/__DEFINES/~darkpack/status_effects_debuffs.dm index f3e64f12580d..f3ba3642907a 100644 --- a/code/__DEFINES/~darkpack/status_effects_debuffs.dm +++ b/code/__DEFINES/~darkpack/status_effects_debuffs.dm @@ -1,4 +1,2 @@ -// This is the new file that should hold all the debuff status effects in wod13 -#define STATUS_EFFECT_SILVER_SLOWDOWN /datum/status_effect/silver_slowdown //slows down any mobs with a bane to silver. #define STATUS_EFFECT_AWE /datum/status_effect/awe diff --git a/code/__DEFINES/~darkpack/storyteller_dice.dm b/code/__DEFINES/~darkpack/storyteller_dice.dm index b7e8f9d2516c..f1ae455292c4 100644 --- a/code/__DEFINES/~darkpack/storyteller_dice.dm +++ b/code/__DEFINES/~darkpack/storyteller_dice.dm @@ -5,3 +5,14 @@ //Used for /datum/preference/choiced/dice_output #define DICE_OUTPUT_BALLOON "Balloon" #define DICE_OUTPUT_CHAT "Chat" + +/// Output is shown to everyone near you +#define ROLL_PUBLIC "public" +/// Output is only shown to the roller +#define ROLL_PRIVATE "private" +/// Output of the roll to admins + you +#define ROLL_PRIVATE_ADMIN "private+admin" +/// Output of the roll to admins only +#define ROLL_ADMIN "admin" +/// Output is show to no one and is not logged +#define ROLL_NONE "none" diff --git a/code/__DEFINES/~darkpack/traits/declarations.dm b/code/__DEFINES/~darkpack/traits/declarations.dm index 5291c3c51d00..1d0d3424b7f0 100644 --- a/code/__DEFINES/~darkpack/traits/declarations.dm +++ b/code/__DEFINES/~darkpack/traits/declarations.dm @@ -40,9 +40,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_DIABLERIE "diablerie" #define TRAIT_GULLET "gullet" #define TRAIT_CHARMER "charmer" + +// Mutates the apperance of auras #define TRAIT_COLD_AURA "cold_aura" #define TRAIT_WARM_AURA "warm_aura" #define TRAIT_FRENETIC_AURA "frenetic_aura" + #define TRAIT_BLUSH_OF_HEALTH "blush_of_health" /// The mob will automatically breach the Masquerade when seen by others, with no exceptions #define TRAIT_UNMASQUERADE "unmasquerade" @@ -95,7 +98,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_MYSTICISM_KNOWLEDGE "mysticism_knowledge" #define TRAIT_NECROMANCY_KNOWLEDGE "necromancy_knowledge" #define TRAIT_SERPENTIS_SKIN "serpent_skin" -#define TRAIT_ILLEGAL_IDENTITY "illegal_identity" // GOVERMENT + // Allows the user to pass through doors #define TRAIT_PASSDOOR "trait_passdoor" // DARKPACK EDIT ADD - Obtenebration & Mysticism Rework @@ -112,4 +115,29 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai // Is the Vampire currently hungry? Hunger is defined at a bloodpool rating of 7 - self control (if humanity alignment) or instincts (enlightenment alignment) // its called this because theres apparently already a defined quirk called 'hungry' which appears to lower your blood drawn from biting by half. #define TRAIT_NEEDS_BLOOD "vampire_hungry" + +/// If the species has garou breeds to select. +#define TRAIT_WTA_GAROU_BREED "wta_garou_breeds" +// if the species has garou tribes to select. +#define TRAIT_WTA_GAROU_TRIBE "wta_garou_tribes" +// If the species has garou auspices to select. +#define TRAIT_WTA_GAROU_AUSPICE "wta_garou_auspice" +// This mob has fur! +#define TRAIT_FERA_FUR "fera_fur" +/// If the fera is wyrm tainted. Affects their sprite. +#define TRAIT_WYRMTAINTED "wyrm_tainted" +/// For living mobs to prevent adjustments to their lying angle. Used primarly for fera. +#define TRAIT_NO_LYING_ANGLE "no_lying_angle" +// Expensive but allows us to ensure there resting gets updated. +#define TRAIT_TRANSFORM_UPDATES_ICON "transform_updates_icon" +// Massivly boosts the range of your howl emote. +#define TRAIT_LOUD_HOWLER "loud_howler" +/// Prevents the mob from picking up items larger then small +#define TRAIT_SMALL_HANDS "small_hands" + +// BELOW ARE ALL MERITS/FLAWS +#define TRAIT_ILLEGAL_IDENTITY "illegal_identity" // GOVERNMENT +#define TRAIT_PERMAFANGS "permafangs" + + // END TRAIT DEFINES diff --git a/code/__DEFINES/~darkpack/werewolf_auspice.dm b/code/__DEFINES/~darkpack/werewolf_auspice.dm deleted file mode 100644 index b3bb9b8fda50..000000000000 --- a/code/__DEFINES/~darkpack/werewolf_auspice.dm +++ /dev/null @@ -1,7 +0,0 @@ -#define AUSPICE_AHROUN "Ahroun" -#define AUSPICE_GALLIARD "Galliard" -#define AUSPICE_PHILODOX "Philodox" -#define AUSPICE_THEURGE "Theurge" -#define AUSPICE_RAGABASH "Ragabash" - -#define AUSPICE_NONE "Stolen Moon" diff --git a/code/__DEFINES/~darkpack/werewolf_tribe.dm b/code/__DEFINES/~darkpack/werewolf_tribe.dm deleted file mode 100644 index 9a38b204d143..000000000000 --- a/code/__DEFINES/~darkpack/werewolf_tribe.dm +++ /dev/null @@ -1,28 +0,0 @@ -#define TRIBE_NONE "Ronin" - -#define TRIBE_GALESTALKERS "Galestalkers" -#define TRIBE_CHILDRENOFGAIA "Children of Gaia" -#define TRIBE_GHOSTCOUNCIL "Ghost Council" -#define TRIBE_HARTWARDENS "Hart Wardens" -#define TRIBE_GETOFFENRIS "Get of Fenris" -#define TRIBE_BLACKFURIES "Black Furies" -#define TRIBE_SILVERFANGS "Silver Fangs" -#define TRIBE_SILENTSTRIDERS "Silent Striders" -#define TRIBE_REDTALONS "Red Talons" -#define TRIBE_STARGAZERS "Stargazers" -#define TRIBE_GLASSWALKERS "Glass Walkers" -#define TRIBE_BONEGNAWERS "Bone Gnawers" -#define TRIBE_SHADOWLORDS "Shadow Lords" -#define TRIBE_BLACKSPIRALDANCERS "Black Spiral Dancers" - -#define TRIBE_CORAX "Corax" - -#define TRIBE_ALL list(TRIBE_NONE, TRIBE_GALESTALKERS, TRIBE_CHILDRENOFGAIA, TRIBE_GHOSTCOUNCIL, TRIBE_HARTWARDENS, TRIBE_GETOFFENRIS, TRIBE_BLACKFURIES, TRIBE_SILVERFANGS, TRIBE_SILENTSTRIDERS, TRIBE_REDTALONS, TRIBE_STARGAZERS, TRIBE_GLASSWALKERS, TRIBE_BONEGNAWERS, TRIBE_SHADOWLORDS, TRIBE_BLACKSPIRALDANCERS, TRIBE_CORAX) - -#define TRIBE_GAIA list(TRIBE_GALESTALKERS, TRIBE_CHILDRENOFGAIA, TRIBE_GHOSTCOUNCIL, TRIBE_HARTWARDENS, TRIBE_GETOFFENRIS, TRIBE_BLACKFURIES, TRIBE_SILVERFANGS, TRIBE_SILENTSTRIDERS, TRIBE_REDTALONS, TRIBE_STARGAZERS, TRIBE_GLASSWALKERS, TRIBE_BONEGNAWERS, TRIBE_SHADOWLORDS, TRIBE_CORAX) - -#define TRIBE_WYLD list(TRIBE_GALESTALKERS, TRIBE_CHILDRENOFGAIA, TRIBE_GHOSTCOUNCIL, TRIBE_HARTWARDENS, TRIBE_GETOFFENRIS, TRIBE_BLACKFURIES, TRIBE_SILVERFANGS, TRIBE_SILENTSTRIDERS, TRIBE_REDTALONS, TRIBE_STARGAZERS, TRIBE_SHADOWLORDS, TRIBE_CORAX) - -#define TRIBE_WEAVER list(TRIBE_GLASSWALKERS, TRIBE_BONEGNAWERS) - -#define TRIBE_WYRM list(TRIBE_BLACKSPIRALDANCERS) diff --git a/code/__HELPERS/abstract_types.dm b/code/__HELPERS/abstract_types.dm index ecf8fb35bb4b..e2013ad4bbe7 100644 --- a/code/__HELPERS/abstract_types.dm +++ b/code/__HELPERS/abstract_types.dm @@ -10,6 +10,13 @@ return abstracts +// The time complexity of these two procs is really bad, sitting at O(n * m). +// And yet, the overhead is ridiculously small for some reason. We never figured out why. +// So doing list subtraction instead of filtering is faster in all cases, at least for now. +// If we ever breach like 3000-5000 abstract types, that will likely change. +// As of right now we're at 272 or so, with subtraction being ~10x faster. +// Curse this language, for you shall live in hell alongside me. + /// Like subtypesof, but automatically excludes abstract typepaths /proc/valid_subtypesof(datum/sometype) return subtypesof(sometype) - get_abstract_types() diff --git a/code/__HELPERS/colors.dm b/code/__HELPERS/colors.dm index 6a5d6025fd45..d9da27f62c5b 100644 --- a/code/__HELPERS/colors.dm +++ b/code/__HELPERS/colors.dm @@ -264,3 +264,26 @@ modify.underlays[underlay_index] = filter_appearance_recursive(modify.underlays[underlay_index], filter_to_apply) return modify + +#define ALPHA_COMPOSE(src_a, comp_a, back_ch, src_ch) ((1 - src_a / comp_a) * back_ch + (src_a / comp_a) * src_ch) + +/// Blend two colors using the normal blend mode of the CSS compositing algorithm +/proc/blend_color(backdrop = "#00000000", source) + var/list/rgb_source = split_color(source) + var/source_alpha = rgb_source[4] + if(source_alpha == 0) + return backdrop + if(source_alpha == 255) + return source + var/list/rgb_backdrop = split_color(backdrop) + var/backdrop_alpha = rgb_backdrop[4] / 255 + source_alpha /= 255 + var/output_alpha = source_alpha + backdrop_alpha - source_alpha * backdrop_alpha + return rgb( + ALPHA_COMPOSE(source_alpha, output_alpha, rgb_backdrop[1], rgb_source[1]), + ALPHA_COMPOSE(source_alpha, output_alpha, rgb_backdrop[2], rgb_source[2]), + ALPHA_COMPOSE(source_alpha, output_alpha, rgb_backdrop[3], rgb_source[3]), + output_alpha * 255 + ) + +#undef ALPHA_COMPOSE diff --git a/code/__HELPERS/honkerblast.dm b/code/__HELPERS/honkerblast.dm index e76d81782284..c3926d421b01 100644 --- a/code/__HELPERS/honkerblast.dm +++ b/code/__HELPERS/honkerblast.dm @@ -8,7 +8,7 @@ playsound(origin_turf, 'sound/items/airhorn/airhorn.ogg', 100, TRUE) for(var/mob/living/carbon/victim in hearers(max(light_range, medium_range, heavy_range), origin_turf)) - if(!victim.can_hear()) + if(HAS_TRAIT(victim, TRAIT_DEAF)) continue var/distance = get_dist(origin_turf, victim.loc) if(distance <= heavy_range) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index ac67848447ad..4f23df1d0aa4 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1393,3 +1393,50 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects) icon_cache[job_type] = sechud_icon return icon(icon_cache[job_type]) + +/** + * Copies the pixel colors from the passed in icon `I` to the 2d list `grid` + */ +/proc/fill_grid_from_icon(list/grid, icon/I) + var/width = I.Width() + var/height = I.Height() + for(var/x in 1 to width) + for(var/y in 1 to height) + grid[y][x] = I.GetPixel(x,height+1-y) + +// Given a number of frames for an icon state, and the dimensions of the icon, returns the ideal dimensions for a DMI file +/proc/calculate_optimal_icon_grid_dimensions(width, height, count) + var/grid_width = 1 + var/grid_height = 1 + while(grid_width * grid_height < count) + if(height*grid_height < width*grid_width) + grid_height++ + else + grid_width++ + return list(grid_height, grid_width) + +// Reorder the 2d pixel data of the passed in frames into a data string that can be passed to rustg_dmi_create_png +/proc/reorder_pixels(icon_width, icon_height, grid_width, grid_height, list/frames) + var/file_height = icon_height * grid_height + + // This little trick right here reduces the total iteration of repeat_string from the product of the arguments to their sum. + // Can't be applied to the general case without a complex partitioning algorithm, + // since the count could either be a large prime or have large primes as factors + var/linear_pixels = COLOR_DMI_MASK + for(var/count in list(icon_width, icon_height, grid_width, grid_height)) + if(count == 1) + continue + linear_pixels = repeat_string(count, linear_pixels) + + for(var/i in 1 to length(frames)) + var/list/frame = frames[i] + var/row_index = floor((i-1)/grid_width) + var/column = (i-1)%grid_width + for(var/y in 1 to length(frame)) + var/list/row = jointext(frame[y], "") + var/splice_start = (row_index+y-1)*file_height + column*icon_width + 1 + linear_pixels = splicetext(splice_start*9, (splice_start+icon_width)*9, row) + var/zero_alpha_regex = regex(@@#(?:(?!a0a0a0)([0-9]|[a-f]){6}00)@, "gi") + linear_pixels = replacetext(linear_pixels, zero_alpha_regex, COLOR_DMI_MASK) + return linear_pixels + diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm index bbb0d9db5ba4..e1be4c7ad054 100644 --- a/code/__HELPERS/priority_announce.dm +++ b/code/__HELPERS/priority_announce.dm @@ -192,7 +192,7 @@ var/datum/callback/should_play_sound_callback = astype(should_play_sound) for(var/mob/target in players) - if(isnewplayer(target) || !target.can_hear()) + if(isnewplayer(target) || HAS_TRAIT(target, TRAIT_DEAF)) continue to_chat(target, announcement) diff --git a/code/__HELPERS/reagents.dm b/code/__HELPERS/reagents.dm index 181fa0d06db9..d616bdbd5cb7 100644 --- a/code/__HELPERS/reagents.dm +++ b/code/__HELPERS/reagents.dm @@ -86,8 +86,7 @@ /datum/reagents/proc/create_foam(foamtype, foam_volume, result_type = null, notification = null, log = FALSE) var/location = get_turf(my_atom) - var/datum/effect_system/fluid_spread/foam/foam = new foamtype() - foam.set_up(amount = foam_volume, holder = my_atom, location = location, carry = src, result_type = result_type) + var/datum/effect_system/fluid_spread/foam/foam = new foamtype(location, null, foam_volume, my_atom, carry = src, result_type = result_type) foam.start(log = log) clear_reagents() diff --git a/code/__HELPERS/stack_trace.dm b/code/__HELPERS/stack_trace.dm index bb2d78de1108..7a99ff6c2f79 100644 --- a/code/__HELPERS/stack_trace.dm +++ b/code/__HELPERS/stack_trace.dm @@ -2,3 +2,39 @@ /// Do not call directly, use the [stack_trace] macro instead. /proc/_stack_trace(message, file, line) CRASH("[message][WORKAROUND_IDENTIFIER][json_encode(list(file, line))][WORKAROUND_IDENTIFIER]") + + +#if (DM_BUILD > 1667) +#warn if this is ci please remove my Note: comment below, thanks, love you! +#endif +#define STACK_DEPTH_SEARCH_LIMIT 2000 +/// Returns an ordered list of all our parent procs, highest to deepest +/// Note: This will frequently cause erorrs and have seemingly infinitely repeating procs on the current good byond version +/// It'll be fixed when we can update to latest for dev but you likely can't do that right now (since debugging hasn't been fixed yet) +/// BIGGER, MORE IMPORTANT NOTE: Should not be used on master maybe ever, introspection like this is mostly useful for debugging +/// if you have another use I suspect you are just creating god's strongest footgun and should rethink things +/proc/dump_stack(max_depth = STACK_DEPTH_SEARCH_LIMIT) + var/list/proc_paths = list() + var/crashed = FALSE + var/depth = 0 + var/callee/stack_entry = caller + try + while(!isnull(stack_entry) && depth <= max_depth) + proc_paths += stack_entry.proc + stack_entry = stack_entry.caller + depth += 1 + catch + //union job. avoids crashing the stack again + //I just do not trust this construct to work reliably + crashed = TRUE + + if(crashed) + stack_trace("dump_stack's stack walking crashed after walking [length(proc_paths)] procs, Last Read: [proc_paths[length(proc_paths)]] Last Accessed: [stack_entry]") + return proc_paths + + if(depth > max_depth) + stack_trace("dump_stack's stack walking exceeded our soft limit after walking [length(proc_paths)] procs, Last Read: [proc_paths[length(proc_paths)]] Next Accessed: [stack_entry]") + return proc_paths + return proc_paths + +#undef STACK_DEPTH_SEARCH_LIMIT diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm index dd07d1fc9aac..6e23be76ed39 100644 --- a/code/__HELPERS/type_processing.dm +++ b/code/__HELPERS/type_processing.dm @@ -100,7 +100,7 @@ GLOBAL_LIST_INIT(fancy_type_replacements, list( var/list/local_replacements = zebra_typecacheof(fancy_type_cache, ignore_root_path = TRUE) var/list/local_texts = list() for(var/key in fancy_type_cache) - local_texts[local_replacements[key]] = "[key]" + local_texts[fancy_type_cache[key]] = "[key]" types_to_replacement = local_replacements replacement_to_text = local_texts diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 7046a2f8f4db..cc01b194bd8d 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -533,7 +533,6 @@ DEFINE_BITFIELD(head_flags, list( )) DEFINE_BITFIELD(supports_variations_flags, list( - "CLOTHING_NO_VARIATION" = CLOTHING_NO_VARIATION, "CLOTHING_DIGITIGRADE_VARIATION" = CLOTHING_DIGITIGRADE_VARIATION, "CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON" = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON, "CLOTHING_DIGITIGRADE_MASK" = CLOTHING_DIGITIGRADE_MASK, diff --git a/code/_globalvars/darkpack/lists/masquerade.dm b/code/_globalvars/darkpack/lists/masquerade.dm index c83c6b33df1b..c2fffb11b4f3 100644 --- a/code/_globalvars/darkpack/lists/masquerade.dm +++ b/code/_globalvars/darkpack/lists/masquerade.dm @@ -4,5 +4,7 @@ GLOBAL_LIST_EMPTY(logging_machines) GLOBAL_LIST_EMPTY(blood_hunt_announcers) /// List of people who have created unresolved major Masquerade breaches GLOBAL_LIST_EMPTY(masquerade_breakers_list) +/// List of people who have created unresolved major Veil breaches (Masquerade but for werewolves/fera) +GLOBAL_LIST_EMPTY(veil_breakers_list) /// Areas that people will be transported to if they latejoin with a Masquerade-violating appearance GLOBAL_LIST_EMPTY(masquerade_latejoin) diff --git a/code/_globalvars/darkpack/lists/splats.dm b/code/_globalvars/darkpack/lists/splats.dm index c24136b76a38..928ed2ae05cb 100644 --- a/code/_globalvars/darkpack/lists/splats.dm +++ b/code/_globalvars/darkpack/lists/splats.dm @@ -1,7 +1,7 @@ /// An assoc list of splat IDs to type paths GLOBAL_LIST_INIT(splat_list, init_splat_list()) /// List of all splat prototypes to reference, assoc [type] = prototype -GLOBAL_LIST_INIT_TYPED(splat_prototypes, /datum/splat, init_splat_prototypes()) +GLOBAL_LIST_INIT_TYPED(splat_prototypes, /datum/splat, init_subtypes_w_path_keys(/datum/splat, list())) /proc/init_splat_list() var/list/splat_list = list() @@ -9,11 +9,14 @@ GLOBAL_LIST_INIT_TYPED(splat_prototypes, /datum/splat, init_splat_prototypes()) splat_list[splat_path::id] = splat_path return splat_list -/proc/init_splat_prototypes() - var/list/splat_list = list() - for(var/splat_type in valid_subtypesof(/datum/splat)) - splat_list[splat_type] = new splat_type() - return splat_list - /// An assoc list of species types to their features (from get_features()) GLOBAL_LIST_EMPTY(features_by_splats) + + +/// Creates an assoc list indexed by the name of the subsplat +/proc/init_subsplat_list(path = /datum/subsplat) + var/list/subsplat_list = list() + for (var/datum/subsplat/subsplat as anything in valid_subtypesof(path)) + subsplat_list[subsplat::name] = subsplat + //subsplat_list = sort_list(subsplat_list) + return subsplat_list diff --git a/code/_globalvars/lists/girder_wall_recipes.dm b/code/_globalvars/lists/girder_wall_recipes.dm new file mode 100644 index 000000000000..d4a015c6a4e4 --- /dev/null +++ b/code/_globalvars/lists/girder_wall_recipes.dm @@ -0,0 +1,121 @@ +GLOBAL_LIST_INIT(main_girder_wall_recipes, init_main_girder_wall_recipes()) +GLOBAL_LIST_INIT(material_girder_wall_recipes, init_material_girder_wall_recipes()) + +/// Initializes the main, false and tram wall types using three lists of wall types. +/proc/init_main_girder_wall_recipes() + return create_girder_wall_recipes( + wall_types = list( + /turf/closed/wall, + /turf/closed/wall/metal_foam_base, + /turf/closed/wall/r_wall, + /turf/closed/wall/r_wall/plastitanium, + /turf/closed/wall/mineral/gold, + /turf/closed/wall/mineral/silver, + /turf/closed/wall/mineral/diamond, + /turf/closed/wall/mineral/bananium, + /turf/closed/wall/mineral/sandstone, + /turf/closed/wall/mineral/uranium, + /turf/closed/wall/mineral/plasma, + /turf/closed/wall/mineral/wood, + /turf/closed/wall/mineral/wood/nonmetal, + /turf/closed/wall/mineral/bamboo, + /turf/closed/wall/mineral/iron, + /turf/closed/wall/mineral/snow, + /turf/closed/wall/mineral/abductor, + /turf/closed/wall/mineral/titanium, + /turf/closed/wall/mineral/plastitanium, + /turf/closed/wall/mineral/cult, + /turf/closed/wall/mineral/bronze, + /turf/closed/wall/material, + ), + falsewall_types = list( + /obj/structure/falsewall, + /obj/structure/falsewall/reinforced, + /obj/structure/falsewall/gold, + /obj/structure/falsewall/silver, + /obj/structure/falsewall/diamond, + /obj/structure/falsewall/bananium, + /obj/structure/falsewall/sandstone, + /obj/structure/falsewall/uranium, + /obj/structure/falsewall/plasma, + /obj/structure/falsewall/wood, + /obj/structure/falsewall/bamboo, + /obj/structure/falsewall/iron, + /obj/structure/falsewall/abductor, + /obj/structure/falsewall/titanium, + /obj/structure/falsewall/plastitanium, + /obj/structure/falsewall/material, + ), + tram_types = list( + /obj/structure/tram, + /obj/structure/tram/alt/gold, + /obj/structure/tram/alt/silver, + /obj/structure/tram/alt/diamond, + /obj/structure/tram/alt/bananium, + /obj/structure/tram/alt/sandstone, + /obj/structure/tram/alt/uranium, + /obj/structure/tram/alt/plasma, + /obj/structure/tram/alt/wood, + /obj/structure/tram/alt/bamboo, + /obj/structure/tram/alt/iron, + /obj/structure/tram/alt/abductor, + /obj/structure/tram/alt/titanium, + /obj/structure/tram/alt/plastitanium, + ) + ) + +/// Initializes the normal and false girder material wall recipes using two lists of material wall types. +/proc/init_material_girder_wall_recipes() + return create_girder_wall_recipes( + wall_types = list(/turf/closed/wall/material), + falsewall_types = list(/obj/structure/falsewall/material) + ) + +/// Returns a list of newly created [/datum/girder_wall_recipe] instances from the given wall type lists. +/proc/create_girder_wall_recipes(list/wall_types, list/falsewall_types, list/tram_types) + var/list/recipes = list() + + for (var/turf/closed/wall/wall_type as anything in wall_types) + add_girder_wall_recipe( + recipes = recipes, + wall_type = wall_type, + stack_type = wall_type::sheet_type, + stack_amount = wall_type::sheet_amount, + girder_type = wall_type::girder_type, + girder_state = wall_type::girder_state, + make_delay = wall_type::make_delay, + start_alert = "adding plating..." + ) + + for (var/obj/structure/falsewall/falsewall_type as anything in falsewall_types) + add_girder_wall_recipe( + recipes = recipes, + wall_type = falsewall_type, + stack_type = falsewall_type::mineral, + stack_amount = falsewall_type::mineral_amount, + girder_type = falsewall_type::girder_type, + girder_state = GIRDER_DISPLACED, + make_delay = 2 SECONDS, + start_alert = "concealing entrance..." + ) + + for (var/obj/structure/tram/tram_type as anything in tram_types) + add_girder_wall_recipe( + recipes = recipes, + wall_type = tram_type, + stack_type = tram_type::mineral, + stack_amount = tram_type::mineral_amount, + girder_type = tram_type::girder_type, + girder_state = GIRDER_TRAM, + make_delay = 4 SECONDS, + start_alert = "adding plating..." + ) + + return recipes + +/// Adds a new [/datum/girder_wall_recipe] instance to the given recipe list from the given recipe arguments. +/proc/add_girder_wall_recipe(list/recipes, wall_type, stack_type, stack_amount, girder_type, girder_state, make_delay, start_alert) + if (!ispath(stack_type, /obj/item/stack)) + CRASH("Attempted to create a girder wall recipe for wall type ([wall_type]) with an invalid stack type ([stack_type])") + + recipes += new /datum/girder_wall_recipe(wall_type, stack_type, stack_amount, girder_type, girder_state, make_delay, start_alert) diff --git a/code/_globalvars/lists/keybindings.dm b/code/_globalvars/lists/keybindings.dm index 882b171ea263..90a7501f5e45 100644 --- a/code/_globalvars/lists/keybindings.dm +++ b/code/_globalvars/lists/keybindings.dm @@ -20,7 +20,7 @@ LAZYADD(GLOB.default_hotkeys[instance.name], list(bound_key)) /proc/init_emote_keybinds() - for(var/i in subtypesof(/datum/emote)) + for(var/i in valid_subtypesof(/datum/emote)) var/datum/emote/faketype = i if(!initial(faketype.key)) continue diff --git a/code/_globalvars/lists/mapping.dm b/code/_globalvars/lists/mapping.dm index 116fb3adbea0..0f1eebb33473 100644 --- a/code/_globalvars/lists/mapping.dm +++ b/code/_globalvars/lists/mapping.dm @@ -92,6 +92,16 @@ GLOBAL_LIST_INIT(alldirs, list( SOUTHEAST, SOUTHWEST, )) +GLOBAL_LIST_INIT(alldirs_dmi_order, list( + SOUTH, + NORTH, + EAST, + WEST, + SOUTHEAST, + SOUTHWEST, + NORTHEAST, + NORTHWEST, +)) GLOBAL_LIST_INIT(cardinal_angles, list( "[NORTH]" = 0, diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 492306d497a1..c87e9e53efde 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(construct_radial_images, list( /proc/init_emote_list() . = list() - for(var/path in subtypesof(/datum/emote)) + for(var/path in valid_subtypesof(/datum/emote)) var/datum/emote/E = new path() if(E.key) if(!.[E.key]) diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index b21d6520d0fe..8bf5fd8cb257 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -183,6 +183,9 @@ GLOBAL_LIST_INIT(phobia_objs, list( /obj/item/food/sashimi, /obj/item/highfrequencyblade, /obj/item/katana, + /obj/item/storage/belt/sheath/katana, + /obj/item/storage/belt/sheath/ninja, + /obj/item/storage/belt/sheath/hanzo_katana, /obj/item/nullrod/claymore/katana, /obj/item/nullrod/vibro, /obj/item/reagent_containers/cup/glass/bottle/sake, diff --git a/code/_globalvars/time_vars.dm b/code/_globalvars/time_vars.dm index 29526f845b29..fd9b925e0d4f 100644 --- a/code/_globalvars/time_vars.dm +++ b/code/_globalvars/time_vars.dm @@ -2,5 +2,5 @@ /// The difference betwen midnight (of the host computer) and 0 world.ticks. GLOBAL_VAR_INIT(timezoneOffset, 0) -GLOBAL_VAR_INIT(year, time2text(world.realtime, "YYYY", NO_TIMEZONE)) +GLOBAL_VAR_INIT(year, UTC_YEAR) GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 9b48e2d3a60a..04f7eed6098b 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -141,7 +141,8 @@ GLOBAL_LIST_INIT(traits_by_type, list( "STATION_TRAIT_INFESTATION" = STATION_TRAIT_INFESTATION, "STATION_TRAIT_PEST_CONTROL" = STATION_TRAIT_PEST_CONTROL, "STATION_TRAIT_STRAY_MIGRATION" = STATION_TRAIT_STRAY_MIGRATION, - "STATION_TRAIT_COSPLAY_CONVENTION" = STATION_TRAIT_COSPLAY_CONVENTION // DARKPACK EDIT ADD END + "STATION_TRAIT_COSPLAY_CONVENTION" = STATION_TRAIT_COSPLAY_CONVENTION, // DARKPACK EDIT ADD END + "STATION_TRAIT_SPAWN_WEAKPOINTS" = STATION_TRAIT_SPAWN_WEAKPOINTS, ), /datum/deathmatch_lobby = list( "TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS" = TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS, @@ -262,7 +263,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_DEATHCOMA" = TRAIT_DEATHCOMA, "TRAIT_DEFIB_BLACKLISTED" = TRAIT_DEFIB_BLACKLISTED, "TRAIT_DEFICIENT_VITAE" = TRAIT_DEFICIENT_VITAE, // DARKPACK EDIT ADD - "TRAIT_DESENSITIZED" = TRAIT_DESENSITIZED, "TRAIT_DESIGNATED_TARGET" = TRAIT_DESIGNATED_TARGET, "TRAIT_DETECTIVES_TASTE" = TRAIT_DETECTIVES_TASTE, "TRAIT_DETECT_STORM" = TRAIT_DETECT_STORM, @@ -273,7 +273,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_DISCO_DANCER" = TRAIT_DISCO_DANCER, "TRAIT_DISEASELIKE_SEVERITY_HIGH" = TRAIT_DISEASELIKE_SEVERITY_HIGH, "TRAIT_DISEASELIKE_SEVERITY_MEDIUM" = TRAIT_DISEASELIKE_SEVERITY_MEDIUM, - "TRAIT_DISFIGURED" = TRAIT_DISFIGURED, "TRAIT_DISGUISED" = TRAIT_DISGUISED, "TRAIT_DISK_VERIFIER" = TRAIT_DISK_VERIFIER, "TRAIT_DISPLAY_JOB_IN_BINARY" = TRAIT_DISPLAY_JOB_IN_BINARY, @@ -522,6 +521,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_PERCEPTUAL_TRAUMA_BYPASS" = TRAIT_PERCEPTUAL_TRAUMA_BYPASS, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, "TRAIT_PERMANENTLY_MORTAL" = TRAIT_PERMANENTLY_MORTAL, + "TRAIT_PERMAFANGS" = TRAIT_PERMAFANGS, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_PLANT_SAFE" = TRAIT_PLANT_SAFE, @@ -677,6 +677,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_VORACIOUS" = TRAIT_VORACIOUS, "TRAIT_VTM_CLANS" = TRAIT_VTM_CLANS, // DARKPACK EDIT ADD "TRAIT_VTM_MORALITY" = TRAIT_VTM_MORALITY, // DARKPACK EDIT ADD + "TRAIT_WTA_GAROU_AUSPICE" = TRAIT_WTA_GAROU_AUSPICE, // DARKPACK EDIT ADD + "TRAIT_WTA_GAROU_BREED" = TRAIT_WTA_GAROU_BREED, // DARKPACK EDIT ADD + "TRAIT_WTA_GAROU_TRIBE" = TRAIT_WTA_GAROU_TRIBE, // DARKPACK EDIT ADD "TRAIT_WAS_EVOLVED" = TRAIT_WAS_EVOLVED, "TRAIT_WATER_ADAPTATION" = TRAIT_WATER_ADAPTATION, "TRAIT_WATER_HATER" = TRAIT_WATER_HATER, @@ -688,6 +691,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_WING_BUFFET" = TRAIT_WING_BUFFET, "TRAIT_WING_BUFFET_TIRED" = TRAIT_WING_BUFFET_TIRED, "TRAIT_WOUND_LICKER" = TRAIT_WOUND_LICKER, + "TRAIT_WYRMTAINTED" = TRAIT_WYRMTAINTED, // DARKPACK EDIT ADD "TRAIT_XENO_HOST" = TRAIT_XENO_HOST, "TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE, "TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING, @@ -710,21 +714,27 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SPACE_ANT_IMMUNITY" = TRAIT_SPACE_ANT_IMMUNITY, "TRAIT_BRAIN_TRAUMA_IMMUNITY" = TRAIT_BRAIN_TRAUMA_IMMUNITY, "TRAIT_BLUSH_OF_HEALTH" = TRAIT_BLUSH_OF_HEALTH, // DARKPACK EDIT ADD - "TRAIT_GHOST_VISION" = TRAIT_GHOST_VISION, // DARKPACK EDIT ADD - Necromancy + "TRAIT_GHOST_VISION" = TRAIT_GHOST_VISION, // DARKPACK EDIT ADD - POWERS - (Necromancy) "TRAIT_COLD_AURA" = TRAIT_COLD_AURA, // DARKPACK EDIT ADD + "TRAIT_WARM_AURA" = TRAIT_WARM_AURA, // DARKPACK EDIT ADD "TRAIT_FRENETIC_AURA" = TRAIT_FRENETIC_AURA, // DARKPACK EDIT ADD "TRAIT_THAUMATURGY_KNOWLEDGE" = TRAIT_THAUMATURGY_KNOWLEDGE, // DARKPACK EDIT ADD "TRAIT_VICISSITUDE_KNOWLEDGE" = TRAIT_VICISSITUDE_KNOWLEDGE, // DARKPACK EDIT ADD "TRAIT_MYSTICISM_KNOWLEDGE" = TRAIT_MYSTICISM_KNOWLEDGE, // DARKPACK EDIT ADD "TRAIT_NECROMANCY_KNOWLEDGE" = TRAIT_NECROMANCY_KNOWLEDGE, // DARKPACK EDIT ADD - "TRAIT_WARM_AURA" = TRAIT_WARM_AURA, // DARKPACK EDIT ADD "TRAIT_PASSDOOR" = TRAIT_PASSDOOR, // DARKPACK EDIT ADD "TRAIT_TIMEWARPER" = TRAIT_TIMEWARPER, // DARKPACK EDIT ADD "TRAIT_SERPENTIS_SKIN" = TRAIT_SERPENTIS_SKIN, // DARKPACK EDIT ADD - POWERS - (Serpentis) "TRAIT_NEEDS_BLOOD" = TRAIT_NEEDS_BLOOD, //DARKPACK EDIT ADD - Hunger and Frenzy - "TRAIT_SILENCED" = TRAIT_SILENCED, // DARKPACK EDIT ADD - Quietus - "TRAIT_WEAK_TO_DOMINATE" = TRAIT_WEAK_TO_DOMINATE, // DARKPACK EDIT ADD - Dominate + "TRAIT_SILENCED" = TRAIT_SILENCED, // DARKPACK EDIT ADD - POWERS - (Quietus) + "TRAIT_WEAK_TO_DOMINATE" = TRAIT_WEAK_TO_DOMINATE, // DARKPACK EDIT ADD - POWERS - (Dominate) "TRAIT_ILLEGAL_IDENTITY" = TRAIT_ILLEGAL_IDENTITY, // DARKPACK EDIT ADD - GOVERMENT + "TRAIT_NO_LYING_ANGLE" = TRAIT_NO_LYING_ANGLE, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_TRANSFORM_UPDATES_ICON" = TRAIT_TRANSFORM_UPDATES_ICON, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_LOUD_HOWLER" = TRAIT_LOUD_HOWLER, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_FERA_FUR" = TRAIT_FERA_FUR, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_SMALL_HANDS" = TRAIT_SMALL_HANDS, // DARKPACK EDIT ADD - WEREWOLF + "TRAIT_NECROPOLIS_WORSHIP" = TRAIT_NECROPOLIS_WORSHIP, ), /mob/living/carbon = list( "TRAIT_BRAINLESS_CARBON" = TRAIT_BRAINLESS_CARBON, @@ -784,13 +794,12 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_DISABLED_BY_WOUND" = TRAIT_DISABLED_BY_WOUND, "TRAIT_IGNORED_BY_LIVING_FLESH" = TRAIT_IGNORED_BY_LIVING_FLESH, "TRAIT_IMMUNE_TO_CRANIAL_FISSURE" = TRAIT_IMMUNE_TO_CRANIAL_FISSURE, - ), - /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS, - ), - /obj/item/bodypart = list( "TRAIT_EASY_ATTACH" = TRAIT_EASY_ATTACH, ), + /obj/item/bodypart/head = list( + "TRAIT_DISFIGURED" = TRAIT_DISFIGURED, + ), /obj/item/card/id = list( "TRAIT_JOB_FIRST_ID_CARD" = TRAIT_JOB_FIRST_ID_CARD, "TRAIT_MAGNETIC_ID_CARD" = TRAIT_MAGNETIC_ID_CARD, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index 06af5ab71856..a5e9ce27bdd6 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -89,11 +89,9 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_DEAF" = TRAIT_DEAF, "TRAIT_DEATHCOMA" = TRAIT_DEATHCOMA, "TRAIT_DEFIB_BLACKLISTED" = TRAIT_DEFIB_BLACKLISTED, - "TRAIT_DESENSITIZED" = TRAIT_DESENSITIZED, "TRAIT_DETECT_STORM" = TRAIT_DETECT_STORM, "TRAIT_DIAGNOSTIC_HUD" = TRAIT_DIAGNOSTIC_HUD, "TRAIT_DISCOORDINATED_TOOL_USER" = TRAIT_DISCOORDINATED_TOOL_USER, - "TRAIT_DISFIGURED" = TRAIT_DISFIGURED, "TRAIT_DISK_VERIFIER" = TRAIT_DISK_VERIFIER, "TRAIT_DISSECTED" = TRAIT_DISSECTED, "TRAIT_DRINKS_BLOOD" = TRAIT_DRINKS_BLOOD, @@ -375,6 +373,9 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( /obj/item/bodypart = list( "TRAIT_PARALYSIS" = TRAIT_PARALYSIS, ), + /obj/item/bodypart/head = list( + "TRAIT_DISFIGURED" = TRAIT_DISFIGURED, + ), /obj/item/card/id = list( "TRAIT_MAGNETIC_ID_CARD" = TRAIT_MAGNETIC_ID_CARD, ), diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 3ff5530952ad..95a32d09db23 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -249,6 +249,13 @@ GLOBAL_LIST_INIT(available_ui_styles, list( healthdoll = null bloodpool_icon = null // DARKPACK EDIT ADD zone_icon = null // DARKPACK EDIT ADD + // DARKPACK EDIT ADD START - WEREWOLF + auspice_icon = null + rage_and_gnosis_icon = null + homid_trans_icon = null + war_trans_icon = null + feral_trans_icon = null + // DARKPACK EDIT ADD END spacesuit = null hunger = null alien_plasma_display = null diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 7a8e6bc1cfd9..3a34be48f1f5 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -273,11 +273,15 @@ infodisplay += healthdoll // DARKPACK EDIT ADD START - bloodpool_icon = new /atom/movable/screen/bloodpool(null, src) + bloodpool_icon = new /atom/movable/screen/bloodpool(null, src) infodisplay += bloodpool_icon zone_icon = new /atom/movable/screen/zone_hud(null, src) infodisplay += zone_icon + + if(owner.splats) + for(var/datum/splat/splat in owner.splats) + splat.add_relevent_huds(src) // DARKPACK EDIT ADD END stamina = new /atom/movable/screen/stamina(null, src) diff --git a/code/_onclick/hud/living.dm b/code/_onclick/hud/living.dm index 93335c11b507..97fe1f7a7ca3 100644 --- a/code/_onclick/hud/living.dm +++ b/code/_onclick/hud/living.dm @@ -27,9 +27,13 @@ infodisplay += stamina // DARKPACK EDIT ADD START - bloodpool_icon = new /atom/movable/screen/bloodpool(null, src) + bloodpool_icon = new /atom/movable/screen/bloodpool(null, src) infodisplay += bloodpool_icon zone_icon = new /atom/movable/screen/zone_hud(null, src) infodisplay += zone_icon + + if(owner.splats) + for(var/datum/splat/splat in owner.splats) + splat.add_relevent_huds(src) // DARKPACK EDIT ADD END diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 1cd89773c55a..9098dfc1a3a1 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -67,8 +67,10 @@ return attack_target.attack_paw(src, modifiers) /mob/living/carbon/human/resolve_unarmed_attack(atom/attack_target, list/modifiers) + /* // DARKPACK EDIT REMOVAL - Dont assume we are a chimp because we cant use tools. if(!ISADVANCEDTOOLUSER(src)) return ..() + */ return attack_target.attack_hand(src, modifiers) diff --git a/code/controllers/subsystem/addiction.dm b/code/controllers/subsystem/addiction.dm index 13719edaad88..370c23ac2403 100644 --- a/code/controllers/subsystem/addiction.dm +++ b/code/controllers/subsystem/addiction.dm @@ -6,7 +6,7 @@ SUBSYSTEM_DEF(addiction) name = "Addiction" flags = SS_NO_FIRE ///Dictionary of addiction.type || addiction ref - var/list/all_addictions = list() + var/list/datum/addiction/all_addictions = list() /datum/controller/subsystem/addiction/Initialize() InitializeAddictions() diff --git a/code/controllers/subsystem/ambience.dm b/code/controllers/subsystem/ambience.dm index f49604b058a5..88db3ffcc332 100644 --- a/code/controllers/subsystem/ambience.dm +++ b/code/controllers/subsystem/ambience.dm @@ -27,7 +27,7 @@ SUBSYSTEM_DEF(ambience) client_old_areas -= client_iterator continue - if(!client_mob.can_hear()) //WHAT? I CAN'T HEAR YOU + if(HAS_TRAIT(client_mob, TRAIT_DEAF)) //WHAT? I CAN'T HEAR YOU continue //Check to see if the client-mob is in a valid area @@ -126,7 +126,7 @@ SUBSYSTEM_DEF(ambience) client.current_ambient_sound = null return - if(!can_hear()) // Can the mob hear? + if(HAS_TRAIT(src, TRAIT_DEAF)) // Can the mob hear? SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = CHANNEL_BUZZ)) client.current_ambient_sound = null return diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index ed8f499beac3..fd374b5704ac 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -15,6 +15,7 @@ SUBSYSTEM_DEF(blackbox) "round_end_stats" = 2, "testmerged_prs" = 2, "dynamic_threat" = 2, + "bitrunning_domain_loaded" = 2, ) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this /datum/controller/subsystem/blackbox/Initialize() diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm index ce6ced5b01e7..9a765423eaa7 100644 --- a/code/controllers/subsystem/economy.dm +++ b/code/controllers/subsystem/economy.dm @@ -14,11 +14,6 @@ SUBSYSTEM_DEF(economy) ACCOUNT_CAR = ACCOUNT_CAR_NAME, ACCOUNT_SEC = ACCOUNT_SEC_NAME) var/list/departmental_accounts = list() - /** - * Enables extra money charges for things that normally would be free, such as sleepers/cryo/beepsky. - * Take care when enabling, as players will NOT respond well if the economy is set up for low cash flows. - */ - var/full_ancap = FALSE /// Departmental cash provided to science when a node is researched in specific configs. var/techweb_bounty = 250 @@ -126,7 +121,7 @@ SUBSYSTEM_DEF(economy) /** * Handy proc for obtaining a department's bank account, given the department ID, AKA the define assigned for what department they're under. */ -/datum/controller/subsystem/economy/proc/get_dep_account(dep_id) +/datum/controller/subsystem/economy/proc/get_dep_account(dep_id) as /datum/bank_account/department for(var/datum/bank_account/department/D in departmental_accounts) if(D.department_id == dep_id) return D diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm index 4ca6932cb3c5..c01fe4f718fd 100644 --- a/code/controllers/subsystem/explosions.dm +++ b/code/controllers/subsystem/explosions.dm @@ -383,13 +383,11 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec shake_the_room(epicenter, orig_max_distance, far_dist, devastation_range, heavy_impact_range) if(heavy_impact_range > 1) - var/datum/effect_system/explosion/E - if(smoke) - E = new /datum/effect_system/explosion/smoke - else - E = new - E.set_up(epicenter) - E.start() + var/effect_type = /datum/effect_system/explosion + if (smoke) + effect_type = /datum/effect_system/explosion/smoke + var/datum/effect_system/explosion/system = new effect_type(epicenter) + system.start() //flash mobs if(flash_range) diff --git a/code/controllers/subsystem/market.dm b/code/controllers/subsystem/market.dm index 283a147d5ae9..a56ad90b1fd6 100644 --- a/code/controllers/subsystem/market.dm +++ b/code/controllers/subsystem/market.dm @@ -116,10 +116,7 @@ SUBSYSTEM_DEF(market) return var/atom/movable/thing = purchase.entry.spawn_item(target, purchase) purchase.post_purchase_effects(thing) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(5, 1, target) - sparks.attach(thing) - sparks.start() + do_sparks(5, TRUE, target) qdel(purchase) /// Used to add /datum/market_purchase to queued_purchases var. Returns TRUE when queued. diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm index e0b2bcc73378..fa81990ef182 100644 --- a/code/controllers/subsystem/materials.dm +++ b/code/controllers/subsystem/materials.dm @@ -23,7 +23,7 @@ SUBSYSTEM_DEF(materials) var/list/base_stack_recipes = list( new /datum/stack_recipe("Chair", /obj/structure/chair/greyscale, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, category = CAT_FURNITURE), new /datum/stack_recipe("Toilet", /obj/structure/toilet/greyscale, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, category = CAT_FURNITURE), - new /datum/stack_recipe("Sink Frame", /obj/structure/sinkframe, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, category = CAT_FURNITURE), + new /datum/stack_recipe("Sink Frame", /obj/item/wallframe/sinkframe, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, category = CAT_FURNITURE), new /datum/stack_recipe("Material floor tile", /obj/item/stack/tile/material, 1, 4, 20, crafting_flags = CRAFT_SKIP_MATERIALS_PARITY, category = CAT_TILES), /* new /datum/stack_recipe("Material airlock assembly", /obj/structure/door_assembly/door_assembly_material, 4, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, category = CAT_DOORS), */ // DARKPACK EDIT REMOVE new /datum/stack_recipe("Material platform", /obj/structure/platform/material, 2, time = 3 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_SKIP_MATERIALS_PARITY, trait_booster = TRAIT_QUICK_BUILD, trait_modifier = 0.75, category = CAT_STRUCTURE), \ diff --git a/code/controllers/subsystem/minor_mapping.dm b/code/controllers/subsystem/minor_mapping.dm index d0e741109cf3..aca5845a14dd 100644 --- a/code/controllers/subsystem/minor_mapping.dm +++ b/code/controllers/subsystem/minor_mapping.dm @@ -23,6 +23,12 @@ SUBSYSTEM_DEF(minor_mapping) #else trigger_migration(CONFIG_GET(number/mice_roundstart)) place_satchels(satchel_amount = 2) + var/weakpoint_spawns = 3 + if(HAS_TRAIT(SSstation, STATION_TRAIT_SPAWN_WEAKPOINTS)) + weakpoint_spawns = rand(4,8) + + weakpoint_spawns += SSmapping.current_map.bonus_weakpoints //This will add 0 by default, or additional on large maps where it's included in the config. + place_weakpoints(weakpoint_spawns) return SS_INIT_SUCCESS #endif @@ -97,4 +103,31 @@ SUBSYSTEM_DEF(minor_mapping) return shuffle(suitable) +/// This behaves nearly the same as spawning underfloot satchels, but instead spawns weakpoints. +/datum/controller/subsystem/minor_mapping/proc/place_weakpoints(weakpoint_amount) + if(weakpoint_amount == 0) + return + var/list/turfs = find_satchel_suitable_turfs() + ///List of areas where weakpoints should not be placed. + var/list/blacklisted_area_types = get_blacklist_areas() + /area/station/maintenance + while(turfs.len && weakpoint_amount > 0) + var/turf/turf = pick_n_take(turfs) + if(is_type_in_list(get_area(turf), blacklisted_area_types)) + continue + var/obj/effect/weakpoint/new_point = new(turf) + SEND_SIGNAL(new_point, COMSIG_OBJ_HIDE, turf.underfloor_accessibility) + weakpoint_amount-- + +/** + * Areas for minor_mapping procs to avoid. Mutate or adjust based on use case. + */ +/datum/controller/subsystem/minor_mapping/proc/get_blacklist_areas() + var/list/blacklist_areas = list( + /area/station/holodeck, + /area/space/nearstation, + /area/station/solars, + ) + return blacklist_areas + + #undef PROB_SPIDER_REPLACEMENT diff --git a/code/controllers/subsystem/networks/bitrunning.dm b/code/controllers/subsystem/networks/bitrunning.dm index 51b7f329a4a6..6f6d68a0b05c 100644 --- a/code/controllers/subsystem/networks/bitrunning.dm +++ b/code/controllers/subsystem/networks/bitrunning.dm @@ -21,8 +21,8 @@ SUBSYSTEM_DEF(bitrunning) for(var/datum/lazy_template/virtual_domain/domain as anything in all_domains) if(domain.domain_flags & DOMAIN_TEST_ONLY) continue - var/can_view = domain.difficulty < scanner_tier && domain.cost <= points + 5 - var/can_view_reward = domain.difficulty < (scanner_tier + 1) && domain.cost <= points + 3 + var/can_view = domain.can_view_name(scanner_tier, points) + var/can_view_reward = domain.can_view_reward(scanner_tier, points) UNTYPED_LIST_ADD(levels, list( "announce_ghosts" = domain.announce_to_ghosts, diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm index fbfaa99f5f85..99c725a7d609 100644 --- a/code/controllers/subsystem/processing/quirks.dm +++ b/code/controllers/subsystem/processing/quirks.dm @@ -68,14 +68,11 @@ PROCESSING_SUBSYSTEM_DEF(quirks) /datum/controller/subsystem/processing/quirks/proc/SetupQuirks() // Sort by Positive, Negative, Neutral; and then by name - var/list/quirk_list = sort_list(subtypesof(/datum/quirk), GLOBAL_PROC_REF(cmp_quirk_asc)) + var/list/quirk_list = sort_list(subtypesof(/datum/quirk/darkpack), GLOBAL_PROC_REF(cmp_quirk_asc)) // DARKPACK EDIT CHANGE - Original : var/list/quirk_list = sort_list(subtypesof(/datum/quirk), GLOBAL_PROC_REF(cmp_quirk_asc)) for(var/type in quirk_list) var/datum/quirk/quirk_type = type - if(initial(quirk_type.abstract_parent_type) == type) - continue - quirk_prototypes[type] = new type quirks[initial(quirk_type.name)] = quirk_type quirk_points[initial(quirk_type.name)] = initial(quirk_type.value) diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index d87aa0f1dd1d..ea8b02e81296 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(tgui) ntos_error = "" basehtml = replacetextEx(basehtml, "", ntos_error) - basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2525-[CURRENT_STATION_YEAR]") + basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2525-[text2num(UTC_YEAR) + STATION_YEAR_OFFSET]") // This can't use the GLOB as it runs before those are populated /datum/controller/subsystem/tgui/OnConfigLoad() var/storage_iframe = CONFIG_GET(string/storage_cdn_iframe) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 70bb8426ad4f..84a89b9d8243 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -200,6 +200,7 @@ SUBSYSTEM_DEF(ticker) if(!roundend_check_paused && (check_finished() || force_ending)) current_state = GAME_STATE_FINISHED + GLOB.canon_event = FALSE // We generally consider all events in postgame to be non-canon due to most servers having EORG // DARKPACK EDIT ADD toggle_ooc(TRUE) // Turn it on toggle_dooc(TRUE) declare_completion(force_ending) @@ -219,6 +220,10 @@ SUBSYSTEM_DEF(ticker) if(SScity_time.roundend_started) return TRUE // DARKPACK EDIT ADD END + // DARKPACK EDIT ADD START - MASQUERADE + if(SSmasquerade.roundend_started) + return TRUE + // DARKPACK EDIT ADD END return FALSE /// Gets a list of players with their readied state so we can post it as a log @@ -330,7 +335,7 @@ SUBSYSTEM_DEF(ticker) var/arguments = list() if(GLOB.revdata.originmastercommit) to_set += "commit_hash = :commit_hash" - arguments["commit_hash"] = GLOB.revdata.originmastercommit + arguments["commit_hash"] = GLOB.revdata.GetDatabaseCommitSha() if(to_set.len) arguments["round_id"] = GLOB.round_id var/datum/db_query/query_round_game_mode = SSdbcore.NewQuery( diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm index 224941e6ce3d..a15977e7e53f 100644 --- a/code/datums/achievements/misc_achievements.dm +++ b/code/datums/achievements/misc_achievements.dm @@ -250,3 +250,10 @@ desc = "Nutritionists often recommend a balanced and varied diet. However that clearly isn't the case for some creatures." database_id = MEDAL_SHARKDRAGON icon_state = "dragon_plus_fish" + +/datum/award/achievement/misc/desensitized + name = "In Flanders Fields" + desc = "You have witnessed more death and despair in one shift than most TerraGov marines have seen across an entire tour of duty. \ + Perhaps one day you'll find peace. But not today." + database_id = MEDAL_DESENSITIZED + icon_state = "desensitized" diff --git a/code/datums/bodypart_overlays/emote_bodypart_overlay.dm b/code/datums/bodypart_overlays/emote_bodypart_overlay.dm index fbdea6018395..78cb9752fa3b 100644 --- a/code/datums/bodypart_overlays/emote_bodypart_overlay.dm +++ b/code/datums/bodypart_overlays/emote_bodypart_overlay.dm @@ -63,6 +63,19 @@ offset_key = OFFSET_FACE attached_body_zone = BODY_ZONE_HEAD +/datum/bodypart_overlay/simple/emote/blush/color_image(image/overlay, layer, obj/item/bodypart/limb) + var/base_color = limb.owner?.get_bloodtype()?.get_damage_color() + if(!base_color) + return ..() + + var/list/blood_hsl = rgb2num(base_color, COLORSPACE_HSL) + // take blood color then just make it a lot brighter and desaturate it a bit + blood_hsl[2] = max(0, blood_hsl[2] - 20) + blood_hsl[3] = min(100, blood_hsl[3] + 30) + + overlay.color = rgb(blood_hsl[1], blood_hsl[2], blood_hsl[3], space = COLORSPACE_HSL) + overlay.alpha = 200 + /datum/bodypart_overlay/simple/emote/cry icon_state = "tears" draw_color = COLOR_DARK_CYAN diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm index 8a2e13ed9e56..8cc32018531b 100644 --- a/code/datums/brain_damage/creepy_trauma.dm +++ b/code/datums/brain_damage/creepy_trauma.dm @@ -37,7 +37,7 @@ antagonist.greet() log_game("[key_name(antagonist)] has developed an obsession with [key_name(obsession)].") RegisterSignal(owner, COMSIG_CARBON_HELPED, PROC_REF(on_hug)) - ADD_TRAIT(owner, TRAIT_DESENSITIZED, REF(src)) + owner.apply_status_effect(/datum/status_effect/desensitized, REF(src), DESENSITIZED_THRESHOLD) /datum/brain_trauma/special/obsessed/on_life(seconds_per_tick) if(!obsession || obsession.stat == DEAD) @@ -75,7 +75,7 @@ if(obsession) log_game("[key_name(owner)] is no longer obsessed with [key_name(obsession)].") UnregisterSignal(obsession, COMSIG_MOB_EYECONTACT) - REMOVE_TRAIT(owner, TRAIT_DESENSITIZED, REF(src)) + owner.remove_status_effect(/datum/status_effect/desensitized, REF(src)) /datum/brain_trauma/special/obsessed/handle_speech(datum/source, list/speech_args) if(!viewing) diff --git a/code/datums/brain_damage/hypnosis.dm b/code/datums/brain_damage/hypnosis.dm index 05c98b0ec30e..d789a7d142cb 100644 --- a/code/datums/brain_damage/hypnosis.dm +++ b/code/datums/brain_damage/hypnosis.dm @@ -76,6 +76,6 @@ ) /datum/brain_trauma/hypnosis/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return hearing_args[HEARING_RAW_MESSAGE] = target_phrase.Replace(hearing_args[HEARING_RAW_MESSAGE], span_hypnophrase("$1")) diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm index 3db89c038807..993ddea4ebb1 100644 --- a/code/datums/brain_damage/mild.dm +++ b/code/datums/brain_damage/mild.dm @@ -256,7 +256,7 @@ var/list/speak_dejavu = list() /datum/brain_trauma/mild/mind_echo/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return if(hear_dejavu.len >= 5) diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm index 522bc2479c60..3e6f0e6691bd 100644 --- a/code/datums/brain_damage/severe.dm +++ b/code/datums/brain_damage/severe.dm @@ -294,7 +294,7 @@ owner.remove_status_effect(/datum/status_effect/trance) /datum/brain_trauma/severe/hypnotic_trigger/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return var/regex/reg = new("(\\b[REGEX_QUOTE(trigger_phrase)]\\b)","ig") diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm index 8928f36409a4..e4e6847011f9 100644 --- a/code/datums/brain_damage/special.dm +++ b/code/datums/brain_damage/special.dm @@ -483,15 +483,15 @@ /datum/brain_trauma/special/ptsd/on_gain() owner.add_mood_event("combat_ptsd", /datum/mood_event/desentized) owner.mob_mood?.mood_modifier -= 1 //Basically nothing can change your mood - owner.mob_mood?.sanity_level = SANITY_DISTURBED //Makes sanity on a unstable level unless cured - ADD_TRAIT(owner, TRAIT_DESENSITIZED, REF(src)) + owner.mob_mood?.set_sanity(SANITY_DISTURBED, override = TRUE) //Makes sanity on a unstable level unless cured + owner.apply_status_effect(/datum/status_effect/desensitized, REF(src), DESENSITIZED_THRESHOLD) . = ..() /datum/brain_trauma/special/ptsd/on_lose() owner.clear_mood_event("combat_ptsd") owner.mob_mood?.mood_modifier += 1 - owner.mob_mood?.sanity_level = SANITY_GREAT - REMOVE_TRAIT(owner, TRAIT_DESENSITIZED, REF(src)) + owner.mob_mood?.set_sanity(SANITY_GREAT, override = TRUE) + owner.remove_status_effect(/datum/status_effect/desensitized, REF(src)) return ..() /datum/brain_trauma/special/primal_instincts diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index 57c94e60b1d9..3cedb77dcdb4 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -235,7 +235,7 @@ return //no random switching /datum/brain_trauma/severe/split_personality/brainwashing/handle_hearing(datum/source, list/hearing_args) - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return var/message = hearing_args[HEARING_RAW_MESSAGE] diff --git a/code/datums/components/atom_mounted.dm b/code/datums/components/atom_mounted.dm index 1b58f2ec09ff..2d2cea36abff 100644 --- a/code/datums/components/atom_mounted.dm +++ b/code/datums/components/atom_mounted.dm @@ -113,7 +113,7 @@ return isclosedturf(target) /// Returns an list of object types we can mount on if the turf is unmountable -/obj/proc/get_moutable_objects() +/obj/proc/get_mountable_objects() PROTECTED_PROC(TRUE) SHOULD_BE_PURE(TRUE) RETURN_TYPE(/list/obj) @@ -156,7 +156,7 @@ if(is_mountable_turf(target)) attachable_atom = target //your usual wallmount else - var/list/obj/attachables = get_moutable_objects() + var/list/obj/attachables = get_mountable_objects() for(var/obj/attachable in target) if(is_type_in_list(attachable, attachables)) attachable_atom = attachable diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm index 08e873e526fb..71621f623d29 100644 --- a/code/datums/components/butchering.dm +++ b/code/datums/components/butchering.dm @@ -250,7 +250,7 @@ if (reagents_in_produced) if (target.owner.reagents) target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.bodyparts), remove_blacklisted = TRUE) - result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, target.owner.nutrition / 15 / reagents_in_produced) + result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, target.owner.nutrition / /datum/reagent/consumable/nutriment/fat::nutriment_factor / reagents_in_produced) if(LAZYLEN(diseases)) var/list/datum/disease/diseases_to_add = list() diff --git a/code/datums/components/codeword_hearing.dm b/code/datums/components/codeword_hearing.dm index 0b171d9492e1..a8ac051ab1c6 100644 --- a/code/datums/components/codeword_hearing.dm +++ b/code/datums/components/codeword_hearing.dm @@ -42,7 +42,7 @@ return // don't skip codewords when owner speaks - if(!owner.can_hear() || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return var/message = hearing_args[HEARING_RAW_MESSAGE] diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index b80d0b7d82bd..1eaa5f0c8663 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -239,7 +239,7 @@ for(var/behavior in recipe.tool_behaviors) recipe_time += dynamic_recipe_time * found_behaviors[behavior] - // DARKPACK EDIT ADD START - STORYTELLR_STATS + // DARKPACK EDIT ADD START - STORYTELLER_STATS var/mob/living/carbon/human/human_crafter if(ishuman(crafter)) human_crafter = crafter diff --git a/code/datums/components/crafting/tailoring.dm b/code/datums/components/crafting/tailoring.dm index 0fa1a50831e2..336a3aa37f32 100644 --- a/code/datums/components/crafting/tailoring.dm +++ b/code/datums/components/crafting/tailoring.dm @@ -118,13 +118,6 @@ time = 4 SECONDS category = CAT_CLOTHING */ -/datum/crafting_recipe/grass_sheath - name = "Grass Sabre Sheath" - result = /obj/item/storage/belt/sheath/grass_sabre - reqs = list(/obj/item/food/grown/grass = 4, - /obj/item/food/grown/grass/fairy = 2) - time = 4 SECONDS - category = CAT_CONTAINERS /datum/crafting_recipe/fannypack name = "Fannypack" @@ -133,7 +126,42 @@ /obj/item/stack/sheet/leather = 1) time = 2 SECONDS category = CAT_CONTAINERS + +/datum/crafting_recipe/grass_sheath + name = "Grass Sabre Sheath" + result = /obj/item/storage/belt/sheath/grass_sabre + reqs = list(/obj/item/food/grown/grass = 4, + /obj/item/food/grown/grass/fairy = 2) + time = 4 SECONDS + category = CAT_CONTAINERS + /* // DARKPACK EDIT REMOVE +/datum/crafting_recipe/gunsheath + name = "Gun Powered Sabre Sheath" + result = /obj/item/storage/belt/sheath/sabre/gunpowered + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WRENCH) + reqs = list(/datum/reagent/gunpowder = 15, + /obj/item/storage/belt/sheath/sabre = 1, + /obj/item/weaponcrafting/receiver = 1, + /obj/item/stack/cable_coil = 5, + ) + time = 15 SECONDS + category = CAT_EQUIPMENT + crafting_flags = parent_type::crafting_flags | CRAFT_SKIP_MATERIALS_PARITY + +/datum/crafting_recipe/grass_gunsheath + name = "Gun Powered Grass Sabre Sheath" + result = /obj/item/storage/belt/sheath/grass_sabre/gunpowered + tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WRENCH) + reqs = list(/datum/reagent/gunpowder = 15, + /obj/item/storage/belt/sheath/grass_sabre = 1, + /obj/item/weaponcrafting/receiver = 1, + /obj/item/stack/cable_coil = 5, + ) + time = 15 SECONDS + category = CAT_EQUIPMENT + crafting_flags = parent_type::crafting_flags | CRAFT_SKIP_MATERIALS_PARITY + /datum/crafting_recipe/hudsunsec name = "Security HUDsunglasses" result = /obj/item/clothing/glasses/hud/security/sunglasses diff --git a/code/datums/components/cult_kill_tracker.dm b/code/datums/components/cult_kill_tracker.dm new file mode 100644 index 000000000000..00023ab23e3e --- /dev/null +++ b/code/datums/components/cult_kill_tracker.dm @@ -0,0 +1,85 @@ +/// Component to handle the behavior of a nullrod keeping track of cultists it has crit or killed, and converting the item into a cult weapon when sacrificed +/datum/component/cult_kill_tracker + /// Lazylist, tracks weakrefs()s to all cultists which have been crit or killed by this nullrod. + var/list/cultists_slain + /// Ref to the last mob hit with this rod. + var/last_ref + /// The stat of the target being hit with this rod, before actually performing damage calculations. + var/last_stat = DEAD + +/datum/component/cult_kill_tracker/Initialize(...) + if(!istype(parent, /obj/item)) + return COMPONENT_INCOMPATIBLE + +/datum/component/cult_kill_tracker/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(on_attack_zone)) + RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, PROC_REF(post_hit)) + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ITEM_CULT_SACRIFICE, PROC_REF(on_sacrificed)) + if(isgun(parent)) + RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, PROC_REF(on_projectile_hit)) + RegisterSignal(parent, COMSIG_PROJECTILE_POST_HIT_LIVING, PROC_REF(post_hit)) + +/datum/component/cult_kill_tracker/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, list(COMSIG_ITEM_ATTACK_ZONE, COMSIG_ITEM_AFTERATTACK, COMSIG_ATOM_EXAMINE, COMSIG_ITEM_CULT_SACRIFICE, COMSIG_PROJECTILE_ON_HIT, COMSIG_PROJECTILE_POST_HIT_LIVING)) + +/datum/component/cult_kill_tracker/proc/on_attack_zone(obj/item/source, mob/living/target, mob/living/user) + SIGNAL_HANDLER + if(!user.mind?.holy_role) + return + if(!IS_CULTIST(target) || istype(target, /mob/living/carbon/human/cult_ghost)) + return + last_ref = WEAKREF(target) + last_stat = target.stat + +/datum/component/cult_kill_tracker/proc/post_hit(source, mob/living/target) + SIGNAL_HANDLER + if(!last_ref) + return + var/mob/living/resolved_mob = locate(last_ref) + //If they got deleted during the processing of the attack, they're probably fucking dead. + if(!istype(resolved_mob) || (resolved_mob == target && resolved_mob.stat > last_stat)) + LAZYOR(cultists_slain, last_ref) + last_ref = null + last_stat = DEAD + +/datum/component/cult_kill_tracker/proc/on_examine(obj/item/source, mob/viewer, list/examine_list) + SIGNAL_HANDLER + if(!IS_CULTIST(viewer) || !GET_ATOM_BLOOD_DNA_LENGTH(source)) + return + + var/num_slain = LAZYLEN(cultists_slain) + examine_list += span_cult_italic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \ + Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.") + +/datum/component/cult_kill_tracker/proc/on_sacrificed(obj/item/source, obj/effect/rune/convert/rune) + SIGNAL_HANDLER + var/num_slain = LAZYLEN(cultists_slain) + var/displayed_message = "[source] glows an unholy red and begins to transform..." + if(num_slain && GET_ATOM_BLOOD_DNA_LENGTH(source)) + displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [source]!" + + source.visible_message(span_cult_italic(displayed_message)) + switch(num_slain) + if(0) + rune.animate_convert_item(source, /obj/item/melee/cultblade/dagger) + if(1) + rune.animate_convert_item(source, /obj/item/melee/cultblade) + else + rune.animate_convert_item(source, /obj/item/melee/cultblade/halberd) + return COMPONENT_SACRIFICE_SUCCESSFUL + +/datum/component/cult_kill_tracker/proc/on_projectile_hit(obj/projectile/source, atom/movable/firer, atom/target) + SIGNAL_HANDLER + if(!isliving(firer) || !isliving(target)) + return + var/mob/living/firer_mob = firer + var/mob/living/target_mob = target + if(!firer_mob.mind.holy_role) + return + if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost)) + return + last_ref = WEAKREF(target_mob) + last_stat = target_mob.stat diff --git a/code/datums/components/fearful/sources/phobia.dm b/code/datums/components/fearful/sources/phobia.dm index e93038677698..155e949f298f 100644 --- a/code/datums/components/fearful/sources/phobia.dm +++ b/code/datums/components/fearful/sources/phobia.dm @@ -101,7 +101,7 @@ return // Words can't trigger you if you can't hear them *taps head* - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER] || !owner.has_language(hearing_args[HEARING_LANGUAGE])) return if(trigger_regex.Find(hearing_args[HEARING_RAW_MESSAGE])) diff --git a/code/datums/components/fullauto.dm b/code/datums/components/fullauto.dm index bf19c5b16727..d7f68e55bae0 100644 --- a/code/datums/components/fullauto.dm +++ b/code/datums/components/fullauto.dm @@ -209,8 +209,7 @@ stop_autofiring() return COMPONENT_CLIENT_MOUSEUP_INTERCEPT - -/datum/component/automatic_fire/proc/stop_autofiring(datum/source, atom/object, turf/location, control, params) +/datum/component/automatic_fire/proc/stop_autofiring(mob/living/source, obj/item/swapped_to, obj/item/swapped_from) SIGNAL_HANDLER if(autofire_stat != AUTOFIRE_STAT_FIRING) return diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm index 5498a8a81ef8..65b3a9e13764 100644 --- a/code/datums/components/jetpack.dm +++ b/code/datums/components/jetpack.dm @@ -94,9 +94,8 @@ /datum/component/jetpack/proc/setup_trail(mob/user) if(trail) QDEL_NULL(trail) - trail = new effect_type + trail = new effect_type(user) trail.auto_process = FALSE - trail.set_up(user) trail.start() /datum/component/jetpack/proc/activate(datum/source, mob/new_user) diff --git a/code/datums/components/palette.dm b/code/datums/components/palette.dm index 0fb937a3b11c..a3551b0b59f1 100644 --- a/code/datums/components/palette.dm +++ b/code/datums/components/palette.dm @@ -5,9 +5,10 @@ * and call set_painting_tool_color() on the parent for more specific object behavior. */ /datum/component/palette + /// The maximum number of colors this palette can have. + var/max_colors /* * A list that stores a selection of colors. - * The number of available spaces is defined by the available_space arg of Initialize() */ var/list/colors = list() /* @@ -20,25 +21,20 @@ /// The radial menu choice datums are stored here as a microop to avoid generating new ones every time the menu is opened or updated. var/list/datum/radial_menu_choice/menu_choices -/datum/component/palette/Initialize(available_space, selected_color) +/datum/component/palette/Initialize(max_colors, selected_color) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE - if(!isnum(available_space) || available_space < 1) /// This component means nothing if there's no space for colors - stack_trace("palette component initialized without a proper value for the available_space arg") + if(!isnum(max_colors) || max_colors < 1) /// This component means nothing if there's no space for colors + stack_trace("palette component initialized without a proper value for the max_colors arg") return COMPONENT_INCOMPATIBLE - for(var/index in 1 to available_space) - colors += "#ffffff" - - src.colors = colors + src.max_colors = max_colors src.selected_color = selected_color || "#ffffff" RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF_SECONDARY, PROC_REF(on_attack_self_secondary)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(parent, COMSIG_PAINTING_TOOL_SET_COLOR, PROC_REF(on_painting_tool_set_color)) - RegisterSignal(parent, COMSIG_PAINTING_TOOL_GET_ADDITIONAL_DATA, PROC_REF(get_palette_data)) - RegisterSignal(parent, COMSIG_PAINTING_TOOL_PALETTE_COLOR_CHANGED, PROC_REF(palette_color_changed)) /datum/component/palette/Destroy() QDEL_NULL(color_picker_menu) @@ -51,7 +47,7 @@ SIGNAL_HANDLER examine_list += span_notice("Right-Click this item while it's in your active hand to open/close its color picker menu.") - examine_list += span_notice("In the color picker, Left-Click a color button to pick it or Right-Click to edit it.") + examine_list += span_notice("In the color picker, Left-Click a color button to pick it or Right-Click to remove it.") /datum/component/palette/proc/on_attack_self_secondary(datum/source, mob/user) SIGNAL_HANDLER @@ -72,12 +68,27 @@ /datum/component/palette/proc/build_radial_list() var/radial_list = list() - LAZYSETLEN(menu_choices, length(colors)) + var/color_count = length(colors) + LAZYSETLEN(menu_choices, max(color_count+1)) + if(color_count < max_colors && !(selected_color in colors)) + var/datum/radial_menu_choice/add_option = peek(menu_choices) + if(!add_option) + add_option = new + menu_choices[color_count+1] = add_option + var/image/element = image(icon = 'icons/hud/radial.dmi', icon_state = "palette_element") + element.color = selected_color + var/image/plus = image(icon = 'icons/hud/radial.dmi', icon_state = "palette_add") + plus.appearance_flags = /image::appearance_flags | RESET_COLOR + element.add_overlay(plus) + add_option.image = element + add_option.name = "Add Color ([selected_color])" + radial_list["add"] = add_option for(var/index in 1 to length(colors)) var/hexcolor = colors[index] var/datum/radial_menu_choice/option = menu_choices[index] if(!option) option = new + menu_choices[index] = option var/icon_state_to_use = hexcolor == selected_color ? "palette_selected" : "palette_element" var/image/element = image(icon = 'icons/hud/radial.dmi', icon_state = icon_state_to_use) element.color = hexcolor @@ -104,11 +115,14 @@ close_radial_menu() return var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK) + if(choice == "add") + if(length(colors) < max_colors) + colors += selected_color + update_radial_list() + return var/index = text2num(choice) if(is_right_clicking) - var/chosen_color = tgui_color_picker(user, "Pick new color", "[parent]", colors[index]) - if(chosen_color && !QDELETED(src) && !IS_DEAD_OR_INCAP(user) && user.is_holding(parent)) - colors[index] = chosen_color + colors.Cut(index, index+1) update_radial_list() else var/obj/item/parent_item = parent @@ -119,24 +133,3 @@ selected_color = chosen_color update_radial_list() - -/datum/component/palette/proc/get_palette_data(datum/source, data) - SIGNAL_HANDLER - var/list/painting_data = list() - for(var/hexcolor in colors) - painting_data += list(list( - "color" = hexcolor, - "is_selected" = hexcolor == selected_color - )) - data["paint_tool_palette"] = painting_data - -/datum/component/palette/proc/palette_color_changed(datum/source, chosen_color, index) - SIGNAL_HANDLER - - var/was_selected_color = selected_color == colors[index] - colors[index] = chosen_color - if(was_selected_color) - var/obj/item/parent_item = parent - parent_item.set_painting_tool_color(chosen_color) - else - update_radial_list() diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index 34ecac634bab..b0dc7d8f8629 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -112,7 +112,8 @@ var/original_speed = shell.loaded_projectile.speed var/original_wounds_bonus = shell.loaded_projectile.wound_bonus var/original_bare_wounds_bonus = shell.loaded_projectile.exposed_wound_bonus - var/original_ignored_faction = shell.loaded_projectile.ignored_factions + var/original_ignored_faction = shell.loaded_projectile.get_faction() + var/original_ignored_allies = shell.loaded_projectile.allies for(var/i in 1 to num_pellets) shell.ready_proj(target, user, SUPPRESSED_VERY, zone_override, fired_from) @@ -129,7 +130,8 @@ shell.loaded_projectile.speed = original_speed shell.loaded_projectile.wound_bonus = original_wounds_bonus shell.loaded_projectile.exposed_wound_bonus = original_bare_wounds_bonus - shell.loaded_projectile.ignored_factions = original_ignored_faction + shell.loaded_projectile.set_faction(original_ignored_faction) + shell.loaded_projectile.set_allies(original_ignored_allies) pellets += shell.loaded_projectile var/turf/current_loc = get_turf(fired_from) if (!istype(target_loc) || !istype(current_loc) || !(shell.loaded_projectile)) diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm index 295438c4bd2b..716f8e0ccd20 100644 --- a/code/datums/components/plumbing/_plumbing.dm +++ b/code/datums/components/plumbing/_plumbing.dm @@ -20,7 +20,6 @@ /// Ex - if this was set to "3", our component would only request the first 3 reagents found, even if more are available var/distinct_reagent_cap = INFINITY -///turn_connects is for wheter or not we spin with the object to change our pipes /datum/component/plumbing/Initialize(ducting_layer) if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE @@ -36,6 +35,7 @@ reagents = parent_movable.reagents + on_parent_dir_change(parent_movable, NONE, parent_movable.dir) if(parent_movable.anchored) if(PERFORM_ALL_TESTS(maptest_log_mapping)) var/datum/overlap = ducting_layer_check(parent_movable, ducting_layer) @@ -51,23 +51,21 @@ enable() /datum/component/plumbing/RegisterWithParent() - RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(disable)) RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_WRENCH), PROC_REF(check_wrench)) RegisterSignal(parent, COMSIG_MOVABLE_SET_ANCHORED, PROC_REF(toggle_active)) RegisterSignal(parent, COMSIG_OBJ_HIDE, PROC_REF(hide)) - RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(create_overlays)) //called by lateinit on startup - RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_parent_dir_change)) //called when placed on a shuttle and it moves, and other edge cases + RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(create_overlays)) + RegisterSignal(parent, COMSIG_ATOM_POST_DIR_CHANGE, PROC_REF(on_parent_dir_change)) RegisterSignal(parent, COMSIG_MOVABLE_CHANGE_DUCT_LAYER, PROC_REF(change_ducting_layer)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) /datum/component/plumbing/UnregisterFromParent() UnregisterSignal(parent, list( - COMSIG_MOVABLE_MOVED, COMSIG_ATOM_TOOL_ACT(TOOL_WRENCH), COMSIG_MOVABLE_SET_ANCHORED, COMSIG_OBJ_HIDE, COMSIG_ATOM_UPDATE_OVERLAYS, - COMSIG_ATOM_DIR_CHANGE, + COMSIG_ATOM_POST_DIR_CHANGE, COMSIG_MOVABLE_CHANGE_DUCT_LAYER, COMSIG_ATOM_EXAMINE, )) @@ -96,24 +94,6 @@ ///settle wherever we are, and start behaving like a piece of plumbing /datum/component/plumbing/proc/enable() - var/atom/movable/parent_movable = parent - - //We update our connects only when we settle down by taking our current and original direction to find our new connects - - demand_connects = initial(demand_connects) - supply_connects = initial(supply_connects) - if(parent_movable.dir != SOUTH) - var/angle = 180 - dir2angle(parent_movable.dir) - var/new_demand_connects = NONE - var/new_supply_connects = NONE - for(var/direction in GLOB.cardinals) - if(direction & demand_connects) - new_demand_connects |= turn(direction, angle) - if(direction & supply_connects) - new_supply_connects |= turn(direction, angle) - demand_connects = new_demand_connects - supply_connects = new_supply_connects - if(demand_connects) START_PROCESSING(SSplumbing, src) @@ -232,24 +212,34 @@ /datum/component/plumbing/proc/on_parent_dir_change(atom/movable/parent_obj, old_dir, new_dir) SIGNAL_HANDLER - if(old_dir == new_dir) - return + demand_connects = initial(demand_connects) + supply_connects = initial(supply_connects) + if(new_dir != SOUTH) + var/angle = 180 - dir2angle(new_dir) + var/new_demand_connects = NONE + var/new_supply_connects = NONE + for(var/direction in GLOB.cardinals) + if(direction & demand_connects) + new_demand_connects |= turn(direction, angle) + if(direction & supply_connects) + new_supply_connects |= turn(direction, angle) + demand_connects = new_demand_connects + supply_connects = new_supply_connects - // Defer to later frame because pixel_* is actually updated after all callbacks - addtimer(CALLBACK(parent_obj, TYPE_PROC_REF(/atom/, update_appearance)), 0.1 SECONDS) + if(length(ducts)) + disable() + enable() /datum/component/plumbing/proc/change_ducting_layer(obj/source, obj/changer, new_layer = DUCT_LAYER_DEFAULT) SIGNAL_HANDLER ducting_layer = new_layer - var/atom/movable/parent_movable = parent - parent_movable.update_appearance() + source.update_appearance(UPDATE_OVERLAYS) if(changer) playsound(changer, 'sound/items/tools/ratchet.ogg', 10, TRUE) //sound - //quickly disconnect and reconnect the network. - if(active()) + if(length(ducts)) disable() enable() diff --git a/code/datums/components/plumbing/simple_components.dm b/code/datums/components/plumbing/simple_components.dm index c86dddda391b..50ba2cc6a8ad 100644 --- a/code/datums/components/plumbing/simple_components.dm +++ b/code/datums/components/plumbing/simple_components.dm @@ -20,6 +20,19 @@ edge_overlay.pixel_z = -parent_movable.pixel_y - parent_movable.pixel_z overlays += edge_overlay +///For disposing reagents +/datum/component/plumbing/simple_demand/disposer + +/datum/component/plumbing/simple_demand/disposer/Initialize(ducting_layer, distinct_reagent_cap) + if(!istype(parent, /obj/machinery/plumbing/disposer)) + return COMPONENT_INCOMPATIBLE + return ..() + +/datum/component/plumbing/simple_demand/disposer/send_request(dir) + var/obj/machinery/plumbing/disposer/target = parent + if(target.on) + process_request(target.disposal_rate * SSFLUIDS_DT, dir = dir) + ///has one pipe output that only supplies. example is liquid pump and manual input pipe /datum/component/plumbing/simple_supply supply_connects = SOUTH diff --git a/code/datums/components/reskinnable_atom.dm b/code/datums/components/reskinnable_atom.dm index 5f4847eafcb0..ab46ec0c3297 100644 --- a/code/datums/components/reskinnable_atom.dm +++ b/code/datums/components/reskinnable_atom.dm @@ -13,6 +13,8 @@ var/change_base_icon_state = FALSE /// If true, changing the reskin also changes the inhand_icon_state of the atom var/change_inhand_icon_state = FALSE + /// If true, changing the reskin also changes the worn_icon_state of the atom + var/change_worn_icon_state = TRUE /// If true, unset vars are reset to their original values when applying this skin var/reset_missing = TRUE @@ -86,7 +88,8 @@ if(!item_apply_to.greyscale_config_inhand_left) APPLY_VAR_OR_RESET_INITIAL(item_apply_to, lefthand_file, new_lefthand_file, reset_missing) APPLY_VAR_OR_RESET_INITIAL(item_apply_to, righthand_file, new_righthand_file, reset_missing) - APPLY_VAR_OR_RESET_INITIAL(item_apply_to, worn_icon_state, new_icon_state, reset_missing) + if(change_worn_icon_state) + APPLY_VAR_OR_RESET_INITIAL(item_apply_to, worn_icon_state, new_icon_state, reset_missing) if(change_inhand_icon_state || new_inhand_icon_state) APPLY_VAR_OR_RESET_INITIAL(item_apply_to, inhand_icon_state, new_inhand_icon_state || new_icon_state, reset_missing) diff --git a/code/datums/components/riding/riding.dm b/code/datums/components/riding/riding.dm index f89557f0e2a7..8e5f1eab52f3 100644 --- a/code/datums/components/riding/riding.dm +++ b/code/datums/components/riding/riding.dm @@ -32,6 +32,8 @@ var/override_allow_spacemove = FALSE /// can anyone other than the rider unbuckle the rider? var/can_force_unbuckle = TRUE + /// Like last_bumped for mobs, but for vehicles. Exists to allow the door bump cooldown while also passing door openings through Bumped() + var/vehicle_last_bumped = 0 /** * Ride check flags defined for the specific riding component types, so we know if we need arms, legs, or whatever. @@ -270,10 +272,12 @@ /// So we can check all occupants when we bump a door to see if anyone has access /datum/component/riding/proc/vehicle_bump(atom/movable/movable_parent, obj/machinery/door/possible_bumped_door) SIGNAL_HANDLER - if(!istype(possible_bumped_door)) + if(!istype(possible_bumped_door) || world.time - vehicle_last_bumped <= 0.3 SECONDS) return - for(var/occupant in movable_parent.buckled_mobs) - INVOKE_ASYNC(possible_bumped_door, TYPE_PROC_REF(/obj/machinery/door/, bumpopen), occupant) + for(var/mob/living/occupant in movable_parent.buckled_mobs) + vehicle_last_bumped = world.time + occupant.last_bumped = 0 + INVOKE_ASYNC(possible_bumped_door, TYPE_PROC_REF(/atom, Bumped), occupant) /datum/component/riding/proc/Unbuckle(atom/movable/M) addtimer(CALLBACK(parent, TYPE_PROC_REF(/atom/movable/, unbuckle_mob), M), 0, TIMER_UNIQUE) diff --git a/code/datums/components/throwbonus_on_windup.dm b/code/datums/components/throwbonus_on_windup.dm index dd12b945f29e..0b767939f9f5 100644 --- a/code/datums/components/throwbonus_on_windup.dm +++ b/code/datums/components/throwbonus_on_windup.dm @@ -91,7 +91,7 @@ else end_windup() -/datum/component/throwbonus_on_windup/proc/on_hands_swap(mob/living/source) +/datum/component/throwbonus_on_windup/proc/on_hands_swap(mob/living/source, obj/item/swapped_to, obj/item/swapped_from) SIGNAL_HANDLER if(source.get_active_held_item() != parent) diff --git a/code/datums/components/weatherannouncer.dm b/code/datums/components/weatherannouncer.dm index 35c51f0c3436..c514aa77b150 100644 --- a/code/datums/components/weatherannouncer.dm +++ b/code/datums/components/weatherannouncer.dm @@ -17,10 +17,20 @@ /// Overlay added when you are in danger var/state_danger + /// If null, does nothing + /// If set to a ztrait, like ZTRAIT_MINING, we check zlevels with that trait for weather towers + /// Having no active radar towers on those levels will reduce the accuracy of the warnings + var/radar_z_trait + + /// Modifier to the accuracy of the weather warning times + /// Changes every unique storm event + VAR_FINAL/accuracy_mod = 0 + /datum/component/weather_announcer/Initialize( state_normal, state_warning, state_danger, + radar_z_trait, ) . = ..() if (!ismovable(parent)) @@ -34,6 +44,7 @@ src.state_normal = state_normal src.state_warning = state_warning src.state_danger = state_danger + src.radar_z_trait = radar_z_trait var/atom/speaker = parent speaker.update_appearance(UPDATE_ICON) update_light_color() @@ -70,6 +81,14 @@ var/atom/speaker = parent speaker.update_appearance(UPDATE_ICON) +/datum/component/weather_announcer/proc/check_accuracy() + for(var/z_level in SSmapping.levels_by_trait(radar_z_trait)) + for(var/obj/machinery/power/weather_tower/radar as anything in GLOB.weather_towers["[z_level]"]) + if(radar.active) + return TRUE + + return FALSE + /datum/component/weather_announcer/process(seconds_per_tick) if (!enabled) return @@ -84,7 +103,7 @@ var/obj/machinery/announcement_system/aas = get_announcement_system(/datum/aas_config_entry/weather, speaker, list(RADIO_CHANNEL_SUPPLY)) // Active AAS will override default announcement lines if (aas) - msg = aas.compile_config_message(/datum/aas_config_entry/weather, list(), !is_weather_dangerous ? 4 : warning_level + 1) + msg = aas.compile_config_message(/datum/aas_config_entry/weather, list(), is_weather_dangerous ? warning_level + 1 : 4) // Stop toggling on radios for it, please! aas.broadcast(msg, list(RADIO_CHANNEL_SUPPLY)) // Still say it, because you can be not on our level @@ -117,17 +136,17 @@ return "Error in meteorological calculation. Please report this deviation to a trained programmer." /datum/component/weather_announcer/proc/time_till_storm() - var/list/mining_z_levels = SSmapping.levels_by_trait(ZTRAIT_MINING) + var/list/mining_z_levels = SSmapping.levels_by_trait(radar_z_trait) if(!length(mining_z_levels)) return // No problems if there are no mining z levels - for(var/datum/weather/check_weather as anything in SSweather.processing) if(!(check_weather.weather_flags & WEATHER_BAROMETER) || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE) continue for (var/mining_level in mining_z_levels) if(mining_level in check_weather.impacted_z_levels) warning_level = WEATHER_ALERT_IMMINENT_OR_ACTIVE + accuracy_mod = 0 // reset for next storm return 0 var/time_until_next = INFINITY @@ -135,6 +154,13 @@ var/next_time = timeleft(SSweather.next_hit_by_zlevel["[mining_level ]"]) || INFINITY if (next_time && next_time < time_until_next) time_until_next = next_time + + if(!check_accuracy()) + if(!accuracy_mod && radar_z_trait) + accuracy_mod = rand(-9, 9) * 5 SECONDS + + time_until_next = max(10 SECONDS, time_until_next + accuracy_mod) + return time_until_next /// Polls existing weather for what kind of warnings we should be displaying. @@ -156,7 +182,7 @@ for(var/datum/weather/check_weather as anything in SSweather.processing) if(!(check_weather.weather_flags & WEATHER_BAROMETER) || check_weather.stage == WIND_DOWN_STAGE || check_weather.stage == END_STAGE) continue - var/list/mining_z_levels = SSmapping.levels_by_trait(ZTRAIT_MINING) + var/list/mining_z_levels = SSmapping.levels_by_trait(radar_z_trait) for(var/mining_level in mining_z_levels) if(mining_level in check_weather.impacted_z_levels) is_weather_dangerous = (check_weather.weather_flags & FUNCTIONAL_WEATHER) @@ -167,10 +193,13 @@ if(isnull(time_until_next)) return if (time_until_next == 0) - examine_texts += span_warning ("A storm is currently active, please seek shelter.") + examine_texts += span_warning("A storm is currently active, please seek shelter.") else examine_texts += span_notice("The next storm is inbound in [DisplayTimeText(time_until_next)].") + if(!check_accuracy()) + examine_texts += span_smallnoticeital("Due to insufficient radar coverage, the timing of this forecast may be inaccurate.") + /datum/component/weather_announcer/RegisterWithParent() RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index ff3de4c48234..3861377212f4 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -107,6 +107,7 @@ var/recovery_prob = 0 var/cure_mod var/bad_immune = HAS_TRAIT(affected_mob, TRAIT_IMMUNODEFICIENCY) ? 2 : 1 + var/is_sleeping = HAS_TRAIT_FROM_ONLY(affected_mob, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(/datum/status_effect/incapacitating/sleeping::id)) if(required_organ) if(!has_required_infectious_organ(affected_mob, required_organ)) @@ -133,7 +134,7 @@ if(stage == max_stages && stage_peaked != TRUE) //mostly a sanity check in case we manually set a virus to max stages stage_peaked = TRUE - if(SPT_PROB(stage_prob*slowdown*bad_immune, seconds_per_tick)) + if(SPT_PROB(stage_prob * slowdown * bad_immune, seconds_per_tick)) update_stage(min(stage + 1, max_stages)) if(!(disease_flags & CHRONIC) && disease_flags & CURABLE && bypasses_immunity != TRUE) @@ -185,7 +186,7 @@ if(SANITY_LEVEL_INSANE) recovery_prob += -0.4 - if((HAS_TRAIT(affected_mob, TRAIT_NOHUNGER) || !(affected_mob.satiety < 0 || affected_mob.nutrition < NUTRITION_LEVEL_STARVING)) && HAS_TRAIT_FROM_ONLY(affected_mob, TRAIT_KNOCKEDOUT, TRAIT_STATUS_EFFECT(/datum/status_effect/incapacitating/sleeping::id))) //resting starved won't help, but resting helps + if((HAS_TRAIT(affected_mob, TRAIT_NOHUNGER) || !(affected_mob.satiety < 0 || affected_mob.nutrition < NUTRITION_LEVEL_STARVING)) && is_sleeping) //resting starved won't help, but resting helps var/turf/rest_turf = get_turf(affected_mob) var/is_sleeping_in_darkness = rest_turf.get_lumcount() <= LIGHTING_TILE_IS_DARK @@ -230,7 +231,7 @@ return FALSE update_stage(max(stage - 1, 1)) - if(HAS_TRAIT(affected_mob, TRAIT_KNOCKEDOUT) || slowdown != 1) //sleeping and using spaceacillin lets us nosell applicable virus symptoms firing with decreasing effectiveness over time + if(is_sleeping || slowdown != 1) //sleeping and using spaceacillin lets us nosell applicable virus symptoms firing with decreasing effectiveness over time if(prob(100 - min((100 * (symptom_offsets / DISEASE_SYMPTOM_OFFSET_DURATION)), 100 - cure_chance * DISEASE_FINAL_CURE_CHANCE_MULTIPLIER))) //viruses with higher cure_chance will ultimately be more possible to offset symptoms on symptom_offsets = min(symptom_offsets + 1, DISEASE_SYMPTOM_OFFSET_DURATION) return FALSE diff --git a/code/datums/diseases/advance/symptoms/disfiguration.dm b/code/datums/diseases/advance/symptoms/disfiguration.dm index 2079e58d56e7..08f6896d0162 100644 --- a/code/datums/diseases/advance/symptoms/disfiguration.dm +++ b/code/datums/diseases/advance/symptoms/disfiguration.dm @@ -20,24 +20,28 @@ symptom_delay_max = 75 symptom_cure = /datum/reagent/consumable/milk -/datum/symptom/disfiguration/Activate(datum/disease/advance/A) +/datum/symptom/disfiguration/Activate(datum/disease/advance/disease) . = ..() if(!.) return - var/mob/living/M = A.affected_mob - if (HAS_TRAIT(M, TRAIT_DISFIGURED)) + var/mob/living/carbon/victim = disease.affected_mob + var/obj/item/bodypart/head = victim.get_bodypart(BODY_ZONE_HEAD) + if(!head || HAS_TRAIT_FROM(head, TRAIT_DISFIGURED, DISEASE_TRAIT)) return - switch(A.stage) + + switch(disease.stage) if(5) - ADD_TRAIT(M, TRAIT_DISFIGURED, DISEASE_TRAIT) - M.visible_message(span_warning("[M]'s face appears to cave in!"), span_notice("You feel your face crumple and cave in!")) + ADD_TRAIT(head, TRAIT_DISFIGURED, DISEASE_TRAIT) + victim.visible_message(span_warning("[victim]'s face appears to cave in!"), span_notice("You feel your face crumple and cave in!")) else - M.visible_message(span_warning("[M]'s face begins to contort..."), span_notice("Your face feels wet and malleable...")) + victim.visible_message(span_warning("[victim]'s face begins to contort..."), span_notice("Your face feels wet and malleable...")) -/datum/symptom/disfiguration/End(datum/disease/advance/A) +/datum/symptom/disfiguration/End(datum/disease/advance/disease) . = ..() if(!.) return - if(A.affected_mob) - REMOVE_TRAIT(A.affected_mob, TRAIT_DISFIGURED, DISEASE_TRAIT) + var/mob/living/carbon/victim = disease.affected_mob + var/obj/item/bodypart/head = victim?.get_bodypart(BODY_ZONE_HEAD) + if(head) + REMOVE_TRAIT(head, TRAIT_DISFIGURED, DISEASE_TRAIT) diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 154642529983..92099d101ab9 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -601,6 +601,8 @@ needs_update += carbon_host.adjust_tox_loss(-2 * heal_amt, updating_health = FALSE) var/brute_burn_heal = carbon_host.heal_overall_damage(heal_amt, heal_amt, required_bodytype = healable_bodytypes, updating_health = FALSE) needs_update += brute_burn_heal + if(needs_update) + carbon_host.updatehealth() if(brute_burn_heal && prob(4)) to_chat(carbon_host, span_notice("Your skin glows faintly, and you feel your wounds mending themselves.")) return TRUE diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index 34923ed9ffb3..0a26353e0f6d 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -73,7 +73,7 @@ if(!advanced_disease.has_required_infectious_organ(advanced_disease.affected_mob, required_organ)) return FALSE if(symptom_cure) - if(advanced_disease.affected_mob.has_reagent(symptom_cure)) + if(symptom_cure && advanced_disease.affected_mob.has_reagent(symptom_cure)) remedied = TRUE return FALSE remedied = FALSE diff --git a/code/datums/elements/art.dm b/code/datums/elements/art.dm index 0ec98410b1df..93e156081d9d 100644 --- a/code/datums/elements/art.dm +++ b/code/datums/elements/art.dm @@ -72,6 +72,8 @@ var/list/haters = list() for(var/hater_department_type in list(/datum/job_department/security, /datum/job_department/command)) var/datum/job_department/hater_department = SSjob.get_department_type(hater_department_type) + if (isnull(hater_department)) + continue for(var/datum/job/hater_job as anything in hater_department.department_jobs) haters += hater_job.title var/datum/job/quartermaster/fucking_quartermaster = SSjob.get_job_type(/datum/job/quartermaster) diff --git a/code/datums/elements/blood_limb_overlay.dm b/code/datums/elements/blood_limb_overlay.dm index 0738bc16b13b..3028d3f4754e 100644 --- a/code/datums/elements/blood_limb_overlay.dm +++ b/code/datums/elements/blood_limb_overlay.dm @@ -15,13 +15,14 @@ /datum/element/blood_limb_overlay/proc/on_limb_icon(obj/item/bodypart/source, list/limb_icons, dropped, mob/living/carbon/update_on) SIGNAL_HANDLER - if (!LAZYLEN(source.blood_dna_info) || source.is_invisible) + var/list/blood_dna_info = source.blood_dna_info || update_on?.get_blood_dna_list() + if (!LAZYLEN(blood_dna_info) || source.is_invisible) return var/image/limb = limb_icons[1] var/image/blood_visual = image(limb.icon, "[limb.icon_state]_blood", dir = (dropped ? SOUTH : null)) // We need to convert it to HSV and then adjust the colors to make them look brighter on the grayscale blood overlay - var/list/target_color = rgb2num(get_color_from_blood_list(source.blood_dna_info), COLORSPACE_HSV) + var/list/target_color = rgb2num(get_color_from_blood_list(blood_dna_info), COLORSPACE_HSV) blood_visual.color = rgb(target_color[1], ceil(target_color[2] * 0.4), clamp(ceil(target_color[3] * 1.33), 0, 100), space = COLORSPACE_HSV) limb.overlays += blood_visual if (!source.aux_zone) @@ -35,5 +36,6 @@ /datum/element/blood_limb_overlay/proc/on_icon_key(obj/item/bodypart/source, list/icon_keys) SIGNAL_HANDLER - if (LAZYLEN(source.blood_dna_info) && !source.is_invisible) - icon_keys += "-blood-[get_color_from_blood_list(source.blood_dna_info)]" + var/list/blood_dna_info = source.blood_dna_info || source.owner?.get_blood_dna_list() + if (LAZYLEN(blood_dna_info) && !source.is_invisible) + icon_keys += "-blood-[get_color_from_blood_list(blood_dna_info)]" diff --git a/code/datums/elements/deliver_first.dm b/code/datums/elements/deliver_first.dm index b49c4dc28f4b..7455c0646a1d 100644 --- a/code/datums/elements/deliver_first.dm +++ b/code/datums/elements/deliver_first.dm @@ -97,8 +97,7 @@ ///called to remove the element in a flavorful way, either from delivery or from emagging/breaking open the crate /datum/element/deliver_first/proc/remove_lock(obj/structure/closet/target) target.visible_message(span_notice("[target]'s delivery lock self destructs, spewing sparks from the mechanism!")) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(4, 0, target.loc) + var/datum/effect_system/basic/spark_spread/spark_system = new(target.loc, 4, 0) spark_system.start() playsound(src, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) target.RemoveElement(/datum/element/deliver_first, goal_area_type, payment) diff --git a/code/datums/elements/food/dunkable.dm b/code/datums/elements/food/dunkable.dm index fa2a9cbbc1c5..5491b5ca8dac 100644 --- a/code/datums/elements/food/dunkable.dm +++ b/code/datums/elements/food/dunkable.dm @@ -19,16 +19,16 @@ /datum/element/dunkable/proc/get_dunked(obj/item/source, mob/user, atom/target, params) SIGNAL_HANDLER - if(target.reagents?.flags & DUNKABLE) // container should be a valid target for dunking - if(!target.is_drainable()) - to_chat(user, span_warning("[target] is unable to be dunked in!")) - return ITEM_INTERACT_BLOCKING - if(target.reagents.trans_to(source, dunk_amount, transferred_by = user)) //if reagents were transferred, show the message - to_chat(user, span_notice("You dunk \the [target] into \the [target].")) - return ITEM_INTERACT_SUCCESS - if(!target.reagents.total_volume) - to_chat(user, span_warning("[target] is empty!")) - else - to_chat(user, span_warning("[source] is full!")) + if(!target.is_dunkable()) // container should be a valid target for dunking + return NONE + if(!target.is_drainable()) + to_chat(user, span_warning("[target] is unable to be dunked in!")) return ITEM_INTERACT_BLOCKING - return NONE + if(target.reagents.trans_to(source, dunk_amount, transferred_by = user)) //if reagents were transferred, show the message + to_chat(user, span_notice("You dunk \the [target] into \the [target].")) + return ITEM_INTERACT_SUCCESS + if(!target.reagents.total_volume) + to_chat(user, span_warning("[target] is empty!")) + else + to_chat(user, span_warning("[source] is full!")) + return ITEM_INTERACT_BLOCKING diff --git a/code/datums/elements/gags_recolorable.dm b/code/datums/elements/gags_recolorable.dm index 3802492bc3b5..643e89b33db3 100644 --- a/code/datums/elements/gags_recolorable.dm +++ b/code/datums/elements/gags_recolorable.dm @@ -42,6 +42,10 @@ allowed_configs += "[initial(item.greyscale_config_inhand_left)]" if(initial(item.greyscale_config_inhand_right)) allowed_configs += "[initial(item.greyscale_config_inhand_right)]" + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + if(initial(item.greyscale_config_onfloor)) + allowed_configs += "[initial(item.greyscale_config_onfloor)]" + // DARKPACK EDIT ADD END var/datum/greyscale_modify_menu/spray_paint/menu = new( target, user, allowed_configs, CALLBACK(src, PROC_REF(recolor), user, can, target), diff --git a/code/datums/elements/nullrod_core.dm b/code/datums/elements/nullrod_core.dm index 020874777d5f..fa48e421f8e1 100644 --- a/code/datums/elements/nullrod_core.dm +++ b/code/datums/elements/nullrod_core.dm @@ -18,6 +18,7 @@ on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed), target), \ effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \ ) + target.AddComponent(/datum/component/cult_kill_tracker) target.AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE) ADD_TRAIT(target, TRAIT_NULLROD_ITEM, ELEMENT_TRAIT(type)) diff --git a/code/datums/elements/table_smash.dm b/code/datums/elements/table_smash.dm index 74bf2bc9d58f..8650b2d88e28 100644 --- a/code/datums/elements/table_smash.dm +++ b/code/datums/elements/table_smash.dm @@ -96,7 +96,7 @@ if (carried_mob == user) //Piggyback user. return NONE - INVOKE_ASYNC(src, PROC_REF(riding_offhand_act), user, item) + INVOKE_ASYNC(src, PROC_REF(riding_offhand_act), user, item, table) return ITEM_INTERACT_BLOCKING /// Called when someone clicks on our surface using a fireman's carry diff --git a/code/datums/elements/uses_girder_wall_recipes.dm b/code/datums/elements/uses_girder_wall_recipes.dm new file mode 100644 index 000000000000..b313f78200e9 --- /dev/null +++ b/code/datums/elements/uses_girder_wall_recipes.dm @@ -0,0 +1,130 @@ +/// An element for girders, wall barricades, etc. that makes them use wall construction recipes. +/// Only really meant for recipes where you click on the girder with a stack of materials to make a wall. +/datum/element/uses_girder_wall_recipes + +/datum/element/uses_girder_wall_recipes/Attach(datum/target) + . = ..() + if (!isstructure(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignals(target, list(COMSIG_ATOM_ITEM_INTERACTION, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY), PROC_REF(on_item_interaction)) + +/datum/element/uses_girder_wall_recipes/Detach(datum/target, ...) + UnregisterSignal(target, list(COMSIG_ATOM_ITEM_INTERACTION, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY)) + return ..() + +/datum/element/uses_girder_wall_recipes/proc/on_item_interaction(obj/structure/structure, mob/living/user, obj/item/stack/stack, list/modifiers) + SIGNAL_HANDLER + if (!isstack(stack)) + return NONE + if (!stack.usable_for_construction) + structure.balloon_alert(user, "unusable material!") + return ITEM_INTERACT_BLOCKING + + var/datum/girder_wall_recipe/main_recipe = get_main_recipe(structure, stack) + + if (main_recipe) + INVOKE_ASYNC(src, PROC_REF(attempt_recipe), structure, user, stack, main_recipe, is_material_recipe = FALSE) + return ITEM_INTERACT_BLOCKING + + if (stack.has_unique_girder) + structure.balloon_alert(user, "needs a different girder!") + return ITEM_INTERACT_BLOCKING + + // Plasteel is used for reinforcing girders. + if (istype(structure, /obj/structure/girder) && istype(stack, /obj/item/stack/sheet/plasteel)) + return NONE + + var/datum/girder_wall_recipe/material_recipe = get_material_recipe(structure, stack) + + if (material_recipe) + INVOKE_ASYNC(src, PROC_REF(attempt_recipe), structure, user, stack, material_recipe, is_material_recipe = TRUE) + return ITEM_INTERACT_BLOCKING + +/// Returns the main wall recipe of the stack for the structure, if any. +/datum/element/uses_girder_wall_recipes/proc/get_main_recipe(obj/structure/structure, obj/item/stack/stack) + for (var/datum/girder_wall_recipe/recipe as anything in GLOB.main_girder_wall_recipes) + if (!istype(stack, recipe.stack_type)) + continue + if (!istype(structure, recipe.girder_type)) + continue + if (!check_girder_state(structure, recipe)) + continue + return recipe + +/// Returns the material wall recipe of the stack for the structure, if any. +/datum/element/uses_girder_wall_recipes/proc/get_material_recipe(obj/structure/structure, obj/item/stack/stack) + for (var/datum/girder_wall_recipe/recipe as anything in GLOB.material_girder_wall_recipes) + if (!istype(structure, recipe.girder_type)) + continue + if (!check_girder_state(structure, recipe)) + continue + return recipe + +/// Has the user attempt the wall recipe asynchronously. +/// Assumes that the structure and stack are of valid types for the recipe. +/datum/element/uses_girder_wall_recipes/proc/attempt_recipe_async(obj/structure/structure, mob/living/user, obj/item/stack/stack, datum/girder_wall_recipe/recipe, is_material_recipe) + INVOKE_ASYNC(src, PROC_REF(attempt_recipe), structure, user, stack, recipe, is_material_recipe) + +/// Has the user attempt the wall recipe. +/// Assumes that the structure and stack are of valid types for the recipe. +/datum/element/uses_girder_wall_recipes/proc/attempt_recipe(obj/structure/structure, mob/living/user, obj/item/stack/stack, datum/girder_wall_recipe/recipe, is_material_recipe) + if (!check_recipe(structure, user, recipe)) + return + if (!stack.tool_start_check(user, recipe.stack_amount)) + return + + user.visible_message( + message = span_notice("\The [user] start[user.p_s()] building a wall on \the [structure]."), + self_message = span_notice("You start building a wall on \the [structure]."), + blind_message = span_hear("You hear a series of clangs."), + ) + + structure.add_fingerprint(user) + stack.add_fingerprint(user) + + if (!stack.use_tool(structure, user, recipe.make_delay, recipe.stack_amount, extra_checks = CALLBACK(src, PROC_REF(check_recipe), structure, user, recipe))) + return + + var/atom/wall + if (ispath(recipe.wall_type, /turf)) + var/turf/structure_turf = get_turf(structure) + wall = structure_turf.place_on_top(recipe.wall_type) + else if (ispath(recipe.wall_type, /obj)) + wall = new recipe.wall_type(structure.drop_location()) + else + CRASH("Attempted a girder wall recipe with an invalid wall type ([recipe.wall_type])") + + user.visible_message( + message = span_notice("\The [user] finish[user.p_es()] building \a [wall] on \the [structure]."), + self_message = span_notice("You finish building \a [wall] on \the [structure]."), + ) + + structure.transfer_fingerprints_to(wall) + qdel(structure) + + if (is_material_recipe) + wall.set_custom_materials(stack.mats_per_unit, recipe.stack_amount) + +/// Checks if the user can do the wall recipe. +/datum/element/uses_girder_wall_recipes/proc/check_recipe(obj/structure/structure, mob/living/user, datum/girder_wall_recipe/recipe) + if(iswallturf(structure.loc) || (locate(/obj/structure/falsewall) in structure.loc.contents)) + structure.balloon_alert(user, "wall already present!") + return FALSE + if (!ispath(recipe.wall_type, /obj/structure/tram)) + if (!isfloorturf(structure.loc)) + structure.balloon_alert(user, "need floor!") + return FALSE + else if (!(locate(/obj/structure/transport/linear/tram) in structure.loc.contents)) + structure.balloon_alert(user, "need tram floor!") + return FALSE + if (!check_girder_state(structure, recipe)) + return FALSE + return TRUE + +/// Checks if the girder state of the structure matches the required girder state of the wall recipe. +/datum/element/uses_girder_wall_recipes/proc/check_girder_state(obj/structure/structure, datum/girder_wall_recipe/recipe) + if (istype(structure, /obj/structure/girder)) + var/obj/structure/girder/girder = structure + if (girder.state != recipe.girder_state) + return FALSE + return TRUE diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index da25d13a0b84..5b61f09c31e5 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -10,6 +10,7 @@ * */ /datum/emote + abstract_type = /datum/emote /// What calls the emote. var/key = "" /// This will also call the emote. @@ -50,6 +51,7 @@ var/stat_allowed = CONSCIOUS /// Sound to play when emote is called. var/sound + var/extra_range = 0 // DARKPACK EDIT ADD /// Does this emote vary in pitch? var/vary = FALSE /// If this emote's sound is affected by TTS pitch @@ -119,7 +121,7 @@ frequency = rand(MIN_EMOTE_PITCH, MAX_EMOTE_PITCH) * (1 + sqrt(abs(user.pitch)) * SIGN(user.pitch) * EMOTE_TTS_PITCH_MULTIPLIER) else if(vary) frequency = rand(MIN_EMOTE_PITCH, MAX_EMOTE_PITCH) - playsound(source = user,soundin = tmp_sound,vol = 50, vary = FALSE, ignore_walls = sound_wall_ignore, frequency = frequency) + playsound(source = user,soundin = tmp_sound,vol = 50, vary = FALSE, extrarange = get_range(user), ignore_walls = sound_wall_ignore, frequency = frequency) // DARKPACK EDIT CHANGE - (Added extrarange getter) var/is_important = running_emote_type & EMOTE_IMPORTANT @@ -136,7 +138,7 @@ if(isnull(viewer.client)) continue if(!is_important && viewer != user && (!is_visual || !is_audible)) - if(is_audible && !viewer.can_hear()) + if(is_audible && HAS_TRAIT(viewer, TRAIT_DEAF)) continue if(is_visual && viewer.is_blind()) continue @@ -247,6 +249,11 @@ /datum/emote/proc/get_sound(mob/living/user) return sound //by default just return this var. +// DARKPACK EDIT ADD START +/datum/emote/proc/get_range() + return extra_range +// DARKPACK EDIT ADD END + /** * To get the flags visible/audible messages for ran by the emote. * diff --git a/code/datums/girder_wall_recipe.dm b/code/datums/girder_wall_recipe.dm new file mode 100644 index 000000000000..4e0845ffc359 --- /dev/null +++ b/code/datums/girder_wall_recipe.dm @@ -0,0 +1,27 @@ +/// Contains recipe data for constructing a specific type of wall on a girder. +/// Only the [/datum/element/uses_girder_wall_recipes] should be using these. +/datum/girder_wall_recipe + /// The wall type this recipe is for. Can be a turf or an object. + var/wall_type + /// The type of stack required for this recipe. + var/stack_type + /// The amount of stack items required for this recipe. + var/stack_amount + /// The [/obj/structure] type required for this recipe. + var/girder_type + /// The [/obj/structure/girder/var/state] required for this recipe. + /// Ignored if [var/girder] is not of type [/obj/structure/girder]. + var/girder_state + /// The amount of time it takes to make this recipe in deciseconds. + var/make_delay + /// The starting balloon alert text for this recipe. + var/start_alert + +/datum/girder_wall_recipe/New(wall_type, stack_type, stack_amount, girder_type, girder_state, make_delay, start_alert) + src.wall_type = wall_type + src.stack_type = stack_type + src.stack_amount = stack_amount + src.girder_type = girder_type + src.girder_state = girder_state + src.make_delay = make_delay + src.start_alert = start_alert diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm index adb676acc2ca..20ad08a77b63 100644 --- a/code/datums/helper_datums/getrev.dm +++ b/code/datums/helper_datums/getrev.dm @@ -41,6 +41,26 @@ return msg.Join("\n") +/datum/getrev/proc/GetDatabaseCommitSha() + . = originmastercommit + if (commit == .) + return . + + var/gh_url = CONFIG_GET(string/githuburl) + if(!gh_url) + return . + + var/datum/http_request/request = new() + request.prepare(RUSTG_HTTP_METHOD_GET, "[gh_url]/commit/[commit]", "", "") + request.begin_async() + UNTIL(request.is_complete()) + var/datum/http_response/response = request.into_response() + + if (response.status_code >= 200 && response.status_code < 300) + return commit + + return . + /datum/getrev/proc/GetTestMergeInfo(header = TRUE) if(!testmerge.len) return "" diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 9983761fa91b..4d0b715d762c 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -46,8 +46,7 @@ // if effects are not specified and not explicitly disabled, sparks if((!effectin || !effectout) && !no_effects) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(5, 1, teleatom) + var/datum/effect_system/basic/spark_spread/sparks = new(teleatom, 5, TRUE) if (!effectin) effectin = sparks if (!effectout) @@ -55,8 +54,7 @@ if(TELEPORT_CHANNEL_QUANTUM) // if effects are not specified and not explicitly disabled, rainbow sparks if ((!effectin || !effectout) && !no_effects) - var/datum/effect_system/spark_spread/quantum/sparks = new - sparks.set_up(5, 1, teleatom) + var/datum/effect_system/basic/spark_spread/quantum/sparks = new(teleatom, 5, TRUE) if (!effectin) effectin = sparks if (!effectout) @@ -127,8 +125,7 @@ if(sound) playsound(location, sound, 60, TRUE) if(effect) - effect.attach(location) - effect.start() + effect.attach(location).start() /** * Attempts to find a "safe" floor turf within some given z-levels diff --git a/code/datums/map_config.dm b/code/datums/map_config.dm index 820fd36ca783..f6e6685deeb6 100644 --- a/code/datums/map_config.dm +++ b/code/datums/map_config.dm @@ -66,6 +66,9 @@ /// Boolean that tells SSmapping to load all away missions in the codebase. var/load_all_away_missions = FALSE + /// Number of additional weakpoints to spawn for SSminor_mapping + var/bonus_weakpoints = 0 + var/max_npcs = 0 // DARKPACK EDIT ADD - NPC var/umbra_map_path // DARKPACK EDIT ADD - UMBRA var/umbra_map_file // DARKPACK EDIT ADD - UMBRA @@ -222,6 +225,10 @@ if ("give_players_hooks" in json) give_players_hooks = json["give_players_hooks"] + if ("bonus_weakpoints" in json) + bonus_weakpoints = json["bonus_weakpoints"] + + allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE if ("job_changes" in json) diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm index cf7a34772f54..b89ff361621a 100644 --- a/code/datums/mind/_mind.dm +++ b/code/datums/mind/_mind.dm @@ -101,6 +101,12 @@ /// A list to keep track of which books a person has read (to prevent people from reading the same book again and again for positive mood events) var/list/book_titles_read + /// How desensitized are we to death - multiplier to magnitude of death moodlet. + /// Doesn't go beneath 0.1 (but could go above 1.0 if you really wanted) + var/desensitized_level = 1 + /// Counts how many humanoid deaths we've seen + var/deaths_witnessed = 0 + /datum/mind/New(_key) key = _key init_known_skills() @@ -211,9 +217,9 @@ new_character.client.init_verbs() // re-initialize character specific verbs SEND_SIGNAL(src, COMSIG_MIND_TRANSFERRED, old_current) - SEND_SIGNAL(current, COMSIG_MOB_MIND_TRANSFERRED_INTO, old_current) + SEND_SIGNAL(current, COMSIG_MOB_MIND_TRANSFERRED_INTO, old_current, src) if(!isnull(old_current)) - SEND_SIGNAL(old_current, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, current) + SEND_SIGNAL(old_current, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, current, src) //I cannot trust you fucks to do this properly /datum/mind/proc/set_original_character(new_original_character) @@ -543,3 +549,20 @@ work_areas += dep.primary_work_area return work_areas + +/// Called when we witness the death of a humanoid mob. +/datum/mind/proc/witnessed_death(mob/living/dead_mob) + if(HAS_TRAIT(dead_mob, TRAIT_SPAWNED_MOB) || !ishuman(dead_mob) || (dead_mob.flags_1 & ADMIN_SPAWNED_1)) + return + + // every humanoid death gives us % resistance to the next one + desensitized_level = max(desensitized_level - DESENSITIZED_REDUCTION_PER_DEATH, DESENSITIZED_MINIMUM) + deaths_witnessed += 1 + + // if you manage to gain 90% resistance to death moodlets in one shift, you get an "achievement" + if(deaths_witnessed * DESENSITIZED_REDUCTION_PER_DEATH >= (1.0 - DESENSITIZED_MINIMUM)) + current.client?.give_award(/datum/award/achievement/misc/desensitized, current) + +/// Called when this mob is killed, but not gibbed or dusted +/datum/mind/proc/experienced_death() + witnessed_death(current) // you get desensitized for your own death! diff --git a/code/datums/mood_events/death.dm b/code/datums/mood_events/death.dm index 8f64808da330..3101b3dea96e 100644 --- a/code/datums/mood_events/death.dm +++ b/code/datums/mood_events/death.dm @@ -5,6 +5,7 @@ #define PET_PRIORITY 30 #define XENO_PRIORITY 35 #define DONTCARE_PRIORITY 40 +#define ASHWALKER_PRIORITY 50 #define GAMER_PRIORITY 80 #define REVOLUTIONARY_PRIORITY 85 #define CULT_PRIORITY 90 @@ -25,15 +26,18 @@ return TRUE /datum/mood_event/conditional/see_death/add_effects(mob/dead_mob, dusted, gibbed) - update_effect(dead_mob) + update_effect(dead_mob, dusted, gibbed) if(HAS_TRAIT(dead_mob, TRAIT_SPAWNED_MOB)) mood_change *= 0.25 timeout *= 0.2 - if(HAS_PERSONALITY(owner, /datum/personality/compassionate) && mood_change < 0) - mood_change *= 1.5 - timeout *= 1.5 + if(mood_change < 0) + mood_change = ceil(mood_change * max(DESENSITIZED_MINIMUM, owner.mind?.desensitized_level || 1.0)) + + if(HAS_PERSONALITY(owner, /datum/personality/compassionate)) + mood_change *= 1.5 + timeout *= 1.5 if(gibbed || dusted) mood_change *= 1.2 @@ -50,7 +54,7 @@ description = capitalize(replacetext(description, "%DEAD_MOB%", get_descriptor(dead_mob))) /// Blank proc which allows conditional effects to modify mood, timeout, or description before the main effect is applied -/datum/mood_event/conditional/see_death/proc/update_effect(mob/dead_mob) +/datum/mood_event/conditional/see_death/proc/update_effect(mob/dead_mob, dusted, gibbed) return /// Checks if the dead mob is a pet @@ -71,7 +75,7 @@ /// Checks if our mood can get worse by seeing another death (or better if we're weird like that) /datum/mood_event/conditional/see_death/proc/can_stack_effect(mob/dead_mob) // if we're desensitized, don't stack unless it's a buff - if(HAS_MIND_TRAIT(owner, TRAIT_DESENSITIZED) && mood_change > 0) + if(IS_DESENSITIZED(owner) && mood_change < 0) return FALSE // if we're seeing a spawned mob die, don't stack if(HAS_TRAIT(dead_mob, TRAIT_SPAWNED_MOB)) @@ -94,7 +98,7 @@ /datum/mood_event/conditional/see_death/naive/condition_fulfilled(mob/living/who, mob/dead_mob, dusted, gibbed) return HAS_MIND_TRAIT(who, TRAIT_NAIVE) && !dusted && !gibbed -/datum/mood_event/conditional/see_death/naive/update_effect(mob/dead_mob) +/datum/mood_event/conditional/see_death/naive/update_effect(mob/dead_mob, dusted, gibbed) description = "Have a good nap, [get_descriptor(dead_mob)]." /// Cultists are super brainwashed so they get buffs instead @@ -118,7 +122,7 @@ /datum/mood_event/conditional/see_death/revolutionary/condition_fulfilled(mob/living/who, mob/dead_mob, dusted, gibbed) return IS_REVOLUTIONARY(who) && (dead_mob.mind?.assigned_role.job_flags & JOB_HEAD_OF_STAFF) -/datum/mood_event/conditional/see_death/revolutionary/update_effect(mob/dead_mob) +/datum/mood_event/conditional/see_death/revolutionary/update_effect(mob/dead_mob, dusted, gibbed) var/datum/job/possible_head_job = dead_mob.mind?.assigned_role description = "[possible_head_job.title ? "The [LOWER_TEXT(possible_head_job.title)]" : "Another head of staff"] is dead! Long live the revolution!" @@ -152,6 +156,23 @@ else description = "Oh, %DEAD_MOB% died. Shame, I guess." +/// Ashwalkers get a small boost from sacrificing people to the necropolis spire, and don't care otherwise +/datum/mood_event/conditional/see_death/ashwalker + priority = ASHWALKER_PRIORITY + mood_change = 0 + +/datum/mood_event/conditional/see_death/ashwalker/condition_fulfilled(mob/living/who, mob/dead_mob, dusted, gibbed) + return HAS_TRAIT(who, TRAIT_NECROPOLIS_WORSHIP) && !HAS_TRAIT(dead_mob, TRAIT_NECROPOLIS_WORSHIP) + +/datum/mood_event/conditional/see_death/ashwalker/update_effect(mob/dead_mob, dusted, gibbed) + if(gibbed) + description = "%DEAD_MOB% hasss been torn asssunder, glory to the Necropolisss!" + mood_change = /datum/mood_event/conditional/see_death::mood_change * -0.5 + else if(dusted) + description = "Oh, %DEAD_MOB% wasss vaporized." + else + description = "Oh, %DEAD_MOB% died. Ssshame, I guesss." + /// Pets take priority over normal death moodlets /datum/mood_event/conditional/see_death/pet priority = PET_PRIORITY @@ -257,11 +278,10 @@ /// Desensitized brings up the rear /datum/mood_event/conditional/see_death/desensitized priority = DESENSITIZED_PRIORITY - mood_change = parent_type::mood_change * 0.5 timeout = parent_type::timeout * 0.5 /datum/mood_event/conditional/see_death/desensitized/condition_fulfilled(mob/living/who, mob/dead_mob, dusted, gibbed) - return HAS_MIND_TRAIT(who, TRAIT_DESENSITIZED) + return IS_DESENSITIZED(who) /datum/mood_event/conditional/see_death/desensitized/update_effect(mob/dead_mob, dusted, gibbed) if(gibbed) @@ -276,6 +296,7 @@ #undef PET_PRIORITY #undef XENO_PRIORITY #undef DONTCARE_PRIORITY +#undef ASHWALKER_PRIORITY #undef GAMER_PRIORITY #undef REVOLUTIONARY_PRIORITY #undef CULT_PRIORITY diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 8102d9282122..665857195f19 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -638,6 +638,8 @@ mood_change = 0 description = "I just got coated in blood. Fascinating!" return + if(IS_DESENSITIZED(owner)) + mood_change *= 0.5 /datum/mood_event/teetotal_hangover description = "What a disgraceful display! This is what happens when one indulges in alcohol!" diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm index 4fe07773ca87..c20046d3fe2f 100644 --- a/code/datums/mutations/body.dm +++ b/code/datums/mutations/body.dm @@ -701,7 +701,9 @@ /datum/mutation/inexorable/on_life(seconds_per_tick) if(owner.health > owner.crit_threshold || owner.stat != CONSCIOUS || HAS_TRAIT(owner, TRAIT_STASIS)) return - // Gives you 30 seconds of being in soft crit... give or take + if(HAS_TRAIT(owner, TRAIT_NOCRITDAMAGE) && owner.health <= owner.hardcrit_threshold + 10) + return + // Gives you 30 seconds of being in fake soft crit... give or take if(HAS_TRAIT(owner, TRAIT_TOXIMMUNE) || HAS_TRAIT(owner, TRAIT_TOXINLOVER)) owner.adjust_brute_loss(1 * seconds_per_tick * GET_MUTATION_SYNCHRONIZER(src), forced = TRUE) else diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm index 7d03dbe0f44d..69bf447a04e1 100644 --- a/code/datums/quirks/_quirk.dm +++ b/code/datums/quirks/_quirk.dm @@ -1,6 +1,7 @@ //every quirk in this folder should be coded around being applied on spawn //these are NOT "mob quirks" like GOTTAGOFAST, but exist as a medium to apply them and other different effects /datum/quirk + abstract_type = /datum/quirk /// The name of the quirk var/name = "Test Quirk" /// The description of the quirk @@ -26,8 +27,6 @@ /// This is used to pick the quirks assigned to a hardcore character. //// 0 means its not available to hardcore draws. var/hardcore_value = 0 - /// When making an abstract quirk (in OOP terms), don't forget to set this var to the type path for that abstract quirk. - var/abstract_parent_type = /datum/quirk /// The icon to show in the preferences menu. /// This references a tgui icon, so it can be FontAwesome or a tgfont (with a tg- prefix). var/icon @@ -221,7 +220,7 @@ var/list/where_items_spawned /// If true, the backpack automatically opens on post_add(). Usually set to TRUE when an item is equipped inside the player's backpack. var/open_backpack = FALSE - abstract_parent_type = /datum/quirk/item_quirk + abstract_type = /datum/quirk/item_quirk /** * Handles inserting an item in any of the valid slots provided, then allows for post_add notification. diff --git a/code/datums/quirks/negative_quirks/addict.dm b/code/datums/quirks/negative_quirks/addict.dm index b1a51f689fda..74e2eda69166 100644 --- a/code/datums/quirks/negative_quirks/addict.dm +++ b/code/datums/quirks/negative_quirks/addict.dm @@ -3,7 +3,7 @@ desc = "You are addicted to something that doesn't exist. Suffer." gain_text = span_danger("You suddenly feel the craving for... something? You're not sure what it is.") medical_record_text = "Patient has a history with SOMETHING but he refuses to tell us what it is." - abstract_parent_type = /datum/quirk/item_quirk/addict + abstract_type = /datum/quirk/item_quirk/addict quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_PROCESSES no_process_traits = list(TRAIT_LIVERLESS_METABOLISM) var/datum/reagent/reagent_type //!If this is defined, reagent_id will be unused and the defined reagent type will be instead. diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index 4fb240638ee9..ac164084b368 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -756,4 +756,12 @@ report_message = "Due to a mishap at the Robust Softdrinks Megafactory, some drinks may contain traces of ethanol or psychoactive chemicals." trait_to_give = STATION_TRAIT_SPIKED_DRINKS +/datum/station_trait/structural_weakness + name = "Structural Weaknesses" + trait_type = STATION_TRAIT_NEGATIVE + weight = 5 + show_in_report = TRUE + report_message = "Our station subdivision informed us that this station may have been built with a number of structural weaknesses due to defective construction materials. Be on the lookout for them and try not to let anything explode." + trait_to_give = STATION_TRAIT_SPAWN_WEAKPOINTS + #undef GLOW_NEBULA diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 750bfba732f6..50a2e2292733 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -369,7 +369,7 @@ status_type = STATUS_EFFECT_REFRESH /datum/status_effect/good_music/tick(seconds_between_ticks) - if(owner.can_hear()) + if(!HAS_TRAIT(owner, TRAIT_DEAF)) owner.adjust_dizzy(-4 SECONDS) owner.adjust_jitter(-4 SECONDS) owner.adjust_confusion(-1 SECONDS) @@ -675,3 +675,44 @@ desc = "Bathed in soothing darkness, you will slowly heal yourself" use_user_hud_icon = TRUE overlay_state = "lightless" + +/// Applies desensitized mood modifier to the mob, carrying between mind transfers +/datum/status_effect/desensitized + id = "desensitized" + duration = STATUS_EFFECT_PERMANENT + status_type = STATUS_EFFECT_MULTIPLE + alert_type = null + /// How much to multiply desensitization level by + var/magnitude = 1.0 + /// Effect ID for removal purposes + var/effect_id + +/datum/status_effect/desensitized/on_creation(mob/living/new_owner, effect_id, magnitude) + src.effect_id = effect_id + src.magnitude = max(DESENSITIZED_MINIMUM, magnitude) + return ..() + +/datum/status_effect/desensitized/on_apply() + owner.mind?.desensitized_level *= magnitude + RegisterSignal(owner, COMSIG_MOB_MIND_TRANSFERRED_INTO, PROC_REF(add_magnitude)) + RegisterSignal(owner, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, PROC_REF(remove_magnitude)) + return TRUE + +/datum/status_effect/desensitized/on_remove() + owner.mind?.desensitized_level /= magnitude + UnregisterSignal(owner, list(COMSIG_MOB_MIND_TRANSFERRED_INTO, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF)) + +/datum/status_effect/desensitized/before_remove(effect_id, magnitude) + if(istext(src.effect_id) && istext(effect_id)) // if an id is set, they must match + return src.effect_id == effect_id + if(isnum(magnitude)) // otherwise if a magnitude is passed, it must match + return src.magnitude == magnitude + return FALSE + +/datum/status_effect/desensitized/proc/add_magnitude(datum/source, mob/living/old_body, datum/mind/swapping) + SIGNAL_HANDLER + swapping.desensitized_level *= magnitude + +/datum/status_effect/desensitized/proc/remove_magnitude(datum/source, mob/living/old_body, datum/mind/swapping) + SIGNAL_HANDLER + swapping.desensitized_level /= magnitude diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 556a8e2e4dcb..5d390a8035bb 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -604,7 +604,7 @@ /datum/status_effect/trance/proc/hypnotize(datum/source, list/hearing_args) SIGNAL_HANDLER - if(!owner.can_hear() || owner == hearing_args[HEARING_SPEAKER]) + if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER]) return var/mob/hearing_speaker = hearing_args[HEARING_SPEAKER] diff --git a/code/datums/storage/subtypes/belts.dm b/code/datums/storage/subtypes/belts.dm index e981e7e1ac2a..763131eb98ea 100644 --- a/code/datums/storage/subtypes/belts.dm +++ b/code/datums/storage/subtypes/belts.dm @@ -305,6 +305,50 @@ . = ..() set_holdable(/obj/item/claymore/gladius) +///Katana sheath +/datum/storage/katana_sheath + max_slots = 1 + do_rustle = FALSE + max_specific_storage = WEIGHT_CLASS_BULKY + click_alt_open = FALSE + +/datum/storage/katana_sheath/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(/obj/item/katana) + +///Ninja energy katana sheath +/datum/storage/ninja_sheath + max_slots = 1 + do_rustle = FALSE + max_specific_storage = WEIGHT_CLASS_BULKY + click_alt_open = FALSE + +/datum/storage/ninja_sheath/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(/obj/item/energy_katana) + +///Chaplain katana sheath +/datum/storage/hanzo_sheath + max_slots = 1 + do_rustle = FALSE + max_specific_storage = WEIGHT_CLASS_BULKY + click_alt_open = FALSE + +/datum/storage/hanzo_sheath/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(/obj/item/nullrod/claymore/katana) + +///Toy Katana Sheath(Metabreaker) +/datum/storage/toy_sheath + max_slots = 1 + do_rustle = FALSE + max_specific_storage = WEIGHT_CLASS_BULKY + click_alt_open = FALSE + +/datum/storage/toy_sheath/New(atom/parent, max_slots, max_specific_storage, max_total_storage, rustle_sound, remove_rustle_sound) + . = ..() + set_holdable(/obj/item/toy/katana) + ///Plant belt /datum/storage/plant_belt max_slots = 6 diff --git a/code/datums/storage/subtypes/pockets.dm b/code/datums/storage/subtypes/pockets.dm index 207722746743..65941cdad10d 100644 --- a/code/datums/storage/subtypes/pockets.dm +++ b/code/datums/storage/subtypes/pockets.dm @@ -218,7 +218,6 @@ ///Void cloak pocket /datum/storage/pockets/void_cloak - quickdraw = TRUE max_total_storage = 5 // 2 small items + 1 tiny item, or 1 normal item + 1 small item max_slots = 3 diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm index 1b7014320769..a49c7cb6c340 100644 --- a/code/datums/voice_of_god_command.dm +++ b/code/datums/voice_of_god_command.dm @@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands()) var/to_remove_string var/list/candidates = get_hearers_in_view(8, user) - (include_speaker ? null : user) for(var/mob/living/candidate in candidates) - if(candidate.stat != DEAD && candidate.can_hear()) + if(candidate.stat != DEAD && !HAS_TRAIT(candidate, TRAIT_DEAF)) if(candidate.can_block_magic(MAGIC_RESISTANCE_HOLY|MAGIC_RESISTANCE_MIND, charge_cost = 0)) to_chat(user, span_userdanger("Something's wrong! [candidate] seems to be resisting your commands.")) continue diff --git a/code/datums/wires/wire_bundle_component.dm b/code/datums/wires/wire_bundle_component.dm index 5cd4ffa2af62..0fb0e91998df 100644 --- a/code/datums/wires/wire_bundle_component.dm +++ b/code/datums/wires/wire_bundle_component.dm @@ -11,7 +11,7 @@ CRASH("Holder does not have a shell component!") var/wire_count = clamp(round(shell_comp.capacity / CAPACITY_PER_WIRE, 1), 1, MAX_WIRE_COUNT) for(var/index in 1 to wire_count) - wires += "Port [index]" + LAZYADD(wires, "Port [index]") ..() /datum/wires/wire_bundle_component/always_reveal_wire(color) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index a5a22ec53473..cdaa2ea434c9 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -410,6 +410,10 @@ /atom/proc/is_drainable() return reagents && (reagents.flags & DRAINABLE) +/// Can we dunk stuff into this container? +/atom/proc/is_dunkable() + return reagents && (reagents.flags & DUNKABLE) + /** Handles exposing this atom to a list of reagents. * * Sends COMSIG_ATOM_EXPOSE_REAGENTS diff --git a/code/game/atom/atom_defense.dm b/code/game/atom/atom_defense.dm index edb5623aec82..f582462a1c2d 100644 --- a/code/game/atom/atom_defense.dm +++ b/code/game/atom/atom_defense.dm @@ -10,6 +10,10 @@ var/integrity_failure = 0 //0 if we have no special broken behavior, otherwise is a percentage of at what point the atom breaks. 0.5 being 50% ///Damage under this value will be completely ignored var/damage_deflection = 0 + // DARKPACK EDIT ADD START + /// Atom uses integrity but will not be deleted upon reaching 0 through normal means + var/prevent_destruction = FALSE + // DARKPACK EDIT ADD END var/resistance_flags = NONE // INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ON_FIRE | UNACIDABLE | ACID_PROOF @@ -19,8 +23,12 @@ CRASH("[src] had /atom/proc/take_damage() called on it without it being a type that has uses_integrity = TRUE!") if(QDELETED(src)) CRASH("[src] taking damage after deletion") - if(atom_integrity <= 0) + // DARKPACK EDIT CHANGE START + if(atom_integrity <= 0 && !prevent_destruction) CRASH("[src] taking damage while having <= 0 integrity") + else if(atom_integrity < 0) + CRASH("[src] taking damage while having < 0 integrity") + // DARKPACK EDIT CHANGE END if(sound_effect) play_attack_sound(damage_amount, damage_type, damage_flag) if(resistance_flags & INDESTRUCTIBLE) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index fdf6806046a9..6dd1f6a4b129 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -153,6 +153,14 @@ ///What was our power state the last time we updated its appearance? ///TRUE for on, FALSE for off, -1 for never checked var/appearance_power_state = -1 + // DARKPACK EDIT ADD START - STORYTELER_STATS + /// Stat define/typepath required for use of this device. No check if null + var/datum/st_stat/skill_required_for_use + // 0 minimum means you have to have an active DEBUFF rather then just no dots. + // As this would otherwise be insanely frustating to apply to all devices and not representive of the tech skill. + /// You need ATLEAST this many dots in a skill to use. + var/skill_dots_minimum = 0 + // DARKPACK EDIT ADD END /datum/armor/obj_machinery melee = 25 @@ -690,53 +698,36 @@ return TRUE // If we passed all of those checks, woohoo! We can interact with this machine. -/** - * Checks for NAP non aggression principle, an anarcho capitalist event triggered by admins - * where using machines cost money - */ -/obj/machinery/proc/check_nap_violations() - PROTECTED_PROC(TRUE) - SHOULD_NOT_OVERRIDE(TRUE) - - if(!SSeconomy.full_ancap) - return TRUE - if(!occupant || state_open) - return TRUE - var/mob/living/occupant_mob = occupant - var/obj/item/card/id/occupant_id = occupant_mob.get_idcard(TRUE) - if(!occupant_id) - say("Customer NAP Violation: No ID card found.") - nap_violation(occupant_mob) - return FALSE - var/datum/bank_account/insurance = occupant_id.registered_account - if(!insurance) - say("Customer NAP Violation: No bank account found.") - nap_violation(occupant_mob) - return FALSE - if(!insurance.adjust_money(-fair_market_price)) - say("Customer NAP Violation: Unable to pay.") - nap_violation(occupant_mob) - return FALSE - var/datum/bank_account/department_account = SSeconomy.get_dep_account(payment_department) - if(department_account) - department_account.adjust_money(fair_market_price) - return TRUE - -/** - * Actions to take in case of NAP violation - * Arguments - * - * * mob/violator - the mob who violated the NAP aggrement - */ -/obj/machinery/proc/nap_violation(mob/violator) - PROTECTED_PROC(TRUE) - - return - //////////////////////////////////////////////////////////////////////////////////////////// //Return a non FALSE value to interrupt attack_hand propagation to subtypes. /obj/machinery/interact(mob/user) + // DARKPACK EDIT ADD START - STORYTELER_STATS + // Likely worth making a dice roll after #633 + if(isliving(user) && skill_required_for_use) + var/mob/living/living_user = user + var/bad_at_device = FALSE + + var/dots = living_user.st_get_stat(skill_required_for_use) + if(dots < skill_dots_minimum) + to_chat(user, span_warning("[src] requires atleast [skill_dots_minimum] dots in [skill_required_for_use::name] for proper use.")) + bad_at_device = TRUE + if(CONFIG_GET(flag/punishing_zero_dots) && dots <= 0) + return + + /* I cant verify the lore accuracy of "rejection past your embrace age" and we dont have a invention date for tech to represent it either + if(skill_dots_minimum > 0 && HAS_TRAIT(user, TRAIT_REJECTED_BY_TECHNOLOGY)) + if(skill_required_for_use in list(STAT_COMPUTER, STAT_TECHNOLOGY)) + bad_at_device = TRUE + */ + + if(bad_at_device) + to_chat(user, span_warning("You start interacting with [src]. Confounded device...")) + if(!do_after(user, 1 TURNS, src)) + to_chat(user, span_warning("Bah! You didn't need [src] anyways.")) + return TRUE + // DARKPACK EDIT ADD END + update_last_used(user) return ..() diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 08cd5bdc61a1..bca3a66c2a83 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -45,8 +45,7 @@ if(!COOLDOWN_FINISHED(src, foam_cooldown)) to_chat(user, span_warning("[src] cannot be activated for [DisplayTimeText(COOLDOWN_TIMELEFT(src, foam_cooldown))]!")) return - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(4, holder = src, location = loc) + var/datum/effect_system/fluid_spread/foam/foam = new(loc, 4, holder = src) foam.start() uses-- to_chat(user, span_notice("You activate [src]. It now has [uses] uses of foam remaining.")) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 92e6c9943059..58800d063983 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -518,9 +518,7 @@ return FALSE if(!prob(prb)) return FALSE - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() + do_sparks(5, TRUE, src) return electrocute_mob(user, get_area(src), src, 0.7, TRUE) /** diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index 6aa336834121..502c664c17db 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -7,6 +7,11 @@ integrity_failure = 0.5 armor_type = /datum/armor/machinery_computer interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_LITERACY + // DARKPACK EDIT ADD START - STORYTELER_STATS + skill_required_for_use = STAT_COMPUTER + // V20 p. 108 says for 1 dot "Student: You can navigate touch-screen and traditional point-and-click GUIs" + skill_dots_minimum = 1 + // DARKPACK EDIT ADD END /// How bright we are when turned on. var/brightness_on = 1 /// Icon_state of the keyboard overlay. diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm index 21039012dd28..11ef5e4a8d1d 100644 --- a/code/game/machinery/computer/arcade/battle.dm +++ b/code/game/machinery/computer/arcade/battle.dm @@ -360,7 +360,7 @@ SStgui.update_uis(src) return //we couldn't heal ourselves or steal MP, we'll just attack instead. - var/skill_level = user.st_get_stat(STAT_TECHNOLOGY) || 1 // DARKPACK EDIT CHANGE - STORYTELLR_STATS + var/skill_level = user.st_get_stat(STAT_TECHNOLOGY) || 1 // DARKPACK EDIT CHANGE - STORYTELLER_STATS var/chance_at_counterattack = 40 + (skill_level * 5) //at level 1 this is 45, at legendary this is 75 var/damage_dealt = (defending_flags & BATTLE_ATTACK_FLAG_DEFEND) ? rand(5, 10) : rand(15, 20) if((defending_flags & BATTLE_ATTACK_FLAG_COUNTERATTACK) && prob(chance_at_counterattack)) diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm index e31d028d95f1..688bcca26713 100644 --- a/code/game/machinery/computer/operating_computer.dm +++ b/code/game/machinery/computer/operating_computer.dm @@ -10,6 +10,10 @@ interaction_flags_machine = parent_type::interaction_flags_machine | INTERACT_MACHINE_REQUIRES_STANDING light_color = LIGHT_COLOR_GREEN + // DARKPACK EDIT ADD START - STORYTELER_STATS + skill_required_for_use = STAT_MEDICINE + skill_dots_minimum = 1 + // DARKPACK EDIT ADD END /// Linked operating table, if any var/obj/structure/table/optable/table 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 06bd251368a9..53e585852945 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 @@ -105,3 +105,13 @@ name = "Mayonnaise" purchase_path = /obj/item/reagent_containers/condiment/mayonnaise cost_per_order = 30 + +/datum/orderable_item/reagents/mustard + name = "Mustard" + purchase_path = /obj/item/reagent_containers/condiment/mustard + cost_per_order = 30 + +/datum/orderable_item/reagents/ketchup + name = "Ketchup" + purchase_path = /obj/item/reagent_containers/condiment/ketchup + cost_per_order = 30 diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 228391e6bdee..0111a5c71b2e 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -155,9 +155,7 @@ var/turf/T = get_turf(drone) message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(drone)] at [ADMIN_VERBOSEJMP(T)]!") log_silicon("[key_name(usr)] detonated [key_name(drone)]!") - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, TRUE, drone) - s.start() + do_sparks(3, TRUE< drone) drone.visible_message(span_danger("\the [drone] self-destructs!")) drone.investigate_log("has been gibbed by a robotics console.", INVESTIGATE_DEATHS) drone.gib() diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 2fea89c46050..1601626e0443 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -81,32 +81,7 @@ AddElement(/datum/element/contextual_screentip_tools, tool_behaviors) register_context() -/obj/structure/barricade/wooden/item_interaction(mob/living/user, obj/item/stack/sheet/mineral/wood/our_wood, list/modifiers) - if(user.combat_mode) - return ITEM_INTERACT_SKIP_TO_ATTACK - - if(!istype(our_wood, /obj/item/stack/sheet/mineral/wood)) - return NONE - - if(our_wood.amount < 5) - balloon_alert(user, "not enough wood!") - to_chat(user, span_warning("You need at least five wooden planks to make a barricade!")) - return ITEM_INTERACT_BLOCKING - - - to_chat(user, span_notice("You start adding [our_wood] to [src]...")) - playsound(src, 'sound/items/hammering_wood.ogg', 50, vary = TRUE) - if(!do_after(user, 5 SECONDS, target=src)) - balloon_alert(user, "interrupted!") - return ITEM_INTERACT_BLOCKING - - if(!our_wood.use(drop_amount)) - return ITEM_INTERACT_BLOCKING - - var/turf/cur_turf = get_turf(src) - cur_turf.place_on_top(/turf/closed/wall/mineral/wood/nonmetal) - qdel(src) - return ITEM_INTERACT_SUCCESS + AddElement(/datum/element/uses_girder_wall_recipes) /obj/structure/barricade/wooden/crowbar_act(mob/living/user, obj/item/tool) balloon_alert(user, "deconstructing barricade...") diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 1951d84a775b..48c7b78fbc88 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -55,7 +55,7 @@ var/safe = TRUE ///whether the door is bolted or not. var/locked = FALSE - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system ///ignore this, just use explosion_block var/real_explosion_block ///if TRUE, this door will always open on red alert @@ -63,7 +63,7 @@ /// Whether or not the door can crush mobs. var/can_crush = TRUE - /// Whether or not the door can be opened by hand (used for blast doors and shutters) + /// Whether or not the door can be opened by hand (used for blast doors, shutters & firelocks primarily) var/can_open_with_hands = TRUE /// Whether or not this door can be opened through a door remote, ever var/opens_with_door_remote = FALSE @@ -120,8 +120,7 @@ GLOB.elevator_doors += src else stack_trace("Elevator door [src] ([x],[y],[z]) has no linked elevator ID!") - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(2, 1, src) + spark_system = new(src, 2, TRUE) if(density) flags_1 |= PREVENT_CLICK_UNDER_1 else @@ -178,9 +177,7 @@ /obj/machinery/door/Destroy() if(elevator_mode) GLOB.elevator_doors -= src - if(spark_system) - qdel(spark_system) - spark_system = null + QDEL_NULL(spark_system) QDEL_NULL(filler) air_update_turf(TRUE, FALSE) return ..() diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 7f4fffac25b1..ae6a3e002ee9 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -21,7 +21,7 @@ closingLayer = CLOSED_FIREDOOR_LAYER armor_type = /datum/armor/door_firedoor interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN - + can_open_with_hands = FALSE COOLDOWN_DECLARE(activation_cooldown) ///X offset for the overlay lights, so that they line up with the thin border firelocks @@ -472,9 +472,6 @@ return ..() return FALSE -/obj/machinery/door/firedoor/bumpopen(mob/living/user) - return FALSE //No bumping to open, not even in mechs - /obj/machinery/door/firedoor/proc/on_power_loss() SIGNAL_HANDLER diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index 285b19b6c4bb..35c967e0f28d 100644 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -151,7 +151,7 @@ var/id = null var/disable = 0 var/last_spark = 0 - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/sparker, 26) @@ -160,8 +160,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/sparker, 26) /obj/machinery/sparker/Initialize(mapload) . = ..() - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(2, 1, src) + spark_system = new(2, TRUE, src) spark_system.attach(src) register_context() if(mapload) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 8e400fc964a0..096a402df83b 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -53,7 +53,6 @@ reagents.add_reagent_list(internal_list_reagents) interaction_flags_machine |= INTERACT_MACHINE_OFFLINE register_context() - update_appearance(UPDATE_ICON) AddElement(/datum/element/noisy_movement) /obj/machinery/iv_drip/Destroy() diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm index e45daf7a98e5..646d5288c51f 100644 --- a/code/game/machinery/launch_pad.dm +++ b/code/game/machinery/launch_pad.dm @@ -184,9 +184,7 @@ if(!hidden) playsound(target, 'sound/items/weapons/flash.ogg', 25, TRUE) - var/datum/effect_system/spark_spread/quantum/spark_system = new /datum/effect_system/spark_spread/quantum() - spark_system.set_up(5, TRUE, target) - spark_system.start() + do_sparks(5, TRUE, target, spark_type = /datum/effect_system/basic/spark_spread/quantum) sleep(teleport_speed) diff --git a/code/game/machinery/mining_weather_monitor.dm b/code/game/machinery/mining_weather_monitor.dm index 32c41871b60d..c057b44f56cd 100644 --- a/code/game/machinery/mining_weather_monitor.dm +++ b/code/game/machinery/mining_weather_monitor.dm @@ -14,6 +14,7 @@ state_normal = "wallgreen", \ state_warning = "wallyellow", \ state_danger = "wallred", \ + radar_z_trait = ZTRAIT_MINING, \ ) /obj/machinery/mining_weather_monitor/update_overlays() @@ -23,3 +24,352 @@ . += emissive_appearance(icon, "emissive", src) MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/mining_weather_monitor, 28) + +/datum/armor/weather_tower + melee = 80 + bullet = 50 + laser = 50 + energy = 50 + fire = 100 + acid = 100 + bomb = 100 + +/datum/armor/weather_tower/constructed + melee = 50 + bullet = 30 + laser = 30 + energy = 30 + +GLOBAL_LIST_EMPTY(weather_towers) + +/obj/machinery/power/weather_tower + name = "doppler radar tower" + desc = "A tower that monitors atmospheric data from mining environments. Provides warnings about incoming weather fronts." + icon = 'icons/obj/mining_zones/terrain.dmi' + icon_state = "radar" + base_icon_state = "radar" + anchored = TRUE + density = TRUE + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF + armor_type = /datum/armor/weather_tower + max_integrity = 500 + move_resist = MOVE_FORCE_EXTREMELY_STRONG + idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION + processing_flags = START_PROCESSING_MANUALLY + custom_materials = list(/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 12) + + /// Whether the tower is active and functioning + var/active = FALSE + /// Reference to a core installed in the tower + var/obj/item/assembly/signaler/anomaly/weather/core + /// Cooldown between weather summons + COOLDOWN_DECLARE(summon_weather_cd) + /// Cooldown between weather clears + COOLDOWN_DECLARE(clear_weather_cd) + +/obj/machinery/power/weather_tower/Initialize(mapload) + . = ..() + if(anchored) + connect_to_network() + update_appearance() + SSmachines.processing_early += src + LAZYADD(GLOB.weather_towers["[src.z]"], src) + AddComponent(/datum/component/gps, "Radar Tower") + +/obj/machinery/power/weather_tower/Destroy() + LAZYREMOVE(GLOB.weather_towers["[src.z]"], src) + QDEL_NULL(core) + SSmachines.processing_early -= src + return ..() + +/obj/machinery/power/weather_tower/connect_to_network() + return anchored && ..() + +/obj/machinery/power/weather_tower/on_deconstruction(disassembled) + new /obj/item/stack/sheet/plasteel(loc, disassembled ? 12 : 4) + +/obj/machinery/power/weather_tower/Exited(atom/movable/gone, direction) + . = ..() + if(gone == core) + core = null + if(!QDELING(src)) + update_appearance() + +/obj/machinery/power/weather_tower/update_overlays() + . = ..() + if(active) + . += mutable_appearance(icon, "[base_icon_state]_on", alpha = src.alpha) + . += emissive_appearance(icon, "[base_icon_state]_em", src, alpha = src.alpha) + else if(anchored) + . += mutable_appearance(icon, "[base_icon_state]_off", alpha = src.alpha) + + if(core) + . += mutable_appearance(icon, "[base_icon_state]_core", alpha = src.alpha) + . += emissive_appearance(icon, "[base_icon_state]_core_em", src, alpha = src.alpha) + +/obj/machinery/power/weather_tower/update_name(updates) + . = ..() + if(isnull(core)) + name = initial(name) + else + name = "anomalous [initial(name)]" + +/obj/machinery/power/weather_tower/examine(mob/user) + . = ..() + if(isnull(core)) + . += span_info("It has a slot in which you could install a weather anomaly core.") + else + . += span_info("It has \a [core] installed, unlocking weather control.") + +/obj/machinery/power/weather_tower/ui_interact(mob/user, datum/tgui/ui) + if(isnull(core)) + return + + . = ..() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AnomalousWeatherTower") + ui.open() + +/obj/machinery/power/weather_tower/ui_status(mob/user, datum/ui_state/state) + if(isnull(core)) + return UI_CLOSE + if(!active) + return UI_DISABLED + return ..() + +/obj/machinery/power/weather_tower/ui_data(mob/user) + var/list/data = list() + + if(isnull(core)) + stack_trace("Tried to get weather tower UI data with no core installed!") + + data["core_charges"] = core?.charges + data["can_summon_weather"] = COOLDOWN_FINISHED(src, summon_weather_cd) + data["can_clear_weather"] = COOLDOWN_FINISHED(src, clear_weather_cd) + data["active_weather_on_z"] = list() + for(var/datum/weather/ongoing as anything in get_active_weather_on_z()) + data["active_weather_on_z"] += list(list( + "id" = REF(ongoing), + "name" = capitalize(ongoing.name), + "desc" = ongoing.desc, + )) + + return data + +/obj/machinery/power/weather_tower/ui_static_data(mob/user) + var/list/data = list() + var/list/summonable_weather_types = list() + for(var/datum/weather/weather_type as anything in get_summonable_weather_types()) + summonable_weather_types += list(list( + "id" = weather_type, + "name" = capitalize(weather_type::name), + "desc" = weather_type::desc, + )) + + data["summonable_weather_types"] = summonable_weather_types + data["weather_charge_cost"] = weather_charge_cost() + data["max_core_charge"] = /obj/item/assembly/signaler/anomaly/weather::charges + return data + +/obj/machinery/power/weather_tower/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + switch(action) + if("summon_weather") + summon_weather(text2path(params["weather_type"]), ui.user) // sanity checks in proc + return TRUE + + if("clear_weather") + clear_weather(params["weather_ref"], ui.user) // sanity checks in proc + return TRUE + +/obj/machinery/power/weather_tower/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/assembly/signaler/anomaly)) + if(!isnull(core)) + to_chat(user, span_warning("The weather core slot is already occupied.")) + return ITEM_INTERACT_FAILURE + + if(!istype(tool, /obj/item/assembly/signaler/anomaly/weather)) + to_chat(user, span_warning("[tool] probably won't do anything useful within [src].")) + return ITEM_INTERACT_FAILURE + + if(!user.transferItemToLoc(tool, src)) + to_chat(user, span_warning("You can't seem to part ways with [tool].")) + return ITEM_INTERACT_FAILURE + + core = tool + update_appearance() + to_chat(user, span_notice("You install [tool] into [src].")) + return ITEM_INTERACT_SUCCESS + + return NONE + +/obj/machinery/power/weather_tower/set_anchored(anchorvalue) + . = ..() + if(isnull(.)) + return + + if(anchored) + connect_to_network() + else + disconnect_from_network() + +/obj/machinery/power/weather_tower/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(old_turf) + LAZYREMOVE(GLOB.weather_towers["[old_turf.z]"], src) + if(new_turf) + LAZYADD(GLOB.weather_towers["[new_turf.z]"], src) + +/obj/machinery/power/weather_tower/process_early() + if(anchored && surplus() >= idle_power_usage) + add_load(idle_power_usage) + if(!active) + active = TRUE + update_appearance() + + else if(active) + active = FALSE + update_appearance() + +/obj/machinery/power/weather_tower/process() + return + +/// Check whether this tower is on a station z-level or not +/obj/machinery/power/weather_tower/proc/is_on_station() + return is_station_level(src.z) && !SSmapping.is_planetary() + +/// Calculate the charge cost to summon weather based on whether the tower is on station or not +/obj/machinery/power/weather_tower/proc/weather_charge_cost() + return is_on_station() ? /obj/item/assembly/signaler/anomaly/weather::charges * 0.5 : 1 + +/// Summon a weather event of the given type on this tower's z-level +/obj/machinery/power/weather_tower/proc/summon_weather(datum/weather/weather_type, mob/user) + if(isnull(core) || !active) + return FALSE + if(!COOLDOWN_FINISHED(src, summon_weather_cd)) + return FALSE + if(!(weather_type in get_summonable_weather_types())) + return FALSE + + var/charge_amount = weather_charge_cost() + if(core.charges < charge_amount) + return FALSE + + var/used_flags = weather_type::weather_flags | WEATHER_THUNDER + var/is_station = is_on_station() + var/list/affected_zs = list(src.z) + var/list/affected_areas + if(is_station) + var/list/storm_free_areas = typecacheof(list( + /area/station/ai, + /area/station/commons/storage/emergency, + /area/station/maintenance, + /area/station/security/prison/safe, + /area/station/security/prison/toilet, + )) + + used_flags |= WEATHER_INDOORS + affected_zs |= SSmapping.levels_by_trait(ZTRAIT_STATION) + affected_areas = list() + for(var/area/station/station_area in GLOB.areas) + if(is_type_in_typecache(station_area, storm_free_areas)) + continue + affected_areas += station_area + // keep the summoner safe as well + affected_areas -= get_area(src) + + var/datum/weather/weather = SSweather.run_weather( + weather_datum_type = weather_type, + z_levels = affected_zs, + weather_data = list( + WEATHER_FORCED_AREAS = affected_areas, + WEATHER_FORCED_FLAGS = used_flags, + WEATHER_FORCED_THUNDER = 0, + WEATHER_FORCED_TELEGRAPH = 30 SECONDS, + WEATHER_FORCED_END = 4 MINUTES, + WEATHER_FORCED_DURATION = 30 SECONDS, + ) + ) + + var/success = !!weather + if(success) + visible_message(span_notice("The [src] hums as it summons a [weather].")) + use_core_charge(charge_amount) + COOLDOWN_START(src, summon_weather_cd, 8 MINUTES) + COOLDOWN_START(src, clear_weather_cd, 4 MINUTES) + if(is_station) + notify_ghosts("Someone summoned weather on the station!", src) + log_game("[user ? key_name(user) : "Unknown"] summoned [weather.name] weather on the station using [src] [AREACOORD(src)].") + message_admins("[user ? ADMIN_LOOKUPFLW(user) : "Unknown"] summoned [weather.name] weather on the station using [src] [ADMIN_COORDJMP(src)].") + else + log_game("[user ? key_name(user) : "Unknown"] summoned [weather.name] weather using [src] [AREACOORD(src)].") + else + audible_message(span_warning("The [src] emits a frustrated buzz as nothing happens.")) + COOLDOWN_START(src, summon_weather_cd, 1 MINUTES) + + return success + +/// Subtract a charge and handle core depletion +/obj/machinery/power/weather_tower/proc/use_core_charge(amount) + if(isnull(core)) + CRASH("Tried to use weather core charge when no core is installed!") + + core.charges -= amount + if(core.charges <= 0) + visible_message(span_boldwarning("[core] expends all of its energy and disintegrates!")) + new /obj/effect/decal/cleanable/ash/large(loc) + QDEL_NULL(core) + +/// Clears whatever weather datum is referenced with weather_ref +/obj/machinery/power/weather_tower/proc/clear_weather(weather_ref, mob/user) + if(isnull(core) || !active) + return FALSE + if(!COOLDOWN_FINISHED(src, clear_weather_cd)) + return FALSE + + // clear weather on zlevel + for(var/datum/weather/ongoing as anything in get_active_weather_on_z()) + if(REF(ongoing) == weather_ref) + log_game("[user ? key_name(user) : "Unknown"] cleared [ongoing.name] using [src] [AREACOORD(src)].") + ongoing.wind_down() + COOLDOWN_START(src, clear_weather_cd, 2 MINUTES) + COOLDOWN_START(src, summon_weather_cd, 1 MINUTES) + use_core_charge(1) + return TRUE + + return FALSE + +/// Return a list of weather typepaths that this tower can summon when given a weather core. +/obj/machinery/power/weather_tower/proc/get_summonable_weather_types() + . = list( + /datum/weather/ash_storm, + /datum/weather/rain_storm, + /datum/weather/sand_storm, + /datum/weather/snow_storm, + ) + if(is_on_station()) + . += /datum/weather/rad_storm + +/// Returns a list of active weather datums that are active on this tower's z-level. +/obj/machinery/power/weather_tower/proc/get_active_weather_on_z() + . = list() + for(var/datum/weather/ongoing as anything in SSweather.processing) + if(ongoing.stage != MAIN_STAGE) + continue + if(src.z in ongoing.impacted_z_levels) + . += ongoing + +/obj/machinery/power/weather_tower/constructed + max_integrity = 200 + armor_type = /datum/armor/weather_tower/constructed + +/obj/machinery/power/weather_tower/core + +/obj/machinery/power/weather_tower/core/Initialize(mapload) + . = ..() + core = new(src) + update_appearance() diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm index 3aef03585f04..befff8dd3d0e 100644 --- a/code/game/machinery/porta_turret/portable_turret.dm +++ b/code/game/machinery/porta_turret/portable_turret.dm @@ -94,7 +94,7 @@ DEFINE_BITFIELD(turret_flags, list( /// Determines if our projectiles hit our faction var/ignore_faction = FALSE /// The spark system, used for generating... sparks? - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system /// The turret will try to shoot from a turf in that direction when in a wall var/wall_turret_direction /// If the turret is manually controlled @@ -124,11 +124,9 @@ DEFINE_BITFIELD(turret_flags, list( if(!base) base = src update_appearance() - //Sets up a spark system - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(5, 0, src) + // Sets up a spark system + spark_system = new(src, 5, FALSE) spark_system.attach(src) - tracker = new(src, scan_range) tracker.recalculate_field(full_recalc = TRUE) // manually call this so that our tracker var is set before we set anything up setup() @@ -678,7 +676,7 @@ DEFINE_BITFIELD(turret_flags, list( A.firer = src A.fired_from = src if(ignore_faction) - A.ignored_factions = faction + APPLY_FACTION_AND_ALLIES_FROM(A, src) A.fire() return A diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm index d09f2af6e7a6..841f2099ca4a 100644 --- a/code/game/machinery/quantum_pad.dm +++ b/code/game/machinery/quantum_pad.dm @@ -116,9 +116,7 @@ doteleport(user, target_pad) /obj/machinery/quantumpad/proc/sparks() - var/datum/effect_system/spark_spread/quantum/s = new /datum/effect_system/spark_spread/quantum - s.set_up(5, 1, get_turf(src)) - s.start() + do_sparks(5, TRUE, src, spark_type = /datum/effect_system/basic/spark_spread/quantum) /obj/machinery/quantumpad/attack_ghost(mob/dead/observer/ghost) . = ..() diff --git a/code/game/machinery/sleepers.dm b/code/game/machinery/sleepers.dm index 1715ead99680..eeae1f93f725 100644 --- a/code/game/machinery/sleepers.dm +++ b/code/game/machinery/sleepers.dm @@ -178,10 +178,6 @@ /obj/machinery/sleeper/process() use_energy(idle_power_usage) -/obj/machinery/sleeper/nap_violation(mob/violator) - . = ..() - open_machine() - /obj/machinery/sleeper/ui_data() var/list/data = list() data["occupied"] = !!occupant @@ -243,7 +239,6 @@ return var/mob/living/mob_occupant = occupant - check_nap_violations() switch(action) if("door") if(state_open) diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index ded188b82bd1..7a965b2c42cd 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -149,8 +149,7 @@ if(obj_flags & EMAGGED) return FALSE obj_flags |= EMAGGED - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(4, 0, src.loc) + var/datum/effect_system/basic/spark_spread/spark_system = new(src.loc, 4, 0) spark_system.start() playsound(src, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) balloon_alert(user, "machine rigged") diff --git a/code/game/machinery/stasis.dm b/code/game/machinery/stasis.dm index c74f37f83760..fa58b303897e 100644 --- a/code/game/machinery/stasis.dm +++ b/code/game/machinery/stasis.dm @@ -148,7 +148,7 @@ if(!can_be_occupant(L)) return set_occupant(L) - if(stasis_running() && check_nap_violations()) + if(stasis_running()) chill_out(L) update_appearance() L.AddComponentFrom(type, /datum/component/free_operation) @@ -161,13 +161,14 @@ L.RemoveComponentSource(type, /datum/component/free_operation) /obj/machinery/stasis/process() - if(!(occupant && isliving(occupant) && check_nap_violations())) + if(!isliving(occupant)) update_use_power(IDLE_POWER_USE) return var/mob/living/L_occupant = occupant if(stasis_running()) if(!HAS_TRAIT(L_occupant, TRAIT_STASIS)) chill_out(L_occupant) + else if(HAS_TRAIT(L_occupant, TRAIT_STASIS)) thaw_them(L_occupant) @@ -180,7 +181,4 @@ . = ..() return default_deconstruction_crowbar(I) || . -/obj/machinery/stasis/nap_violation(mob/violator) - unbuckle_mob(violator, TRUE) - #undef STASIS_TOGGLE_COOLDOWN diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 64e4e735afd0..72fb26599595 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -507,9 +507,7 @@ if(uv_super) visible_message(span_warning("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber.")) playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 50, TRUE) - var/datum/effect_system/fluid_spread/smoke/bad/black/smoke = new - smoke.set_up(0, holder = src, location = src) - smoke.start() + do_smoke(0, src, src, smoke_type = /datum/effect_system/fluid_spread/smoke/bad/black) QDEL_NULL(helmet) QDEL_NULL(suit) QDEL_NULL(mask) @@ -568,12 +566,11 @@ charge_cell(charge_per_item, cell, grid_only = TRUE) /obj/machinery/suit_storage_unit/proc/shock(mob/user, prb) - if(!prob(prb)) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() - if(electrocute_mob(user, src, src, 1, TRUE)) - return 1 + if(prob(prb)) + return + do_sparks(5, TRUE, src) + if(electrocute_mob(user, src, src, 1, TRUE)) + return TRUE /obj/machinery/suit_storage_unit/relaymove(mob/living/user, direction) if(locked) diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm index d28b9b5694ca..c09b74cab85e 100644 --- a/code/game/machinery/syndicatebomb.dm +++ b/code/game/machinery/syndicatebomb.dm @@ -363,13 +363,16 @@ var/range_medium = 9 var/range_light = 17 var/range_flame = 17 + /// Whether this core explodes when burnt + var/explodes_when_burnt = TRUE /obj/item/bombcore/ex_act(severity, target) // Little boom can chain a big boom. detonate() return TRUE /obj/item/bombcore/burn() - detonate() + if(explodes_when_burnt) + detonate() ..() /obj/item/bombcore/proc/detonate() @@ -390,13 +393,11 @@ /obj/item/bombcore/syndicate name = "Donk Co. Super-Stable Bomb Payload" desc = "After a string of unwanted detonations, this payload has been specifically redesigned to not explode unless triggered electronically by a bomb shell." + explodes_when_burnt = FALSE /obj/item/bombcore/syndicate/ex_act(severity, target) return FALSE -/obj/item/bombcore/syndicate/burn() - return ..() - /obj/item/bombcore/syndicate/large name = "Donk Co. Super-Stable Bomb Payload XL" range_heavy = 5 diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm index b822b0c4b698..20b77b00ee67 100644 --- a/code/game/machinery/telecomms/computers/message.dm +++ b/code/game/machinery/telecomms/computers/message.dm @@ -16,8 +16,6 @@ light_color = LIGHT_COLOR_GREEN /// Server linked to. var/obj/machinery/telecomms/message_server/linked_server = null - /// Sparks effect - For emag - var/datum/effect_system/spark_spread/spark_system /// Computer properties. /// 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message var/screen = MSG_MON_SCREEN_MAIN @@ -34,7 +32,6 @@ /obj/machinery/computer/message_monitor/Initialize(mapload) ..() - spark_system = new return INITIALIZE_HINT_LATELOAD /obj/machinery/computer/message_monitor/post_machine_initialize() @@ -69,22 +66,21 @@ /obj/machinery/computer/message_monitor/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) return FALSE - if(!isnull(linked_server)) - obj_flags |= EMAGGED - screen = MSG_MON_SCREEN_HACKED - spark_system.set_up(5, 0, src) - spark_system.start() - var/obj/item/paper/monitorkey/monitor_key_paper = new(loc, linked_server) - // Will help make emagging the console not so easy to get away with. - monitor_key_paper.add_raw_text("

£%@%(*$%&(£&?*(%&£/{}") - var/time = 100 * length(linked_server.decryptkey) - addtimer(CALLBACK(src, PROC_REF(unemag_console)), time) - error_message = "%$&(£: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" - linked_server.toggled = FALSE - return TRUE - else + if(isnull(linked_server)) to_chat(user, span_notice("A no server error appears on the screen.")) - return FALSE + return FALSE + + obj_flags |= EMAGGED + screen = MSG_MON_SCREEN_HACKED + do_sparks(5, FALSE, src) + var/obj/item/paper/monitorkey/monitor_key_paper = new(loc, linked_server) + // Will help make emagging the console not so easy to get away with. + monitor_key_paper.add_raw_text("

£%@%(*$%&(£&?*(%&£/{}") + var/time = 100 * length(linked_server.decryptkey) + addtimer(CALLBACK(src, PROC_REF(unemag_console)), time) + error_message = "%$&(£: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" + linked_server.toggled = FALSE + return TRUE /// Remove the emag effect from the console /obj/machinery/computer/message_monitor/proc/unemag_console() diff --git a/code/game/machinery/wall_vitals.dm b/code/game/machinery/wall_vitals.dm index c175fc98f48c..51790a1e7bc5 100644 --- a/code/game/machinery/wall_vitals.dm +++ b/code/game/machinery/wall_vitals.dm @@ -453,7 +453,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/vitals_reader/advanced, 32) /obj/machinery/vitals_reader/proc/beep_message(message) for(var/mob/viewer as anything in viewers(src)) - if(isnull(viewer.client) || !viewer.can_hear()) + if(isnull(viewer.client) || HAS_TRAIT(viewer, TRAIT_DEAF)) continue if(!viewer.runechat_prefs_check(viewer, EMOTE_MESSAGE)) continue diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index b88f0f9c1dd2..0476fdad5bdd 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -268,6 +268,10 @@ GLOBAL_LIST_INIT(dye_registry, list( new_greyscale_args["new_inhand_left"] = initial(target_type.greyscale_config_inhand_left) if(initial(target_type.greyscale_config_inhand_right)) new_greyscale_args["new_inhand_right"] = initial(target_type.greyscale_config_inhand_right) + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + if(initial(target_type.greyscale_config_onfloor)) + new_greyscale_args["new_onfloor_config"] = initial(target_type.greyscale_config_onfloor) + // DARKPACK EIDT ADD END if(new_greyscale_args.len) new_greyscale_args["colors"] = initial(target_type.greyscale_colors) || COLOR_WHITE diff --git a/code/game/objects/effects/decals/cleanable/robots.dm b/code/game/objects/effects/decals/cleanable/robots.dm index 85bce021f47f..c3c6f9119ef9 100644 --- a/code/game/objects/effects/decals/cleanable/robots.dm +++ b/code/game/objects/effects/decals/cleanable/robots.dm @@ -20,9 +20,7 @@ if (prob(40)) new /obj/effect/decal/cleanable/blood/splatter(loc, null, GET_ATOM_BLOOD_DNA(src)) else if (prob(10)) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, src) - s.start() + do_sparks(3, TRUE, src) // Doesn't have overlay support as of now /obj/effect/decal/cleanable/blood/gibs/robot_debris/update_blood_color() diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index 16254e9610c6..c3bf2d97aa37 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -49,11 +49,7 @@ ///Releases a cloud of smoke based on the randomly generated reagent in Initialize(). /obj/effect/decal/remains/human/smokey/proc/release_smoke(mob/living/smoke_releaser) visible_message(span_warning("[smoke_releaser] disturbs [src], which releases a huge cloud of gas!")) - var/datum/effect_system/fluid_spread/smoke/chem/cigarette_puff = new() - cigarette_puff.chemholder.add_reagent(that_shit_that_killed_saddam, 15) - cigarette_puff.attach(get_turf(src)) - cigarette_puff.set_up(range = 2, amount = DIAMOND_AREA(2), holder = src, location = get_turf(src), silent = TRUE) - cigarette_puff.start() + do_chem_smoke(2, src, get_turf(src), that_shit_that_killed_saddam, 15) ///Subtype of smokey remains used for rare maintenance spawns. /obj/effect/decal/remains/human/smokey/maintenance diff --git a/code/game/objects/effects/decals/turfdecal/weakpoint.dm b/code/game/objects/effects/decals/turfdecal/weakpoint.dm new file mode 100644 index 000000000000..c982cb60a92e --- /dev/null +++ b/code/game/objects/effects/decals/turfdecal/weakpoint.dm @@ -0,0 +1,139 @@ + +#define CRACK_PROPAGATION_DELAY 0.1 SECONDS +#define CRACK_TURN_CHANCE 50 +#define CRACK_DELAY_CHANCE 33 + +/obj/effect/weakpoint + name = "weakpoint crack" + desc = "A suspicious crack runs along the ground." + icon = 'icons/effects/effects.dmi' + icon_state = "weakpoint" + + /// The required strength of explosion for a weakpoint to propogate + var/required_strength = EXPLODE_LIGHT + //How many turfs should this weakpoint crack when triggered? Crack length splits by default and doesn't recurse + var/crack_length = 8 + /// How many split off cracks are expected? + var/crack_split_count = 2 + + /// When the crack is finished expanding, will it spawn more cracks? + var/spawns_children = TRUE + /// How many children weakpoints will this crack spawn when it propagates? + var/new_weakpoints = 2 + +/obj/effect/weakpoint/Initialize(mapload) + . = ..() + AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE, INVISIBILITY_OBSERVER, use_anchor = TRUE) + register_context() + +/obj/effect/weakpoint/ex_act(severity, target) + . = ..() + var/static/list/skip_turfs = typecacheof(list( + /turf/open/space, + /turf/open/misc/asteroid, + /turf/open/misc/snow, + )) + if(severity < required_strength) + balloon_alert_to_viewers("crack!") + playsound(source = src, soundin = SFX_HULL_CREAKING, vol = 50, vary = TRUE, pressure_affected = FALSE, ignore_walls = TRUE) + return //return ominous sounds when we're under the threshold. + + var/list/chain_turfs = get_crack_chain(get_turf(src), 8, TRUE, skip_turfs) // Get a nice chain of turfs + + var/crack_delay = 0 + for(var/turf/crack_turf in chain_turfs) + addtimer(CALLBACK(crack_turf, TYPE_PROC_REF(/atom, ex_act), severity, crack_turf), CRACK_PROPAGATION_DELAY * crack_delay) + playsound(source = crack_turf, soundin = SFX_HULL_CREAKING, vol = 35, vary = TRUE, pressure_affected = FALSE, ignore_walls = TRUE) + if(prob(33)) + crack_delay++ + + if(spawns_children) + chain_turfs = typecache_filter_list_reverse(chain_turfs, skip_turfs) //Filter out things that we don't want to spawn new weakpoints onto. + + for(var/i in 1 to new_weakpoints) + var/obj/effect/weakpoint/newpoint = new(pick(chain_turfs)) + //inherit parent var values in case of var-editing. + newpoint.new_weakpoints = new_weakpoints + newpoint.crack_length = crack_length + newpoint.crack_split_count = crack_split_count + qdel(src) + +/obj/effect/weakpoint/welder_act(mob/living/user, obj/item/tool) + to_chat(user, span_notice("You begin to strengthen [src]...")) + if(!tool.use_tool(src, user, 4 SECONDS, amount = 1, volume=50)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("\The [src] is fully sealed, eliminating the risk of the weakpoint growing.")) + qdel(src) + return ITEM_INTERACT_SUCCESS + +/obj/effect/weakpoint/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/stack/sticky_tape)) + var/obj/item/stack/sticky_tape/duct_tape = tool + if(!duct_tape.use(1)) + return ITEM_INTERACT_BLOCKING + to_chat(user, span_notice("\The [src] is sealed with a little elbow grease and a mound of [duct_tape].")) + qdel(src) + return ITEM_INTERACT_SUCCESS + return ..() + +/obj/effect/weakpoint/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(held_item?.tool_behaviour == TOOL_WELDER) + context[SCREENTIP_CONTEXT_LMB] = "Repair weakpoint" + return CONTEXTUAL_SCREENTIP_SET + return . + +/obj/effect/weakpoint/examine(mob/user) + . = ..() + . += span_notice("\The [src] could be repaired with a welder.") + . += span_warning("A strong enough explosion will cause [src] to expand.") + +/** + * Generates a list of turfs from the start location meandering along a randomized set of turns. + * * start_location: The turf to begin the chain of turfs from. + * * length: How many lengths this chain needs to be. + * * add_splits: Should this crack chain apply additional instances of get_crack_chain while recursively cracking even further. + * * turfs_to_skip: a typecache of turfs that we block spreading to when getting a chain. + */ +/obj/effect/weakpoint/proc/get_crack_chain(start_location, length, add_splits = TRUE, turfs_to_skip = list()) + if(!length) + CRASH("Weakpoint spawned with no length value!") + if(!start_location) + CRASH("No start location for crack specified!") + + var/list/turf/cracked_turfs = list() + var/turf/current = loc //Start on top of ourselves + var/direction = pick(NORTH, SOUTH, EAST, WEST) + + for(var/i in 1 to length) + if(length(turfs_to_skip) && is_type_in_typecache(current, turfs_to_skip)) + direction = turn(direction, pick(90, 135, 180, 225, 270)) //We'll either turn or reverse the direction of the crack if we can't get around our obstacle. + current = get_turf(get_step(current, direction)) + continue + cracked_turfs += current + // Randomly branch or continue + if(prob(CRACK_TURN_CHANCE)) + direction = turn(direction, pick(-90, -45, 45, 90)) + current = get_turf(get_step(current, direction)) + if(!isturf(current)) + break + if(add_splits) + for(var/subcrack in 1 to crack_split_count) + cracked_turfs += get_crack_chain(pick(cracked_turfs), max(round(length/2 ), 1), FALSE) //Stop recursion here + + message_admins("Station weakpoint triggered, affecting [length(cracked_turfs)] turfs in [loc_name(start_location)].") + log_game("Station weakpoint triggered, affecting [length(cracked_turfs)] turfs in [loc_name(start_location)].") + + return cracked_turfs + +/obj/effect/weakpoint/big + name = "dangerous weakpoint" + desc = "A suspicious crack runs along the ground. This one makes you feel particuarly uneasy." + icon_state = "weakpoint" + crack_length = 15 + crack_split_count = 6 + new_weakpoints = 3 + +#undef CRACK_PROPAGATION_DELAY +#undef CRACK_TURN_CHANCE +#undef CRACK_DELAY_CHANCE diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index 73ffc52a2bdb..cbaa286f0682 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -1,10 +1,12 @@ -/* This is an attempt to make some easily reusable "particle" type effect, to stop the code -constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one -defined, then set up when it is created with New(). Then this same system can just be reused each time -it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam -would spawn and follow the beaker, even if it is carried or thrown. +/* + * This is an attempt to make some easily reusable "particle" type effect, to stop the code + * constantly having to be rewritten. An item like the jetpack that uses the ion_trail_follow system, just has one + * defined, then set up when it is created with New(). Then this same system can just be reused each time + * it needs to create more trails.A beaker could have a steam_trail_follow system set up, then the steam + * would spawn and follow the beaker, even if it is carried or thrown. */ +#define PER_SYSTEM_PARTICLE_CAP 20 /obj/effect/particle_effect name = "particle effect" @@ -17,36 +19,61 @@ would spawn and follow the beaker, even if it is carried or thrown. return TRUE /datum/effect_system - var/number = 3 - var/cardinals_only = FALSE - var/turf/location - var/atom/holder - var/effect_type - var/total_effects = 0 - var/autocleanup = FALSE //will delete itself after use + // Does not contain any behaviors and should not be used by itself + abstract_type = /datum/effect_system + /// Turf on which to spawn the effects + var/turf/location = null + /// Atom that is spawning the particles whose location we're following + var/atom/holder = null + +/datum/effect_system/New(turf/location) + . = ..() + src.location = get_turf(location) /datum/effect_system/Destroy() holder = null location = null return ..() -/datum/effect_system/proc/set_up(number = 3, cardinals_only = FALSE, location) - src.number = min(number, 10) - src.cardinals_only = cardinals_only - src.location = get_turf(location) - -/datum/effect_system/proc/attach(atom/atom) - holder = atom +/// Instruct the effect system to start following an atom. Can be chained into .start() +/datum/effect_system/proc/attach(atom/new_holder) + RETURN_TYPE(/datum/effect_system) + holder = new_holder + return src +/// Start the effect system /datum/effect_system/proc/start() + return + +/// Basic effect system which spawns a certain number of moving effects +/datum/effect_system/basic + /// Total number of particles to spawn + var/amount = 3 + /// Should we pick among cardinals or all directions when deciding where the particle should move + var/cardinals_only = FALSE + /// Typepath of the effect to spawn + var/effect_type = null + /// Total amount of effects we currently have active + var/total_effects = 0 + /// Should the system delete itself after finishing? + var/autocleanup = FALSE + +/datum/effect_system/basic/New(turf/location, amount = null, cardinals_only = null) + . = ..() + if (!isnull(amount)) + src.amount = amount + if (!isnull(cardinals_only)) + src.cardinals_only = cardinals_only + +/datum/effect_system/basic/start() if(QDELETED(src)) return - for(var/i in 1 to number) - if(total_effects > 20) + for(var/i in 1 to amount) + if(total_effects > PER_SYSTEM_PARTICLE_CAP) return generate_effect() -/datum/effect_system/proc/generate_effect() +/datum/effect_system/basic/proc/generate_effect() if(holder) location = get_turf(holder) var/obj/effect/effect = new effect_type(location) @@ -56,15 +83,17 @@ would spawn and follow the beaker, even if it is carried or thrown. direction = pick(GLOB.cardinals) else direction = pick(GLOB.alldirs) - var/step_amt = pick(1,2,3) - var/step_delay = 5 + var/step_amt = rand(1, 3) + var/step_delay = 5 var/datum/move_loop/loop = GLOB.move_manager.move(effect, direction, step_delay, timeout = step_delay * step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) - RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(decrement_total_effect)) + if (autocleanup) + RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(decrement_total_effect)) -/datum/effect_system/proc/decrement_total_effect(datum/source) +/datum/effect_system/basic/proc/decrement_total_effect(datum/source) SIGNAL_HANDLER total_effects-- - if(!autocleanup || total_effects > 0) - return - QDEL_IN(src, 2 SECONDS) + if(total_effects == 0) + QDEL_IN(src, 2 SECONDS) + +#undef PER_SYSTEM_PARTICLE_CAP diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index a8a3431ef9c6..9f4db05d967b 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -9,23 +9,20 @@ return INITIALIZE_HINT_LATELOAD /obj/effect/particle_effect/expl_particles/LateInitialize() - var/step_amt = pick(25;1,50;2,100;3,200;4) - + var/step_amt = pick(25;1, 50;2, 100;3, 200;4) var/datum/move_loop/loop = GLOB.move_manager.move(src, pick(GLOB.alldirs), 1, timeout = step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(end_particle)) /obj/effect/particle_effect/expl_particles/proc/end_particle(datum/source) SIGNAL_HANDLER - if(QDELETED(src)) - return - qdel(src) + if (!QDELETED(src)) + qdel(src) -/datum/effect_system/expl_particles - number = 10 +/datum/effect_system/basic/expl_particles + amount = 10 -/datum/effect_system/expl_particles/start() - for(var/i in 1 to number) - new /obj/effect/particle_effect/expl_particles(location) +/datum/effect_system/basic/expl_particles/generate_effect() + new /obj/effect/particle_effect/expl_particles(location) /obj/effect/explosion name = "fire" @@ -45,21 +42,16 @@ /datum/effect_system/explosion -/datum/effect_system/explosion/set_up(location) - src.location = get_turf(location) - /datum/effect_system/explosion/start() - new/obj/effect/explosion( location ) - var/datum/effect_system/expl_particles/P = new/datum/effect_system/expl_particles() - P.set_up(10, 0, location) - P.start() + new /obj/effect/explosion(location) + var/datum/effect_system/basic/expl_particles/boom_particles = new(location) + boom_particles.start() /datum/effect_system/explosion/smoke /datum/effect_system/explosion/smoke/proc/create_smoke() - var/datum/effect_system/fluid_spread/smoke/S = new - S.set_up(2, holder = holder, location = location) - S.start() + var/datum/effect_system/fluid_spread/smoke/smoke_system = new(location, range = 2) + smoke_system.attach(holder).start() /datum/effect_system/explosion/smoke/start() ..() diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm deleted file mode 100644 index c47f46842839..000000000000 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ /dev/null @@ -1,113 +0,0 @@ - -///////////////////////////////////////////// -//////// Attach a trail to any object, that spawns when it moves (like for the jetpack) -/// just pass in the object to attach it to in set_up -/// Then do start() to start it and stop() to stop it, obviously -/// and don't call start() in a loop that will be repeated otherwise it'll get spammed! -///////////////////////////////////////////// - -/datum/effect_system/trail_follow - var/turf/oldposition - var/active = FALSE - var/allow_overlap = FALSE - var/auto_process = TRUE - var/qdel_in_time = 10 - var/fadetype = "ion_fade" - var/fade = TRUE - var/nograv_required = FALSE - -/datum/effect_system/trail_follow/set_up(atom/atom) - attach(atom) - oldposition = get_turf(atom) - -/datum/effect_system/trail_follow/Destroy() - oldposition = null - stop() - return ..() - -/datum/effect_system/trail_follow/proc/stop() - oldposition = null - STOP_PROCESSING(SSfastprocess, src) - active = FALSE - return TRUE - -/datum/effect_system/trail_follow/start() - oldposition = get_turf(holder) - if(!check_conditions()) - return FALSE - if(auto_process) - START_PROCESSING(SSfastprocess, src) - active = TRUE - return TRUE - -/datum/effect_system/trail_follow/process() - generate_effect() - -/datum/effect_system/trail_follow/generate_effect() - if(!check_conditions()) - return stop() - if(oldposition && !(oldposition == get_turf(holder))) - if(!oldposition.has_gravity() || !nograv_required) - var/obj/effect/E = new effect_type(oldposition) - set_dir(E) - if(fade) - flick(fadetype, E) - E.icon_state = "" - if(qdel_in_time) - QDEL_IN(E, qdel_in_time) - oldposition = get_turf(holder) - -/datum/effect_system/trail_follow/proc/check_conditions() - if(!get_turf(holder)) - return FALSE - return TRUE - -/datum/effect_system/trail_follow/steam - effect_type = /obj/effect/particle_effect/steam - -/obj/effect/particle_effect/ion_trails - name = "ion trails" - icon_state = "ion_trails" - anchored = TRUE - -/obj/effect/particle_effect/ion_trails/flight - icon_state = "ion_trails_flight" - -/datum/effect_system/trail_follow/ion - effect_type = /obj/effect/particle_effect/ion_trails - nograv_required = TRUE - qdel_in_time = 20 - -/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I) - I.setDir(holder.dir) - -/datum/effect_system/trail_follow/ion/grav_allowed - nograv_required = FALSE - -//Reagent-based explosion effect - -/datum/effect_system/reagents_explosion - var/amount // TNT equivalent - var/flashing_factor = null // factor of how powerful the flash effect relatively to the explosion - var/flaming_factor = null // factor of how powerful the flame effect is relatively to explosion - var/explosion_message = 1 //whether we show a message to mobs. - -/datum/effect_system/reagents_explosion/set_up(amt, loca, flash_fact = null, flame_fact = null, message = TRUE) - amount = amt - explosion_message = message - if(isturf(loca)) - location = loca - else - location = get_turf(loca) - flashing_factor = flash_fact - flaming_factor = flame_fact - -/// Starts the explosion. The explosion_source is as part of logging and identifying the source of the explosion for logs. -/datum/effect_system/reagents_explosion/start(atom/explosion_source = null) - if(!explosion_source) - stack_trace("Reagent explosion triggered without a source atom. This explosion may have incomplete logging.") - - if(explosion_message) - location.visible_message(span_danger("The solution violently explodes!"), span_hear("You hear an explosion!")) - - dyn_explosion(location, amount, flash_range = flashing_factor, flame_range = flaming_factor, explosion_cause = explosion_source) diff --git a/code/game/objects/effects/effect_system/effects_reagents_explosion.dm b/code/game/objects/effects/effect_system/effects_reagents_explosion.dm new file mode 100644 index 000000000000..41a0292d8b10 --- /dev/null +++ b/code/game/objects/effects/effect_system/effects_reagents_explosion.dm @@ -0,0 +1,30 @@ + + +//Reagent-based explosion effect + +/datum/effect_system/reagents_explosion + /// Explosive power + var/amount + /// Factor of how powerful the flash effect relatively to the explosion + var/flashing_factor = null + /// Factor of how powerful the flame effect is relatively to explosion + var/flaming_factor = null + /// Whether we show a message to mobs. + var/explosion_message = 1 + +/datum/effect_system/reagents_explosion/New(turf/location, amount, flash_fact = null, flame_fact = null, message = TRUE) + . = ..() + src.amount = amount + explosion_message = message + if (!isturf(location)) + location = get_turf(location) + flashing_factor = flash_fact + flaming_factor = flame_fact + +/// Starts the explosion. The explosion_source is as part of logging and identifying the source of the explosion for logs. +/datum/effect_system/reagents_explosion/start(atom/explosion_source = null) + if(!explosion_source) + stack_trace("Reagent explosion triggered without a source atom. This explosion may have incomplete logging.") + if(explosion_message) + location.visible_message(span_danger("The solution violently explodes!"), span_hear("You hear an explosion!")) + dyn_explosion(location, amount, flash_range = flashing_factor, flame_range = flaming_factor, explosion_cause = explosion_source) diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm index feab1149c483..8a40659a003a 100644 --- a/code/game/objects/effects/effect_system/effects_sparks.dm +++ b/code/game/objects/effects/effect_system/effects_sparks.dm @@ -5,13 +5,13 @@ // will always spawn at the items location. ///////////////////////////////////////////// -/proc/do_sparks(number, cardinal_only, datum/source) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(number, cardinal_only, source) +/proc/do_sparks(number, cardinal_only, atom/source, atom/holder = null, spark_type = /datum/effect_system/basic/spark_spread) + var/datum/effect_system/basic/spark_spread/sparks = new spark_type(get_turf(source), number, cardinal_only) + if (holder) + sparks.attach(holder) sparks.autocleanup = TRUE sparks.start() - /obj/effect/particle_effect/sparks name = "sparks" icon_state = "sparks" @@ -32,7 +32,7 @@ var/turf/location = loc if(isturf(location)) affect_location(location, just_initialized = TRUE) - QDEL_IN(src, 20) + QDEL_IN(src, 2 SECONDS) /obj/effect/particle_effect/sparks/Destroy() var/turf/location = loc @@ -41,11 +41,15 @@ return ..() /obj/effect/particle_effect/sparks/Move() - ..() + . = ..() var/turf/location = loc if(isturf(location)) affect_location(location) +/obj/effect/particle_effect/sparks/quantum + name = "quantum sparks" + icon_state = "quantum_sparks" + /* * Apply the effects of this spark to its location. * @@ -56,7 +60,7 @@ * just_initialized - If the spark is just being created, and we need to manually affect everything in the location */ /obj/effect/particle_effect/sparks/proc/affect_location(turf/location, just_initialized = FALSE) - location.hotspot_expose(1000,100) + location.hotspot_expose(1000, 100) SEND_SIGNAL(location, COMSIG_ATOM_TOUCHED_SPARKS, src) // for plasma floors; other floor types only have to worry about the mysterious HAZARDOUS sparks if(just_initialized) for(var/atom/movable/singed in location) @@ -79,27 +83,23 @@ if(reagents && !(reagents.flags & SEALED_CONTAINER)) reagents.expose_temperature(1000) // we set this at 1000 because that's the max reagent temp for a chem heater, higher temps require more than sparks return + if(ishuman(singed)) var/mob/living/carbon/human/singed_human = singed for(var/obj/item/anything in singed_human.get_visible_items()) sparks_touched(src, anything) -/datum/effect_system/spark_spread +/datum/effect_system/basic/spark_spread effect_type = /obj/effect/particle_effect/sparks -/datum/effect_system/spark_spread/quantum +/datum/effect_system/basic/spark_spread/quantum effect_type = /obj/effect/particle_effect/sparks/quantum - //electricity /obj/effect/particle_effect/sparks/electricity name = "lightning" icon_state = "electricity" -/obj/effect/particle_effect/sparks/quantum - name = "quantum sparks" - icon_state = "quantum_sparks" - -/datum/effect_system/lightning_spread +/datum/effect_system/basic/lightning_spread effect_type = /obj/effect/particle_effect/sparks/electricity diff --git a/code/game/objects/effects/effect_system/effects_trail.dm b/code/game/objects/effects/effect_system/effects_trail.dm new file mode 100644 index 000000000000..fa0e688fe92c --- /dev/null +++ b/code/game/objects/effects/effect_system/effects_trail.dm @@ -0,0 +1,103 @@ + +///////////////////////////////////////////// +//////// Attach a trail to any object, that spawns when it moves (like for the jetpack) +/// just pass in the object to attach it to in set_up +/// Then do start() to start it and stop() to stop it, obviously +/// and don't call start() in a loop that will be repeated otherwise it'll get spammed! +///////////////////////////////////////////// + +/datum/effect_system/trail_follow + /// Previous position of the atom we're tracking + var/turf/oldposition + /// Are we currently spawning particles? + var/active = FALSE + /// Can the particles be spawned ontop of eachother? + var/allow_overlap = FALSE + /// Should we automatically start processing ourselves? + var/auto_process = TRUE + /// Delay before we delete the particles + var/qdel_in_time = 1 SECONDS + /// Typepath we should spawn + var/effect_type = null + /// Should we flick an icon state and blank out the particles afterwards? + var/fade = TRUE + /// icon_state to flick on our particles + var/fadetype = "ion_fade" + /// Are we restricted to zero-g only? + var/nograv_required = FALSE + +/datum/effect_system/trail_follow/New(turf/location) + . = ..() + attach(location) + oldposition = location + +/datum/effect_system/trail_follow/Destroy() + oldposition = null + stop() + return ..() + +/datum/effect_system/trail_follow/proc/stop() + oldposition = null + STOP_PROCESSING(SSfastprocess, src) + active = FALSE + return TRUE + +/datum/effect_system/trail_follow/start() + oldposition = get_turf(holder) + if(!check_conditions()) + return FALSE + if(auto_process) + START_PROCESSING(SSfastprocess, src) + active = TRUE + return TRUE + +/datum/effect_system/trail_follow/process() + generate_effect() + +/datum/effect_system/trail_follow/proc/generate_effect() + if(!check_conditions()) + return stop() + + if(!oldposition || oldposition == get_turf(holder)) + oldposition = get_turf(holder) + return + + if(nograv_required && oldposition.has_gravity()) + oldposition = get_turf(holder) + return + + var/obj/effect/particle = new effect_type(oldposition) + set_dir(particle) + if(fade) + flick(fadetype, particle) + particle.icon_state = "" + + if(qdel_in_time) + QDEL_IN(particle, qdel_in_time) + +/datum/effect_system/trail_follow/proc/check_conditions() + if(!get_turf(holder)) + return FALSE + return TRUE + +/datum/effect_system/trail_follow/proc/set_dir(obj/effect/effect) + effect.setDir(holder.dir) + +/datum/effect_system/trail_follow/steam + effect_type = /obj/effect/particle_effect/steam + +/obj/effect/particle_effect/ion_trails + name = "ion trails" + icon_state = "ion_trails" + anchored = TRUE + +/obj/effect/particle_effect/ion_trails/flight + icon_state = "ion_trails_flight" + +/datum/effect_system/trail_follow/ion + effect_type = /obj/effect/particle_effect/ion_trails + nograv_required = TRUE + qdel_in_time = 2 SECONDS + +/datum/effect_system/trail_follow/ion/grav_allowed + nograv_required = FALSE diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm index f94e5d0e31c3..f8f62c13b1b8 100644 --- a/code/game/objects/effects/effect_system/effects_water.dm +++ b/code/game/objects/effects/effect_system/effects_water.dm @@ -4,15 +4,17 @@ name = "water" icon_state = "extinguish" pass_flags = PASSTABLE | PASSMACHINE | PASSSTRUCTURE | PASSGRILLE | PASSBLOB | PASSVEHICLE - var/life = 15 mouse_opacity = MOUSE_OPACITY_TRANSPARENT + /// Amount of turfs we pass before deleting ourselves + var/life = 15 /obj/effect/particle_effect/water/Initialize(mapload) . = ..() - QDEL_IN(src, 70) + QDEL_IN(src, 7 SECONDS) /obj/effect/particle_effect/water/Move(turf/newloc) - if (--src.life < 1) + life -= 1 + if (life <= 0) qdel(src) return FALSE return ..() @@ -20,8 +22,7 @@ /obj/effect/particle_effect/water/Bump(atom/A) if(reagents) reagents.expose(A) - if(A.reagents) - A.reagents.expose_temperature(-25) + A.reagents?.expose_temperature(reagents.chem_temp) return ..() ///Extinguisher snowflake @@ -71,17 +72,11 @@ ///////////////////////////////////////////// // GENERIC STEAM SPREAD SYSTEM -//Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) +// Usage: set_up(number of bits of steam, use North/South/East/West only, spawn location) // The attach(atom/atom) proc is optional, and can be called to attach the effect // to something, like a smoking beaker, so then you can just call start() and the steam // will always spawn at the items location, even if it's moved. -/* Example: - *var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() -- creates new system - *steam.set_up(5, 0, mob.loc) -- sets up variables - *OPTIONAL: steam.attach(mob) - *steam.start() -- spawns the effect -*/ ///////////////////////////////////////////// /obj/effect/particle_effect/steam name = "steam" @@ -90,11 +85,7 @@ /obj/effect/particle_effect/steam/Initialize(mapload) . = ..() - QDEL_IN(src, 20) + QDEL_IN(src, 2 SECONDS) -/datum/effect_system/steam_spread +/datum/effect_system/basic/steam_spread effect_type = /obj/effect/particle_effect/steam - -/obj/effect/particle_effect/water/Bump(atom/A) - if(A.reagents && reagents) - A.reagents.expose_temperature(reagents.chem_temp) diff --git a/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm index a55f7caf5ac3..9a4fb9f1f784 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm @@ -115,13 +115,16 @@ * A factory which produces fluid groups. */ /datum/effect_system/fluid_spread - effect_type = /obj/effect/particle_effect/fluid - /// The amount of smoke to produce. + /// The amount of fluid to produce. var/amount = 10 + /// Type of the effect we're spawning + var/effect_type = /obj/effect/particle_effect/fluid -/datum/effect_system/fluid_spread/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location, ...) - src.holder = holder - src.location = location +/datum/effect_system/fluid_spread/New(turf/location, range = 1, amount = null, atom/holder = null) + . = ..() + attach(holder) + if (isnull(amount)) + amount = DIAMOND_AREA(range) src.amount = amount /datum/effect_system/fluid_spread/start(log = FALSE) @@ -144,7 +147,6 @@ var/blame_msg if (holder) holder.transfer_fingerprints_to(flood) // This is important. If this doesn't exist thermobarics are annoying to adjudicate. - source_msg = "from inside of [ismob(holder) ? ADMIN_LOOKUPFLW(holder) : ADMIN_VERBOSEJMP(holder)]" var/lastkey = holder.fingerprintslast if (lastkey) diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm index f3394e21e191..736be058ee4b 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm @@ -178,31 +178,59 @@ if(prob(max(0, exposed_temperature - 475))) //foam dissolves when heated kill_foam() +/// Proc to quickly spawn foam +/// reagent_type can accept a list of reagents, optionally as a key-value pair with values overriding reagent_volume if not null +/proc/do_foam(range = 1, atom/holder = null, turf/location = null, datum/reagent/reagent_type = null, reagent_volume = 10, datum/reagents/carry = null, amount = null, log = FALSE, datum/effect_system/fluid_spread/foam/foam_type = /datum/effect_system/fluid_spread/foam, result_type = null, stop_reactions = FALSE, reagent_scale = FOAM_REAGENT_SCALE) + if (carry || isnull(reagent_type)) + var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, carry, result_type, stop_reactions, reagent_scale) + foam.start(log = log) + return + + if (ispath(reagent_type, /datum/reagent)) + var/datum/reagents/foam_reagents = new /datum/reagents(reagent_volume) + foam_reagents.add_reagent(reagent_type, reagent_volume) + var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, foam_reagents, result_type, stop_reactions, reagent_scale) + foam.start(log = log) + return + + + if (!islist(reagent_type)) + CRASH("do_foam passed a non-reagent path, non-list reagent_type [reagent_type]!") + + var/list/reagent_list = reagent_type + var/chem_volume = 0 + for (var/chem_type in reagent_list) + chem_volume += reagent_list[chem_type] || reagent_volume + + var/datum/reagents/foam_reagents = new /datum/reagents(chem_volume) + for (var/chem_type in reagent_list) + foam_reagents.add_reagent(chem_type, reagent_list[chem_type] || reagent_volume) + + var/datum/effect_system/fluid_spread/foam/foam = new foam_type(location, range, amount, holder || location, foam_reagents, result_type, stop_reactions, reagent_scale) + foam.start(log = log) + /// A factory for foam fluid floods. /datum/effect_system/fluid_spread/foam effect_type = /obj/effect/particle_effect/fluid/foam /// A container for all of the chemicals we distribute through the foam. - var/datum/reagents/chemholder - /// The amount that + var/datum/reagents/chemholder = null + /// The amount that we multiply the payload by var/reagent_scale = FOAM_REAGENT_SCALE /// What type of thing the foam should leave behind when it dissipates. var/atom/movable/result_type = null - -/datum/effect_system/fluid_spread/foam/New() - ..() +/datum/effect_system/fluid_spread/foam/New(turf/location, range = 1, amount = null, atom/holder = null, datum/reagents/carry = null, result_type = null, stop_reactions = FALSE, reagent_scale = FOAM_REAGENT_SCALE) + . = ..() chemholder = new(1000, NO_REACT) + carry?.trans_to(chemholder, carry.total_volume, no_react = stop_reactions, copy_only = TRUE) + if(!isnull(result_type)) + src.result_type = result_type + src.reagent_scale = reagent_scale /datum/effect_system/fluid_spread/foam/Destroy() QDEL_NULL(chemholder) return ..() -/datum/effect_system/fluid_spread/foam/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location = null, datum/reagents/carry = null, result_type = null, stop_reactions = FALSE) - . = ..() - carry?.trans_to(chemholder, carry.total_volume, no_react = stop_reactions, copy_only = TRUE) - if(!isnull(result_type)) - src.result_type = result_type - /datum/effect_system/fluid_spread/foam/start(log = FALSE) var/obj/effect/particle_effect/fluid/foam/foam = new effect_type(location, new /datum/fluid_group(amount)) var/foamcolor = mix_color_from_reagents(chemholder.reagent_list) @@ -235,7 +263,6 @@ effect_type = /obj/effect/particle_effect/fluid/foam/long_life reagent_scale = FOAM_REAGENT_SCALE * (30 / 8) - // Firefighting foam /// A variant of foam which absorbs plasma in the air if there is a fire. /obj/effect/particle_effect/fluid/foam/firefighting @@ -324,6 +351,7 @@ /obj/structure/foamedmetal/Initialize(mapload) . = ..() air_update_turf(TRUE, TRUE) + AddElement(/datum/element/uses_girder_wall_recipes) /obj/structure/foamedmetal/Destroy() air_update_turf(TRUE, FALSE) @@ -349,37 +377,6 @@ to_chat(user, span_warning("You hit [src] but bounce off it!")) playsound(src.loc, 'sound/items/weapons/tap.ogg', 100, TRUE) -/obj/structure/foamedmetal/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - ///A speed modifier for how fast the wall is build - var/platingmodifier = 1 - if(HAS_TRAIT(user, TRAIT_QUICK_BUILD)) - platingmodifier = 0.7 - if(next_beep <= world.time) - next_beep = world.time + 1 SECONDS - playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE) - add_fingerprint(user) - - if(!istype(W, /obj/item/stack/sheet)) - return ..() - - var/obj/item/stack/sheet/sheet_for_plating = W - if(istype(sheet_for_plating, /obj/item/stack/sheet/iron)) - if(sheet_for_plating.get_amount() < 2) - to_chat(user, span_warning("You need two sheets of iron to finish a wall on [src]!")) - return - to_chat(user, span_notice("You start adding plating to the foam structure...")) - if (do_after(user, 40 * platingmodifier, target = src)) - if(!sheet_for_plating.use(2)) - return - to_chat(user, span_notice("You add the plating.")) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/metal_foam_base) - transfer_fingerprints_to(T) - qdel(src) - return - - add_hiddenprint(user) - /// A metal foam variant which produces slightly sturdier walls. /obj/effect/particle_effect/fluid/foam/metal/iron name = "iron foam" @@ -498,9 +495,7 @@ /obj/effect/spawner/foam_starter/Initialize(mapload) . = ..() - - var/datum/effect_system/fluid_spread/foam/foam = new foam_type() - foam.set_up(foam_size, holder = src, location = loc) + var/datum/effect_system/fluid_spread/foam/foam = new foam_type(loc, foam_size, holder = src) foam.start() /obj/effect/spawner/foam_starter/small diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm index 83ef6e552938..be62c9a97f86 100644 --- a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm @@ -165,12 +165,13 @@ * - range: The amount of smoke to produce as number of steps from origin covered. * - amount: The amount of smoke to produce as the total desired coverage area. Autofilled from the range arg if not set. * - location: Where to produce the smoke cloud. - * - smoke_type: The smoke typepath to spawn. + * - smoke_type - Typepath for the effect system to use + * - effect_type: The smoke typepath to spawn. + * - log: Should the system log the smoke spawned? */ -/proc/do_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, smoke_type = /obj/effect/particle_effect/fluid/smoke, log = FALSE) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.effect_type = smoke_type - smoke.set_up(amount = amount, holder = holder, location = location) +/proc/do_smoke(range = 0, atom/holder = null, location = null, amount = null, smoke_type = /datum/effect_system/fluid_spread/smoke, effect_type = /obj/effect/particle_effect/fluid/smoke, log = FALSE) + var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_type(location, range, amount, holder) + smoke.effect_type = effect_type smoke.start(log = log) ///////////////////////////////////////////// @@ -205,7 +206,6 @@ . = ..() if(!.) return - smoker.drop_all_held_items() smoker.adjust_oxy_loss(1) smoker.emote("cough") @@ -275,6 +275,10 @@ /// Whether to make sure each affected turf is actually within range before cooling it. var/distcheck = TRUE +/datum/effect_system/fluid_spread/smoke/freezing/New(turf/location, range = 1, amount = null, atom/holder = null, blast_radius = 0) + . = ..() + blast = blast_radius + /** * Chills an open turf. * @@ -286,7 +290,7 @@ * Arguments: * - [chilly][/turf/open]: The open turf to chill */ -/datum/effect_system/fluid_spread/smoke/freezing/proc/Chilled(turf/open/chilly) +/datum/effect_system/fluid_spread/smoke/freezing/proc/chill_turf(turf/open/chilly) if(!istype(chilly)) return @@ -319,14 +323,10 @@ for(var/obj/item/potential_tinder in chilly) potential_tinder.extinguish() -/datum/effect_system/fluid_spread/smoke/freezing/set_up(range = 5, amount = DIAMOND_AREA(range), atom/holder, atom/location, blast_radius = 0) - . = ..() - blast = blast_radius - /datum/effect_system/fluid_spread/smoke/freezing/start(log = FALSE) if(blast) for(var/turf/T in RANGE_TURFS(blast, location)) - Chilled(T) + chill_turf(T) return ..() /// A variant of the base freezing smoke formerly used by the vent decontamination event. @@ -398,13 +398,33 @@ return TRUE /// Helper to quickly create a cloud of reagent smoke -/proc/do_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, log = FALSE, datum/effect_system/fluid_spread/smoke/chem/smoke_type = /datum/effect_system/fluid_spread/smoke/chem) - var/datum/reagents/smoke_reagents = new/datum/reagents(reagent_volume) - smoke_reagents.add_reagent(reagent_type, reagent_volume) +/// reagent_type can accept a list of reagents, optionally as a key-value pair with values overriding reagent_volume if not null +/proc/do_chem_smoke(range = 0, atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, datum/reagents/carry = null, carry_limit = null, log = FALSE, amount = null, datum/effect_system/fluid_spread/smoke/chem/smoke_type = /datum/effect_system/fluid_spread/smoke/chem, silent = TRUE) + if (carry) + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, carry, carry_limit, silent) + smoke.start(log = log) + return + + if (ispath(reagent_type, /datum/reagent)) + var/datum/reagents/smoke_reagents = new /datum/reagents(reagent_volume) + smoke_reagents.add_reagent(reagent_type, reagent_volume) + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, smoke_reagents, carry_limit, silent) + smoke.start(log = log) + return + + if (!islist(reagent_type)) + CRASH("do_chem_smoke passed a non-reagent path, non-list reagent_type [reagent_type]!") - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type - smoke.attach(location) - smoke.set_up(amount = amount, holder = holder, location = location, carry = smoke_reagents, silent = TRUE) + var/list/reagent_list = reagent_type + var/chem_volume = 0 + for (var/chem_type in reagent_list) + chem_volume += reagent_list[chem_type] || reagent_volume + + var/datum/reagents/smoke_reagents = new /datum/reagents(chem_volume) + for (var/chem_type in reagent_list) + smoke_reagents.add_reagent(chem_type, reagent_list[chem_type] || reagent_volume) + + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type(location, range, amount, holder || location, smoke_reagents, carry_limit, silent) smoke.start(log = log) /// A factory which produces clouds of chemical bearing smoke. @@ -413,18 +433,10 @@ var/datum/reagents/chemholder effect_type = /obj/effect/particle_effect/fluid/smoke/chem -/datum/effect_system/fluid_spread/smoke/chem/New() - ..() - chemholder = new(1000, NO_REACT) - -/datum/effect_system/fluid_spread/smoke/chem/Destroy() - QDEL_NULL(chemholder) - return ..() - - -/datum/effect_system/fluid_spread/smoke/chem/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location = null, datum/reagents/carry = null, silent = FALSE) +/datum/effect_system/fluid_spread/smoke/chem/New(turf/location, range = 1, amount = null, atom/holder = null, datum/reagents/carry = null, carry_limit = null, silent = FALSE) . = ..() - carry?.trans_to(chemholder, carry.total_volume, copy_only = TRUE) + chemholder = new(1000, NO_REACT) + carry?.trans_to(chemholder, isnull(carry_limit) ? carry.total_volume : carry_limit, copy_only = TRUE) if(silent) return @@ -436,18 +448,22 @@ var/where = "[AREACOORD(location)]" var/contained = length(contained_reagents) ? "\[[contained_reagents.Join(", ")]\] @ [chemholder.chem_temp]K" : null var/area/fluid_area = get_area(location) - if(carry.my_atom?.fingerprintslast) //Some reagents don't have a my_atom in some cases - var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast) - var/more = "" - if(M) - more = "[ADMIN_LOOKUPFLW(M)] " - if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // I like to be able to see my logs thank you - message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].") - log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].") - else - if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // Deathmatch has way too much smoke to log + // Some reagents don't have a my_atom in some cases + if(!carry.my_atom?.fingerprintslast) + // Deathmatch has way too much smoke to log + if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.") log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") + return + + var/mob/bomber = get_mob_by_key(carry.my_atom.fingerprintslast) + if(!istype(carry.my_atom, /obj/machinery/plumbing) && !(fluid_area.area_flags & QUIET_LOGS)) // I like to be able to see my logs thank you + message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [bomber ? "[ADMIN_LOOKUPFLW(bomber)] " : carry.my_atom.fingerprintslast].") + log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].") + +/datum/effect_system/fluid_spread/smoke/chem/Destroy() + QDEL_NULL(chemholder) + return ..() /datum/effect_system/fluid_spread/smoke/chem/start(log = FALSE) var/start_loc = holder ? get_turf(holder) : src.location diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index aaa2b2858b8e..1204ad782554 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -138,9 +138,7 @@ else visible_message(span_danger("[icon2html(src, viewers(src))] [src] detonates!")) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, src) - s.start() + do_sparks(3, TRUE, src) mineEffect(triggerer) triggered = TRUE SEND_SIGNAL(src, COMSIG_MINE_TRIGGERED, triggerer) diff --git a/code/game/objects/effects/effect_system/effect_shield.dm b/code/game/objects/effects/shield.dm similarity index 80% rename from code/game/objects/effects/effect_system/effect_shield.dm rename to code/game/objects/effects/shield.dm index d76712626b8e..7776c7bceb46 100644 --- a/code/game/objects/effects/effect_system/effect_shield.dm +++ b/code/game/objects/effects/shield.dm @@ -5,17 +5,18 @@ layer = ABOVE_NORMAL_TURF_LAYER flags_1 = PREVENT_CLICK_UNDER_1 anchored = TRUE + /// Our turf's previous heat capacity var/old_heat_capacity /obj/effect/shield/Initialize(mapload) . = ..() var/turf/location = get_turf(src) - old_heat_capacity=location.heat_capacity + old_heat_capacity = location.heat_capacity location.heat_capacity = INFINITY /obj/effect/shield/Destroy() var/turf/location = get_turf(src) - location.heat_capacity=old_heat_capacity + location.heat_capacity = old_heat_capacity return ..() /obj/effect/shield/singularity_act() diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index 8a76d885b478..88c186f5d9aa 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -129,6 +129,4 @@ gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST), list(SOUTH, SOUTHEAST, SOUTHWEST), list(WEST, NORTHWEST, SOUTHWEST), list(EAST, NORTHEAST, SOUTHEAST), GLOB.alldirs, GLOB.alldirs) gibtypes[/obj/effect/decal/cleanable/blood/gibs/robot_debris/limb] = pick(0, 1, 2) . = ..() - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(2, 1, drop_location()) - sparks.start() + do_sparks(2, TRUE, drop_location()) diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index b1574d1f8b95..f6107d37c4da 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -174,22 +174,16 @@ M.Move(dest) if(entersparks) - var/datum/effect_system/spark_spread/s = new - s.set_up(4, 1, src) - s.start() + do_sparks(4, TRUE, src) + if(exitsparks) - var/datum/effect_system/spark_spread/s = new - s.set_up(4, 1, dest) - s.start() + do_sparks(4, TRUE, dest) if(entersmoke) - var/datum/effect_system/fluid_spread/smoke/s = new - s.set_up(4, holder = src, location = src) - s.start() + do_smoke(4, src, src) + if(exitsmoke) - var/datum/effect_system/fluid_spread/smoke/s = new - s.set_up(4, holder = src, location = dest) - s.start() + do_smoke(4, dest, dest) uses-- if(uses == 0) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 23ed00800081..266e9e688dff 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -6,6 +6,7 @@ blocks_emissive = EMISSIVE_BLOCK_GENERIC burning_particles = /particles/smoke/burning/small pass_flags_self = PASSITEM + interaction_flags_atom = INTERACT_ATOM_UI_INTERACT /* !!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! @@ -46,6 +47,9 @@ var/greyscale_config_inhand_right ///The config type to use for greyscaled belt overlays. Both this and greyscale_colors must be assigned to work. var/greyscale_config_belt + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + var/greyscale_config_onfloor + // DARKPACK EDIT ADD END /* !!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!! @@ -249,7 +253,7 @@ if(sharpness && force > 5) //give sharp objects butchering functionality, for consistency AddComponent(/datum/component/butchering, speed = 8 SECONDS * toolspeed) - if(!greyscale_config && greyscale_colors && (greyscale_config_worn || greyscale_config_belt || greyscale_config_inhand_right || greyscale_config_inhand_left)) + if(!greyscale_config && greyscale_colors && (greyscale_config_worn || greyscale_config_belt || greyscale_config_inhand_right || greyscale_config_inhand_left || greyscale_config_onfloor)) // DARKPACK EDIT CHANE START - ONFLOOR_ICONS update_greyscale() . = ..() @@ -377,13 +381,17 @@ /obj/item/proc/suicide_act(mob/living/user) return -/obj/item/set_greyscale(list/colors, new_config, new_worn_config, new_inhand_left, new_inhand_right) +/obj/item/set_greyscale(list/colors, new_config, new_worn_config, new_inhand_left, new_inhand_right, new_onfloor_config) // DARKPACK EDIT CHANGE - ONFLOOR_ICONS if(new_worn_config) greyscale_config_worn = new_worn_config if(new_inhand_left) greyscale_config_inhand_left = new_inhand_left if(new_inhand_right) greyscale_config_inhand_right = new_inhand_right + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + if(new_onfloor_config) + greyscale_config_onfloor = new_onfloor_config + // DARKPACK EDIT ADD END return ..() /// Checks if this atom uses the GAGS system and if so updates the worn and inhand icons @@ -397,6 +405,10 @@ lefthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_left, greyscale_colors) if(greyscale_config_inhand_right) righthand_file = SSgreyscale.GetColoredIconByType(greyscale_config_inhand_right, greyscale_colors) + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + if(greyscale_config_onfloor) + onflooricon = SSgreyscale.GetColoredIconByType(greyscale_config_onfloor, greyscale_colors) + // DARKPACK EDIT ADD END /obj/item/verb/move_to_top() set name = "Move To Top" @@ -480,10 +492,6 @@ research_msg += "." return research_msg.Join() -/obj/item/interact(mob/user) - add_fingerprint(user) - ui_interact(user) - /obj/item/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) add_fingerprint(usr) return ..() @@ -842,7 +850,7 @@ return if(usr.get_active_held_item() == null) // Let me know if this has any problems -Yota - usr.UnarmedAttack(src) + usr.UnarmedAttack(src, TRUE) /** *This proc is executed when someone clicks the on-screen UI button. diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 03cc9089fe68..21fd4a18aa41 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -174,25 +174,19 @@ return ..() /obj/item/card/id/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) - if (isitem(old_loc)) - UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED)) - if (ismob(old_loc.loc)) - UnregisterSignal(old_loc.loc, list(COMSIG_MOVABLE_POINTED, COMSIG_MOB_RETRIEVE_ACCESS)) + if(isitem(old_loc)) + unequip_from_item_loc(old_loc) . = ..() - if (!isitem(loc)) - return - RegisterSignal(loc, COMSIG_ITEM_EQUIPPED, PROC_REF(on_loc_equipped)) - RegisterSignal(loc, COMSIG_ITEM_DROPPED, PROC_REF(on_loc_dropped)) - if (ismob(loc.loc)) - var/mob/wearer = loc.loc - // Equip chain shenanigans - UnregisterSignal(wearer, list(COMSIG_MOVABLE_POINTED, COMSIG_MOB_RETRIEVE_ACCESS)) - on_loc_equipped(loc, wearer, wearer.get_slot_by_item(loc)) + if(isitem(loc)) + equip_to_item_loc(loc) /obj/item/card/id/equipped(mob/user, slot) . = ..() if (slot & ITEM_SLOT_ID) RegisterSignal(user, COMSIG_MOVABLE_POINTED, PROC_REF(on_pointed)) + if(ishuman(user)) + var/mob/living/carbon/human/as_human = user + as_human.update_visible_name() if (slot & (ITEM_SLOT_ID|ITEM_SLOT_HANDS)) RegisterSignal(user, COMSIG_MOB_RETRIEVE_ACCESS, PROC_REF(retrieve_access)) if (slot & ITEM_SLOT_POCKETS) @@ -201,15 +195,12 @@ /obj/item/card/id/dropped(mob/user) UnregisterSignal(user, list(COMSIG_MOVABLE_POINTED, COMSIG_MOB_RETRIEVE_ACCESS)) - return ..() - -/obj/item/card/id/equipped(mob/user, slot, initial = FALSE) - . = ..() - if(!(slot & ITEM_SLOT_ID)) - return + if(isitem(loc)) + equip_to_item_loc(loc) // "dropped" into an item, like a worn wallet if(ishuman(user)) var/mob/living/carbon/human/as_human = user as_human.update_visible_name() + return ..() /obj/item/card/id/dropped(mob/user, silent = FALSE) . = ..() @@ -223,6 +214,22 @@ return honorific_title return registered_name +/// ID card is being equipped to an item (like a pda or wallet) +/obj/item/card/id/proc/equip_to_item_loc(atom/new_loc) + RegisterSignal(new_loc, COMSIG_ITEM_EQUIPPED, PROC_REF(on_loc_equipped), override = TRUE) + RegisterSignal(new_loc, COMSIG_ITEM_DROPPED, PROC_REF(on_loc_dropped), override = TRUE) + if (ismob(new_loc.loc)) + var/mob/wearer = new_loc.loc + // Equip chain shenanigans + UnregisterSignal(wearer, list(COMSIG_MOVABLE_POINTED, COMSIG_MOB_RETRIEVE_ACCESS)) + on_loc_equipped(new_loc, wearer, wearer.get_slot_by_item(new_loc)) + +/// ID card is being unequipped from an item (like a pda or wallet) +/obj/item/card/id/proc/unequip_from_item_loc(atom/old_loc) + UnregisterSignal(old_loc, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED)) + if (ismob(old_loc.loc)) + UnregisterSignal(old_loc.loc, list(COMSIG_MOVABLE_POINTED, COMSIG_MOB_RETRIEVE_ACCESS)) + /obj/item/card/id/proc/on_loc_equipped(datum/source, mob/equipper, slot) SIGNAL_HANDLER @@ -1188,6 +1195,10 @@ /// If this is set, will manually override the trim shown for SecHUDs. Intended for admins to VV edit and chameleon ID cards. var/sechud_icon_state_override = null + /// A name (eg "Captain") that is "inherent" to this ID card + /// If the assigned name matches the inherent name it does not apply the label + var/inherent_assigned_name + /obj/item/card/id/advanced/Initialize(mapload) . = ..() RegisterSignal(src, COMSIG_ITEM_EQUIPPED, PROC_REF(update_intern_status)) @@ -1290,6 +1301,13 @@ is_intern = FALSE update_label() +/obj/item/card/id/advanced/update_label() + if(inherent_assigned_name && registered_name == inherent_assigned_name) + name = "[initial(name)][(!assignment || assignment == inherent_assigned_name) ? "" : " ([assignment])"]" + return + + return ..() + /obj/item/card/id/advanced/update_overlays() . = ..() @@ -1392,13 +1410,7 @@ registered_name = "Captain" trim = /datum/id_trim/job/captain registered_age = null - -/obj/item/card/id/advanced/gold/captains_spare/update_label() //so it doesn't change to Captain's ID card (Captain) on a sneeze - if(registered_name == "Captain") - name = "[initial(name)][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]" - update_appearance(UPDATE_ICON) - else - ..() + inherent_assigned_name = "Captain" /obj/item/card/id/advanced/centcom name = "\improper CentCom ID" @@ -1493,14 +1505,7 @@ desc = "The spare ID of the Dark Lord himself." registered_name = "Captain" registered_age = null - -/obj/item/card/id/advanced/black/syndicate_command/captain_id/syndie_spare/update_label() - if(registered_name == "Captain") - name = "[initial(name)][(!assignment || assignment == "Captain") ? "" : " ([assignment])"]" - update_appearance(UPDATE_ICON) - return - - return ..() + inherent_assigned_name = "Captain" /obj/item/card/id/advanced/debug name = "\improper Debug ID" @@ -1954,22 +1959,6 @@ else input_name = "[pick(GLOB.first_names)] [pick(GLOB.last_names)]" - var/change_trim = tgui_alert(user, "Adjust the appearance of your card's trim?", "Modify Trim", list("Yes", "No")) - if(!after_input_check(user)) - return TRUE - var/selected_trim_path - var/static/list/trim_list - if(change_trim == "Yes") - trim_list = list() - for(var/trim_path in typesof(/datum/id_trim)) - var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] - if(trim && trim.trim_state && trim.assignment) - var/fake_trim_name = "[trim.assignment] ([trim.trim_state])" - trim_list[fake_trim_name] = trim_path - selected_trim_path = tgui_input_list(user, "Select trim to apply to your card.\nNote: This will not grant any trim accesses.", "Forge Trim", sort_list(trim_list, GLOBAL_PROC_REF(cmp_typepaths_asc))) - if(!after_input_check(user)) - return TRUE - var/target_occupation = tgui_input_text(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels.", "Agent card job assignment", assignment ? assignment : "Assistant", max_length = MAX_NAME_LEN) if(!after_input_check(user)) return TRUE @@ -1986,8 +1975,6 @@ return registered_name = input_name - if(selected_trim_path) - SSid_access.apply_trim_override(src, trim_list[selected_trim_path]) if(target_occupation) assignment = sanitize(target_occupation) if(new_age) @@ -2001,16 +1988,10 @@ to_chat(user, span_notice("You successfully forge the ID card.")) user.log_message("forged \the [initial(name)] with name \"[registered_name]\", occupation \"[assignment]\" and trim \"[trim?.assignment]\".", LOG_GAME) - if(!ishuman(user)) + if(!ishuman(user) || registered_account) return var/mob/living/carbon/human/owner = user - if (!selected_trim_path) // Ensure that even without a trim update, we update user's sechud - owner.update_ID_card() - - if (registered_account) - return - var/datum/bank_account/account = SSeconomy.bank_accounts_by_id["[owner.account_id]"] if(account) set_account(account) diff --git a/code/game/objects/items/cigarettes.dm b/code/game/objects/items/cigarettes.dm index 5d04bafe36f0..4a4ab465f0ae 100644 --- a/code/game/objects/items/cigarettes.dm +++ b/code/game/objects/items/cigarettes.dm @@ -427,7 +427,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM damtype = BURN force = 4 - if(reagents?.spark_act(0, FALSE, banned_reagents = /datum/reagent/flash_powder) & SPARK_ACT_DESTRUCTIVE) + if(reagents?.spark_act(0, NONE, banned_reagents = /datum/reagent/flash_powder) & SPARK_ACT_DESTRUCTIVE) usr?.log_message("lit a rigged cigarette", LOG_VICTIM) qdel(src) return @@ -1123,9 +1123,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM balloon_alert(user, "voltage maximized") icon_state = "vapeopen_high" set_greyscale(new_config = /datum/greyscale_config/vape/open_high) - var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread //for effect - sp.set_up(5, 1, src) - sp.start() + do_sparks(5, TRUE, src) return TRUE /obj/item/vape/attack_self(mob/user) @@ -1171,11 +1169,10 @@ CIGARETTE PACKETS ARE IN FANCY.DM vaper.adjust_fire_stacks(2) vaper.ignite_mob() - if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(round(reagents.get_reagent_amount(/datum/reagent/toxin/plasma) / 2.5, 1), get_turf(src), 0) - e.start(src) + // Preserve old welding fuel behavior + if(reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) qdel(src) + return if(!reagents.trans_to(vaper, REAGENTS_METABOLISM, methods = INHALE, ignore_stomach = TRUE)) reagents.remove_all(REAGENTS_METABOLISM) @@ -1199,23 +1196,21 @@ CIGARETTE PACKETS ARE IN FANCY.DM //Time to start puffing those fat vapes, yo. COOLDOWN_START(src, drag_cooldown, dragtime) if(obj_flags & EMAGGED) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/puff = new - puff.set_up(4, holder = src, location = loc, carry = reagents, efficiency = 24) - puff.start() + var/smoke_amount = DIAMOND_AREA(4) + do_chem_smoke(amount = smoke_amount, holder = src, location = loc, carry = reagents, carry_limit = 20, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/smoke_machine) + reagents.remove_all(smoke_amount / 24) if(prob(5)) //small chance for the vape to break and deal damage if it's emagged playsound(get_turf(src), 'sound/effects/pop_expl.ogg', 50, FALSE) M.apply_damage(20, BURN, BODY_ZONE_HEAD) M.Paralyze(300) - var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread - sp.set_up(5, 1, src) - sp.start() + do_sparks(5, TRUE, src) to_chat(M, span_userdanger("[src] suddenly explodes in your mouth!")) qdel(src) return else if(super) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/puff = new - puff.set_up(1, holder = src, location = loc, carry = reagents, efficiency = 24) - puff.start() + var/smoke_amount = DIAMOND_AREA(1) + do_chem_smoke(amount = smoke_amount, holder = src, location = loc, carry = reagents, carry_limit = 20, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/smoke_machine) + reagents.remove_all(smoke_amount / 24) handle_reagents() diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index 0abf7cee53e6..ce7214755054 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -141,9 +141,7 @@ /obj/item/soap/suicide_act(mob/living/user) user.say(";FFFFFFFFFFFFFFFFUUUUUUUDGE!!", forced="soap suicide") user.visible_message(span_suicide("[user] lifts [src] to [user.p_their()] mouth and gnaws on it furiously, producing a thick froth! [user.p_They()]'ll never get that BB gun now!")) - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(1, holder = src, location = get_turf(user)) - foam.start() + do_foam(1, src, get_turf(user)) return TOXLOSS /obj/item/soap/proc/should_clean(datum/cleaning_source, atom/atom_to_clean, mob/living/cleaner) @@ -268,7 +266,7 @@ return var/turf/T = get_turf(src) for(M in ohearers(7, T)) - if(M.can_hear()) + if(!HAS_TRAIT(M, TRAIT_DEAF)) M.emote("flip") COOLDOWN_START(src, golden_horn_cooldown, 1 SECONDS) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index f055aa108bd7..9737e7ba1f54 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -238,6 +238,8 @@ /// Sets painting color and updates appearance. /obj/item/toy/crayon/set_painting_tool_color(chosen_color) . = ..() + if(!can_change_colour) + return paint_color = chosen_color update_appearance() diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index 5783b3c867f5..7b854d26bfe7 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -101,10 +101,7 @@ if(active_dummy) for(var/mob/M in active_dummy) to_chat(M, span_danger("Your chameleon projector deactivates.")) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - spark_system.start() + do_sparks(5, FALSE, src, src) eject_all() if(delete_dummy) qdel(active_dummy) diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index 43daebf8a3e9..8bc39d0f1443 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -587,7 +587,7 @@ light_range = 2 light_power = 1.5 light_color = LIGHT_COLOR_FIRE - fuel = 35 MINUTES + fuel = 1 HOURS // DARKPACK EDIT CHANGE - (QOL for longer candles) randomize_fuel = FALSE trash_type = /obj/item/trash/candle can_be_extinguished = TRUE @@ -1201,8 +1201,8 @@ return FALSE var/datum/gas_mixture/environment = loc?.return_air() var/affected_pressure = environment.return_pressure() - if(!light_on && (affected_pressure < ONE_ATMOSPHERE)) - user.balloon_alert(user, "no pressure!") + if(!light_on && (affected_pressure < ONE_ATMOSPHERE - 1)) + user.balloon_alert(user, "[affected_pressure < HAZARD_LOW_PRESSURE? "no" : "low"] pressure!") return FALSE . = ..() if(light_on) diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 626c42d65a44..6a3ec38c3da9 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -27,8 +27,11 @@ throw_speed = 3 drop_sound = 'sound/items/handling/tools/multitool_drop.ogg' pickup_sound = 'sound/items/handling/tools/multitool_pickup.ogg' - custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 0.2) - custom_premium_price = PAYCHECK_COMMAND * 3 + custom_materials = list( + /datum/material/iron =SMALL_MATERIAL_AMOUNT * 0.5, + /datum/material/glass =SMALL_MATERIAL_AMOUNT * 0.2 + ) + custom_premium_price = PAYCHECK_CREW * 3 toolspeed = 1 usesound = 'sound/items/weapons/empty.ogg' var/datum/buffer // simple machine buffer for device linkage diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index c7507b31bd07..7b3d48a69dd7 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -72,10 +72,7 @@ step(L, pick(GLOB.cardinals)) to_chat(L, span_danger("You feel a sharp shock!")) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(3, 1, L) - s.start() - + do_sparks(3, TRUE, L) L.Paralyze(100) if(master) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 89ee9789e756..a5b675703afa 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -288,6 +288,11 @@ if(SEND_SIGNAL(src, COMSIG_RADIO_NEW_MESSAGE, talking_movable, message, channel) & COMPONENT_CANNOT_USE_RADIO) return NONE + // DARKPACK EDIT ADD START + if(talking_movable && HAS_TRAIT(talking_movable, TRAIT_REJECTED_BY_TECHNOLOGY)) + message = scramble_lasombra_message(message, talking_movable) + // DARKPACK EDIT ADD END + if(!spans) spans = list(talking_movable.speech_span) if(!language) @@ -376,7 +381,7 @@ if(isliving(talking_movable)) var/mob/living/talking_living = talking_movable var/volume_modifier = (talking_living.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_radio_noise)) - if(radio_noise && talking_living.can_hear() && volume_modifier && signal.frequency != FREQ_COMMON && !LAZYACCESS(message_mods, MODE_SEQUENTIAL) && COOLDOWN_FINISHED(src, audio_cooldown)) + if(radio_noise && !HAS_TRAIT(talking_living, TRAIT_DEAF) && volume_modifier && signal.frequency != FREQ_COMMON && !LAZYACCESS(message_mods, MODE_SEQUENTIAL) && COOLDOWN_FINISHED(src, audio_cooldown)) COOLDOWN_START(src, audio_cooldown, 0.5 SECONDS) var/sound/radio_noise = sound('sound/items/radio/radio_talk.ogg', volume = volume_modifier) radio_noise.frequency = get_rand_frequency_low_range() diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm index 416abb3064e9..e7cb7bac4f02 100644 --- a/code/game/objects/items/dice.dm +++ b/code/game/objects/items/dice.dm @@ -389,7 +389,7 @@ //Cookie selected_turf.visible_message(span_userdanger("A cookie appears out of thin air!")) var/obj/item/food/cookie/ooh_a_cookie = new(drop_location()) - do_smoke(0, holder = src, location = drop_location()) + do_smoke(0, src, drop_location()) ooh_a_cookie.name = "Cookie of Fate" if(12) //Healing @@ -410,12 +410,12 @@ if(14) //Free Gun selected_turf.visible_message(span_userdanger("An impressive gun appears!")) - do_smoke(0, holder = src, location = drop_location()) + do_smoke(0, src, drop_location()) new /obj/item/gun/ballistic/revolver/mateba(drop_location()) if(15) //Random One-use spellbook selected_turf.visible_message(span_userdanger("A magical looking book drops to the floor!")) - do_smoke(0, holder = src, location = drop_location()) + do_smoke(0, src, drop_location()) new /obj/item/book/granter/action/spell/random(drop_location()) if(16) //Servant & Servant Summon @@ -426,12 +426,12 @@ //Tator Kit selected_turf.visible_message(span_userdanger("A suspicious box appears!")) new /obj/item/storage/box/syndicate/bundle_a(drop_location()) - do_smoke(0, holder = src, location = drop_location()) + do_smoke(0, src, drop_location()) if(18) //Captain ID selected_turf.visible_message(span_userdanger("A golden identification card appears!")) new /obj/item/card/id/advanced/gold/captains_spare(drop_location()) - do_smoke(0, holder = src, location = drop_location()) + do_smoke(0, src, drop_location()) if(19) //Instrinct Resistance selected_turf.visible_message(span_userdanger("[user] looks very robust!")) diff --git a/code/game/objects/items/floppy_disk.dm b/code/game/objects/items/floppy_disk.dm index 135df8ef4924..83a5a6d2dca9 100644 --- a/code/game/objects/items/floppy_disk.dm +++ b/code/game/objects/items/floppy_disk.dm @@ -133,7 +133,7 @@ read_only = !read_only to_chat(user, span_notice("You flip the write-protect tab to [span_bold("[read_only ? "protected" : "unprotected"]")].")) -/obj/item/disk/click_alt(mob/user) +/obj/item/disk/click_alt_secondary(mob/user) if(sticker_icon_state != STARTING_STICKER) return CLICK_ACTION_BLOCKING diff --git a/code/game/objects/items/food/burgers.dm b/code/game/objects/items/food/burgers.dm index 0f01fbd439da..54e4cd9ec3c7 100644 --- a/code/game/objects/items/food/burgers.dm +++ b/code/game/objects/items/food/burgers.dm @@ -699,9 +699,7 @@ /obj/item/food/burger/crazy/process(seconds_per_tick) // DIT EES HORRIBLE if(SPT_PROB(2.5, seconds_per_tick)) - var/datum/effect_system/fluid_spread/smoke/bad/green/smoke = new - smoke.set_up(0, holder = src, location = src) - smoke.start() + do_smoke(0, src, loc, smoke_type = /datum/effect_system/fluid_spread/smoke/bad/green) // empty burger you can customize /obj/item/food/burger/empty diff --git a/code/game/objects/items/grenades/smokebomb.dm b/code/game/objects/items/grenades/smokebomb.dm index 371df2167c1f..3ebdddb439c5 100644 --- a/code/game/objects/items/grenades/smokebomb.dm +++ b/code/game/objects/items/grenades/smokebomb.dm @@ -43,10 +43,7 @@ update_mob() playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3) - var/datum/effect_system/fluid_spread/smoke/bad/smoke = new - smoke.set_up(4, holder = src, location = src) - smoke.start() - qdel(smoke) //And deleted again. Sad really. + do_smoke(4, src, loc, smoke_type = /datum/effect_system/fluid_spread/smoke/bad) for(var/obj/structure/blob/blob in view(8, src)) var/damage = round(30/(get_dist(blob, src) + 1)) blob.take_damage(damage, BURN, MELEE, 0) diff --git a/code/game/objects/items/implants/implant_misc.dm b/code/game/objects/items/implants/implant_misc.dm index 393df069ffef..444cfa8ed602 100644 --- a/code/game/objects/items/implants/implant_misc.dm +++ b/code/game/objects/items/implants/implant_misc.dm @@ -56,9 +56,7 @@ /obj/item/implant/smoke/activate() . = ..() uses-- - var/datum/effect_system/fluid_spread/smoke/bad/smoke = new - smoke.set_up(6, holder = imp_in, location = imp_in) - smoke.start() + do_smoke(6, imp_in, imp_in.loc, smoke_type = /datum/effect_system/fluid_spread/smoke/bad) if(!uses) qdel(src) diff --git a/code/game/objects/items/implants/security/implant_noteleport.dm b/code/game/objects/items/implants/security/implant_noteleport.dm index 36fd56aa1d45..38efeb1a028b 100644 --- a/code/game/objects/items/implants/security/implant_noteleport.dm +++ b/code/game/objects/items/implants/security/implant_noteleport.dm @@ -40,9 +40,7 @@ to_chat(teleportee, span_holoparasite("You feel yourself teleporting, but are suddenly flung back to where you just were!")) teleportee.apply_status_effect(/datum/status_effect/incapacitating/paralyzed, 5 SECONDS) - var/datum/effect_system/spark_spread/quantum/spark_system = new() - spark_system.set_up(5, TRUE, teleportee) - spark_system.start() + do_sparks(5, TRUE, teleportee, spark_type = /datum/effect_system/basic/spark_spread/quantum) return TRUE /// Signal for COMSIG_MOB_PRE_JAUNT that prevents a user from entering a jaunt. @@ -52,9 +50,7 @@ to_chat(jaunter, span_holoparasite("As you attempt to jaunt, you slam directly into the barrier between realities and are sent crashing back into corporeality!")) jaunter.apply_status_effect(/datum/status_effect/incapacitating/paralyzed, 5 SECONDS) - var/datum/effect_system/spark_spread/quantum/spark_system = new() - spark_system.set_up(5, TRUE, jaunter) - spark_system.start() + do_sparks(5, TRUE, jaunter, spark_type = /datum/effect_system/basic/spark_spread/quantum) return COMPONENT_BLOCK_JAUNT /obj/item/implantcase/teleport_blocker diff --git a/code/game/objects/items/lighter.dm b/code/game/objects/items/lighter.dm index 9e64d5d694f3..cd85b2d6d0fb 100644 --- a/code/game/objects/items/lighter.dm +++ b/code/game/objects/items/lighter.dm @@ -245,7 +245,7 @@ if(!lit) return FALSE - if (reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) + if (reagents.spark_act(0, SPARK_ACT_ENCLOSED, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) qdel(src) return FALSE diff --git a/code/game/objects/items/mail.dm b/code/game/objects/items/mail.dm index 02368f80e75f..84d05ad1f0af 100644 --- a/code/game/objects/items/mail.dm +++ b/code/game/objects/items/mail.dm @@ -158,12 +158,12 @@ . += span_notice("You notice the postmarking on the front of the mail...") var/datum/mind/recipient = recipient_ref.resolve() if(recipient) - . += span_info("[postmarked ? "Certified NT" : "Uncertfieid"] mail for [recipient].") + . += span_info("[postmarked ? "Certified [CITY_NAME]" : "Uncertfieid"] mail for [recipient].") // DARKPACK EDIT CHANGE else if(postmarked) . += span_info("Certified mail for [GLOB.station_name].") else . += span_info("This is a dead letter mail with no recipient.") - . += span_info("Distribute by hand or via destination tagger using the certified NT disposal system.") + . += span_info("Distribute by hand or via destination tagger using the certified [CITY_NAME] disposal system.") // DARKPACK EDIT CHANGE /// Accepts a mind to initialize goodies for a piece of mail. /obj/item/mail/proc/initialize_for_recipient(datum/mind/recipient) diff --git a/code/game/objects/items/rcd/RHD.dm b/code/game/objects/items/rcd/RHD.dm index 2b08fc42b728..202c85035588 100644 --- a/code/game/objects/items/rcd/RHD.dm +++ b/code/game/objects/items/rcd/RHD.dm @@ -21,7 +21,7 @@ armor_type = /datum/armor/item_construction resistance_flags = FIRE_PROOF /// the spark system which sparks whever the ui options are dited - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system /// current local matter inside the device, not used when silo link is on var/matter = 0 /// maximum local matter this device can hold, not used when silo link is on @@ -47,8 +47,7 @@ /obj/item/construction/Initialize(mapload) . = ..() - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(5, 0, src) + spark_system = new(5, FALSE, src) spark_system.attach(src) if(construction_upgrades & RCD_UPGRADE_SILO_LINK) silo_mats = new (src, mapload, FALSE) diff --git a/code/game/objects/items/rcd/RLD.dm b/code/game/objects/items/rcd/RLD.dm index 88681dade527..05d4e840925e 100644 --- a/code/game/objects/items/rcd/RLD.dm +++ b/code/game/objects/items/rcd/RLD.dm @@ -175,6 +175,7 @@ L.setDir(get_dir(winner, interacting_with)) L.color = color_choice L.set_light_color(color_choice) + L.find_and_mount_on_atom() useResource(cost, user) return ITEM_INTERACT_SUCCESS @@ -185,6 +186,7 @@ var/obj/machinery/light/floor/FL = new /obj/machinery/light/floor(target) FL.color = color_choice FL.set_light_color(color_choice) + FL.find_and_mount_on_atom() useResource(cost, user) return ITEM_INTERACT_SUCCESS diff --git a/code/game/objects/items/rcd/RPD.dm b/code/game/objects/items/rcd/RPD.dm index 11f1a1fc2c30..9cbe5a9bd3e5 100644 --- a/code/game/objects/items/rcd/RPD.dm +++ b/code/game/objects/items/rcd/RPD.dm @@ -40,7 +40,7 @@ pickup_sound = 'sound/items/handling/tools/rpd_pickup.ogg' sound_vary = TRUE ///Sparks system used when changing device in the UI - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system ///Direction of the device we are going to spawn, set up in the UI var/p_dir = NORTH ///Initial direction of the smart pipe we are going to spawn, set up in the UI @@ -80,8 +80,7 @@ /obj/item/pipe_dispenser/Initialize(mapload) . = ..() - spark_system = new - spark_system.set_up(5, 0, src) + spark_system = new(src, 5, FALSE) spark_system.attach(src) if(!first_atmos) first_atmos = GLOB.atmos_pipe_recipes[GLOB.atmos_pipe_recipes[1]][1] diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm index e820a462ff53..7ccf96f158c5 100644 --- a/code/game/objects/items/religion.dm +++ b/code/game/objects/items/religion.dm @@ -10,6 +10,7 @@ lefthand_file = 'icons/mob/inhands/equipment/banners_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/banners_righthand.dmi' custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT) + item_flags = NO_PIXEL_RANDOM_DROP var/inspiration_available = TRUE //If this banner can be used to inspire crew var/morale_time = 0 var/morale_cooldown = 600 //How many deciseconds between uses diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm index cbee8cd55d2b..2a759a275a8b 100644 --- a/code/game/objects/items/robot/items/hypo.dm +++ b/code/game/objects/items/robot/items/hypo.dm @@ -137,6 +137,8 @@ var/datum/reagent/selected_reagent /// The theme for our UI (hacked hypos get syndicate UI) var/tgui_theme = "ntos" + /// Whether this hypo should be upgradable to enable piercing + var/allow_piercing = TRUE /obj/item/reagent_containers/borghypo/Initialize(mapload) . = ..() @@ -332,6 +334,7 @@ dispensed_temperature = WATER_MATTERSTATE_CHANGE_TEMP //Water stays wet, ice stays ice default_reagent_types = BASE_SERVICE_REAGENTS expanded_reagent_types = EXPANDED_SERVICE_REAGENTS + allow_piercing = FALSE // Can't be used on carbons, so this prevents applying the piercing hypo upgrade to no effect. var/reagent_search_container = REAGENT_CONTAINER_BEVAPPARATUS /obj/item/reagent_containers/borghypo/borgshaker/ui_interact(mob/user, datum/tgui/ui) @@ -431,6 +434,7 @@ recharge_time = 6 //Double the recharge time too, for the same reason. dispensed_temperature = WATER_MATTERSTATE_CHANGE_TEMP default_reagent_types = EXPANDED_SERVICE_REAGENTS + allow_piercing = FALSE // Can't be used on carbons, so this prevents applying the piercing hypo upgrade to no effect. /obj/item/reagent_containers/borghypo/condiment_synthesizer/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index e8a5a93ea666..b8eff3ae304d 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -419,9 +419,8 @@ return . var/found_hypo = FALSE for(var/obj/item/reagent_containers/borghypo/hypo in borg.model.modules) - hypo.bypass_protection = TRUE - found_hypo = TRUE - for(var/obj/item/reagent_containers/borghypo/hypo in borg.model.emag_modules) + if(!hypo.allow_piercing) + continue hypo.bypass_protection = TRUE found_hypo = TRUE @@ -429,6 +428,10 @@ to_chat(user, span_warning("There are no installed hypospray modules to upgrade with piercing!")) //check to see if any hyposprays were upgraded return FALSE + // If we are actually going to install the upgrade due to the presence of compatible modules, make sure their emagged counterparts get upgraded too. + for(var/obj/item/reagent_containers/borghypo/hypo in borg.model.emag_modules) + hypo.bypass_protection = TRUE + /obj/item/borg/upgrade/piercing_hypospray/deactivate(mob/living/silicon/robot/borg, mob/living/user = usr) . = ..() if(!.) @@ -602,9 +605,7 @@ var/prev_lockcharge = borg.lockcharge borg.SetLockdown(TRUE) borg.set_anchored(TRUE) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(1, holder = borg, location = borg.loc) - smoke.start() + do_smoke(1, borg, borg.loc) sleep(0.2 SECONDS) for(var/i in 1 to 4) playsound(borg, pick( diff --git a/code/game/objects/items/skateboards.dm b/code/game/objects/items/skateboards.dm index 7b5e3c92d252..e8f4fbcd9d94 100644 --- a/code/game/objects/items/skateboards.dm +++ b/code/game/objects/items/skateboards.dm @@ -16,9 +16,9 @@ var/board_item_type = /obj/vehicle/ridden/scooter/skateboard /obj/item/melee/skateboard/attack_self(mob/user) - var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user))//this probably has fucky interactions with telekinesis but for the record it wasn't my fault - S.buckle_mob(user) - qdel(src) + var/obj/vehicle/ridden/scooter/skateboard/board = new board_item_type(get_turf(user), src)//this probably has fucky interactions with telekinesis but for the record it wasn't my fault + board.buckle_mob(user) + forceMove(board) /obj/item/melee/skateboard/improvised name = "improvised skateboard" @@ -58,6 +58,7 @@ force = 18 throwforce = 6 w_class = WEIGHT_CLASS_NORMAL + obj_flags = parent_type::obj_flags | UNIQUE_RENAME attack_verb_continuous = list("bashes", "crashes", "grinds", "skates") attack_verb_simple = list("bash", "crash", "grind", "skate") board_item_type = /obj/vehicle/ridden/scooter/skateboard/hoverboard/holyboarded diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 3c427fa6c252..f3c6758cda09 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -248,7 +248,7 @@ /// Checks a bunch of stuff to see if we can heal the patient, including can_heal /// Gives a feedback if we can't ultimatly heal the patient (unless silent is TRUE) /obj/item/stack/medical/proc/try_heal_checks(mob/living/patient, mob/living/user, healed_zone, silent = FALSE) - // DARKPACK EDIT ADD START - STORYTELLR_STATS + // DARKPACK EDIT ADD START - STORYTELLER_STATS if(CONFIG_GET(flag/punishing_zero_dots) && user.st_get_stat(STAT_MEDICINE) < 1) to_chat(user, span_warning("How do I do this...?")) return FALSE diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index 356574b6eda9..466a9476c0b5 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -269,6 +269,11 @@ GLOBAL_LIST_INIT(leather_recipes, list ( \ new/datum/stack_recipe("deputy hat", /obj/item/clothing/head/cowboy/red, 2, crafting_flags = NONE, category = CAT_CLOTHING), \ new/datum/stack_recipe("drifter hat", /obj/item/clothing/head/cowboy/grey, 2, crafting_flags = NONE, category = CAT_CLOTHING), \ )), + new/datum/stack_recipe_list("sword sheaths", list( \ + new/datum/stack_recipe("katana sheath", /obj/item/storage/belt/sheath/katana/empty, 3, crafting_flags = NONE, category = CAT_CLOTHING), \ + new/datum/stack_recipe("hanzo katana sheath", /obj/item/storage/belt/sheath/hanzo_katana/empty, 3, crafting_flags = NONE, category = CAT_CLOTHING), \ + new/datum/stack_recipe("toy katana sheath", /obj/item/storage/belt/sheath/katana/toy/empty, 3, crafting_flags = NONE, category = CAT_CLOTHING), \ + )), )) /obj/item/stack/sheet/leather/get_main_recipes() diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index f9fc27f770af..475d6baf4a04 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -262,6 +262,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ new/datum/stack_recipe("high security airlock assembly", /obj/structure/door_assembly/door_assembly_highsecurity, 4, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_DOORS), new/datum/stack_recipe("vault door assembly", /obj/structure/door_assembly/door_assembly_vault, 6, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_DOORS), )), \ + new /datum/stack_recipe("Radar Tower", /obj/machinery/power/weather_tower/constructed, 12, time = 10 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_STRUCTURE), \ )) // DARKPACK EDIT CHANGE END /obj/item/stack/sheet/plasteel diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index a821f5890d3f..b93f4df41406 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -618,7 +618,7 @@ return CLICK_ACTION_SUCCESS /obj/item/storage/belt/sheath/update_icon_state() - icon_state = initial(base_icon_state) // DARKPACK TODO - Fallcon will make a PR that upstreams this change soon + icon_state = initial(icon_state) inhand_icon_state = initial(inhand_icon_state) worn_icon_state = initial(worn_icon_state) if(contents.len) @@ -691,7 +691,7 @@ swordsman.Immobilize(1 SECONDS) eyed_fool = WEAKREF(cast_on) swordsman.visible_message(span_danger("[swordsman] widens [p_their(swordsman)] stance, [p_their(swordsman)] hand hovering over \the [used_sheath]!"), span_notice("You prepare to counterattack [cast_on]!")) - addtimer(CALLBACK(src, PROC_REF(relax), swordsman), 1 SECONDS) + addtimer(CALLBACK(src, PROC_REF(relax), swordsman, used_sheath), 1 SECONDS) COOLDOWN_START(used_sheath, full_ability_cooldown, 60 SECONDS) unset_ranged_ability(swordsman) return TRUE @@ -708,7 +708,14 @@ var/mob/living/fool = isliving(attackingthing) ? attackingthing : attackingthing.loc if(used_sheath.loc != forward_thinker || fool != eyed_fool.resolve() || !forward_thinker.put_in_active_hand(justicetool)) return FAILED_BLOCK + do_strike(fool, forward_thinker, justicetool) + playsound(forward_thinker, 'sound/items/unsheath.ogg', 50, TRUE) + COOLDOWN_RESET(used_sheath, full_ability_cooldown) + return SUCCESSFUL_BLOCK + +/datum/action/innate/blade_counter/proc/do_strike(mob/living/fool, mob/living/forward_thinker, obj/item/justicetool) var/obj/item/bodypart/offending_hand = fool.get_active_hand() + forward_thinker.visible_message(span_danger("[forward_thinker] swiftly draws \the [justicetool] and strikes [fool] during [p_their(fool)] attack!"), span_notice("You swiftly draw \the [justicetool] and counter-attack [fool]!")) fool.apply_damage( damage = justicetool.force * COUNTERMULTIPLIER, damagetype = justicetool.damtype, @@ -720,16 +727,94 @@ attack_direction = get_dir(forward_thinker, fool), attacking_item = justicetool, ) - playsound(forward_thinker, 'sound/items/unsheath.ogg', 50, TRUE) - forward_thinker.visible_message(span_danger("[forward_thinker] swiftly draws \the [justicetool] and strikes [fool] during [p_their(fool)] attack!"), span_notice("You swiftly draw \the [justicetool] and counter-attack [fool]!")) - COOLDOWN_RESET(used_sheath, full_ability_cooldown) - return SUCCESSFUL_BLOCK -#undef COUNTERMULTIPLIER -/datum/action/innate/blade_counter/proc/relax(mob/living/holder) +/datum/action/innate/blade_counter/proc/relax(mob/living/holder, obj/item/storage/belt/sheath/active_sheath) UnregisterSignal(holder, COMSIG_LIVING_CHECK_BLOCK) +/datum/action/innate/blade_counter/gunpowered + name = "Powered Counterattack" + desc = "Anticipate an enemy's attack and attempt to strike back, at great risk to yourself. The firing angle requires it be held on your hip." + + /// Whether the currently relevant counterattack succeeded. + var/succeeded_attempt = FALSE + +/datum/action/innate/blade_counter/gunpowered/do_ability(mob/living/swordsman, mob/living/cast_on) + . = ..() + succeeded_attempt = FALSE + + +/datum/action/innate/blade_counter/gunpowered/do_strike(mob/living/fool, mob/living/forward_thinker, obj/item/justicetool) + if(forward_thinker.get_slot_by_item(target) == ITEM_SLOT_SUITSTORE) + return ..() + succeeded_attempt = TRUE + var/obj/item/bodypart/offending_hand = fool.get_active_hand() + var/obj/item/bodypart/risked_hand = forward_thinker.get_active_hand() + if(iscarbon(fool) && offending_hand.dismember(BRUTE, FALSE, WOUND_SLASH)) + forward_thinker.visible_message(span_danger("[forward_thinker] swiftly draws \the [justicetool] and strikes [fool] during [p_their(fool)] attack, sending [p_their(fool)] arm flying!"), + span_notice("You swiftly draw \the [justicetool] and cut off [fool]'s arm!")) + else + fool.apply_damage( + damage = justicetool.force * COUNTERMULTIPLIER, + damagetype = justicetool.damtype, + def_zone = offending_hand, + blocked = fool.run_armor_check(offending_hand, MELEE, armour_penetration = justicetool.armour_penetration, silent = TRUE), + wound_bonus = justicetool.wound_bonus * COUNTERMULTIPLIER, + exposed_wound_bonus = justicetool.exposed_wound_bonus * COUNTERMULTIPLIER, + sharpness = justicetool.sharpness, + attack_direction = get_dir(forward_thinker, fool), + attacking_item = justicetool, + ) + forward_thinker.visible_message(span_danger("[forward_thinker] swiftly draws \the [justicetool] and strikes [fool] during [p_their(fool)] attack!"), + span_notice("You swiftly draw \the [justicetool] and strike them mid-attack!")) + if(!IS_ROBOTIC_LIMB(risked_hand)) + forward_thinker.visible_message(span_danger("[forward_thinker]'s arm is unable to withstand the force of the attack!"), + span_danger("You feel a sharp pain as your arm is mutilated by the force of the attack!")) + forward_thinker.apply_damage( + damage = 50, + damagetype = BRUTE, + def_zone = risked_hand, + wound_bonus = 50, + wound_clothing = FALSE, + ) + + +/datum/action/innate/blade_counter/gunpowered/relax(mob/living/holder, obj/item/storage/belt/sheath/active_sheath) + ..() + if(succeeded_attempt || holder.get_slot_by_item(target) == ITEM_SLOT_SUITSTORE) + return + + if(length(active_sheath.contents)) + var/obj/item/denied_weapon = active_sheath.contents[1] + denied_weapon.forceMove(get_turf(holder)) + denied_weapon.throw_at(pick(RANGE_TURFS(3, denied_weapon)), 3, 3) + + // We can assume that the holder is a carbon, and thus has an actual arm, because they have a belt slot. + var/obj/item/bodypart/worthless_hand = holder.get_active_hand() + if(!worthless_hand) + worthless_hand = holder.get_inactive_hand() + if(!worthless_hand) + holder.visible_message(span_danger("[holder]'s sheath misfires, sending their blade flying!"), + span_danger("Your sheath misfires, sending your blade flying!")) + return + + if(IS_ROBOTIC_LIMB(worthless_hand) || !worthless_hand.dismember(BRUTE, FALSE, WOUND_BLUNT)) + holder.visible_message(span_danger("[holder]'s arm is mutilated as they misfire [p_their(holder)] sheathed blade!"), + span_danger("Your arm is mutilated as you fail to safely fire your blade!")) + holder.apply_damage( + damage = 50, + damagetype = BRUTE, + def_zone = worthless_hand, + wound_bonus = 50, + wound_clothing = FALSE, + ) + return + + holder.visible_message(span_danger("[holder]'s arm is violently torn off as they misfire [p_their(holder)] sheathed blade!"), + span_danger("Your arm is torn off as you fail to safely fire your blade!")) + +#undef COUNTERMULTIPLIER + /obj/item/storage/belt/sheath/sabre name = "sabre sheath" desc = "An ornate sheath designed to hold an officer's blade." @@ -756,6 +841,49 @@ storage_type = /datum/storage/gladius_belt stored_blade = /obj/item/claymore/gladius +/obj/item/storage/belt/sheath/katana + name = "katana sheath" + desc = "A sheath that houses the nimble katana." + icon_state = "katana_sheath" + inhand_icon_state = "katana_sheath" + worn_icon_state = "katana_sheath" + slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT + storage_type = /datum/storage/katana_sheath + stored_blade = /obj/item/katana + +/obj/item/storage/belt/sheath/katana/empty + stored_blade = NONE + +/obj/item/storage/belt/sheath/katana/toy + action_slots = NONE + storage_type = /datum/storage/toy_sheath + stored_blade = /obj/item/toy/katana + +/obj/item/storage/belt/sheath/katana/toy/empty + stored_blade = NONE + +/obj/item/storage/belt/sheath/ninja + name = "energy katana sheath" + desc = "A high tech katana sheath that allows for quick blade movements." + icon_state = "ninja_sheath" + inhand_icon_state = "ninja_sheath" + worn_icon_state = "ninja_sheath" + storage_type = /datum/storage/ninja_sheath + stored_blade = /obj/item/energy_katana + +/obj/item/storage/belt/sheath/hanzo_katana + name = "hanzo katana sheath" + desc = "A normal black sheath meant to house the legendary hanzo steel." + icon_state = "hanzo_sheath" + inhand_icon_state = "hanzo_sheath" + worn_icon_state = "hanzo_sheath" + slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT + storage_type = /datum/storage/hanzo_sheath + stored_blade = /obj/item/nullrod/claymore/katana + +/obj/item/storage/belt/sheath/hanzo_katana/empty + stored_blade = NONE + /obj/item/storage/belt/plant name = "botanical belt" desc = "A sturdy leather belt used to hold most hydroponics supplies." @@ -764,3 +892,17 @@ worn_icon_state = "plantbelt" content_overlays = TRUE storage_type = /datum/storage/plant_belt + +/obj/item/storage/belt/sheath/sabre/gunpowered + name = "modified sabre sheath" + desc = "An imitation of a design made by the infamous Cold Space Wind. Has a trigger mechanism to more forcefully draw the blade." + icon_state = "gunsheath" + actions_types = list(/datum/action/innate/blade_counter/gunpowered) + stored_blade = null + +/obj/item/storage/belt/sheath/grass_sabre/gunpowered + name = "modified sabre sheath" + desc = "An imitation of a design grown by the infamous Tiziran Plasma Fire. Has a trigger mechanism to more forcefully draw the blade." + icon_state = "grass_gunsheath" + actions_types = list(/datum/action/innate/blade_counter/gunpowered) + diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 9adea0fa833b..763de372b6ea 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -212,7 +212,7 @@ new /obj/item/toy/cards/deck/syndicate(src) // 1 tc, for poker if(KIT_NINJA) - new /obj/item/katana(src) // Unique , hard to tell how much tc this is worth. 8 tc? + new /obj/item/storage/belt/sheath/katana(src) // Unique , hard to tell how much tc this is worth. 8 tc? new /obj/item/reagent_containers/hypospray/medipen/stimulants(src) // 5 tc for(var/i in 1 to 6) new /obj/item/throwing_star(src) // 1 tc diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm index 686d2f27ae33..a878e89e9753 100644 --- a/code/game/objects/items/tanks/tanks.dm +++ b/code/game/objects/items/tanks/tanks.dm @@ -191,7 +191,9 @@ user.visible_message(span_suicide("[user] is putting [src]'s valve to [user.p_their()] lips! It looks like [user.p_theyre()] trying to commit suicide!")) playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3) if(!QDELETED(human_user) && air_contents && air_contents.return_pressure() >= 1000) - ADD_TRAIT(human_user, TRAIT_DISFIGURED, TRAIT_GENERIC) + var/obj/item/bodypart/head = human_user.get_bodypart(BODY_ZONE_HEAD) + if(head) + ADD_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC) human_user.inflate_gib() return MANUAL_SUICIDE to_chat(user, span_warning("There isn't enough pressure in [src] to commit suicide with...")) diff --git a/code/game/objects/items/tanks/watertank.dm b/code/game/objects/items/tanks/watertank.dm index 3891f34bd095..47007dd3a632 100644 --- a/code/game/objects/items/tanks/watertank.dm +++ b/code/game/objects/items/tanks/watertank.dm @@ -358,9 +358,7 @@ anchored = TRUE /obj/effect/resin_container/proc/Smoke() - var/datum/effect_system/fluid_spread/foam/metal/resin/foaming = new - foaming.set_up(4, holder = src, location = loc) - foaming.start() + do_foam(4, src, loc, foam_type = /datum/effect_system/fluid_spread/foam/metal/resin) playsound(src,'sound/effects/bamf.ogg',100,TRUE) qdel(src) diff --git a/code/game/objects/items/tools/engineering/painter/decal_painter.dm b/code/game/objects/items/tools/engineering/painter/decal_painter.dm index 35abfb90b943..cfd9a34cc2f9 100644 --- a/code/game/objects/items/tools/engineering/painter/decal_painter.dm +++ b/code/game/objects/items/tools/engineering/painter/decal_painter.dm @@ -14,7 +14,7 @@ /// The current base icon state of the decal being printed. VAR_PRIVATE/selected_decal_icon_state = "warningline" /// Current custom color - VAR_PRIVATE/selected_custom_color + var/selected_custom_color /// Current active decal category. Reference to a global singleton VAR_PRIVATE/datum/paintable_decal_category/current_category diff --git a/code/game/objects/items/tools/engineering/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm index a39e9dae9317..0d9dded1c2f8 100644 --- a/code/game/objects/items/tools/engineering/weldingtool.dm +++ b/code/game/objects/items/tools/engineering/weldingtool.dm @@ -185,7 +185,7 @@ user.log_message("set [key_name(attacked_mob)] on fire with [src].", LOG_ATTACK) /obj/item/weldingtool/attack_self(mob/user) - if(reagents.spark_act(0, TRUE, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) + if(reagents.spark_act(0, SPARK_ACT_ENCLOSED, banned_reagents = /datum/reagent/fuel) & SPARK_ACT_DESTRUCTIVE) message_admins("[ADMIN_LOOKUPFLW(user)] activated a rigged welder at [AREACOORD(user)].") user.log_message("activated a rigged welder", LOG_VICTIM) qdel(src) diff --git a/code/game/objects/items/tools/theft_tools.dm b/code/game/objects/items/tools/theft_tools.dm index b8dce57e7c3e..fae7befdff13 100644 --- a/code/game/objects/items/tools/theft_tools.dm +++ b/code/game/objects/items/tools/theft_tools.dm @@ -33,7 +33,7 @@ if(cooldown < world.time - 60) cooldown = world.time flick(pulseicon, src) - radiation_pulse(src, max_range = 2, threshold = RAD_EXTREME_INSULATION) + radiation_pulse(get_turf(src), max_range = 2, threshold = RAD_EXTREME_INSULATION) /obj/item/nuke_core/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is rubbing [src] against [user.p_them()]self! It looks like [user.p_theyre()] trying to commit suicide!")) @@ -70,7 +70,7 @@ icon_state = "core_container_sealed" playsound(src, 'sound/items/deconstruct.ogg', 60, TRUE) if(ismob(loc)) - to_chat(loc, span_warning("[src] is permanently sealed, [core]'s radiation is contained.")) + to_chat(loc, span_warning("[src] is sealed, [core]'s radiation is contained.")) /obj/item/nuke_core_container/attackby(obj/item/nuke_core/core, mob/user) if(istype(core)) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index f3b8f5e84773..27b629f7e356 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -799,15 +799,13 @@ icon = 'icons/obj/weapons/sword.dmi' icon_state = "katana" inhand_icon_state = "katana" - worn_icon_state = "katana" icon_angle = -45 lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' obj_flags = CONDUCTS_ELECTRICITY - slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK force = 5 throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_BULKY attack_verb_continuous = list("attacks", "slashes", "slices") attack_verb_simple = list("attack", "slash", "slice") hitsound = 'sound/items/weapons/bladeslice.ogg' @@ -831,10 +829,8 @@ w_class = WEIGHT_CLASS_TINY var/ash_type = /obj/effect/decal/cleanable/ash -/obj/item/toy/snappop/proc/pop_burst(n=3, c=1) - var/datum/effect_system/spark_spread/s = new() - s.set_up(n, c, src) - s.start() +/obj/item/toy/snappop/proc/pop_burst(n = 3, c = TRUE) + do_sparks(n, c, src) new ash_type(loc) visible_message(span_warning("[src] explodes!"), span_hear("You hear a snap!")) diff --git a/code/game/objects/items/wall_mounted.dm b/code/game/objects/items/wall_mounted.dm index 824341b4c713..a75ef769078c 100644 --- a/code/game/objects/items/wall_mounted.dm +++ b/code/game/objects/items/wall_mounted.dm @@ -13,6 +13,27 @@ //The amount of pixels to shift when mounted var/pixel_shift +/obj/item/wallframe/Initialize(mapload) + . = ..() + register_context() + register_item_context() + +/obj/item/wallframe/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = NONE + if(held_item?.tool_behaviour == TOOL_WRENCH) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET + +/obj/item/wallframe/add_item_context(obj/item/source, list/context, atom/target, mob/living/user) + . = NONE + if(find_support_structure(target)) + context[SCREENTIP_CONTEXT_LMB] = "Mount" + return CONTEXTUAL_SCREENTIP_SET + +/obj/item/wallframe/examine(mob/user) + . = ..() + . += span_notice("It can be [EXAMINE_HINT("wrenched")] apart.") + /** * Returns an structure to mount on from the atom passed * for e.g if its not an closed turf then return an structure on the turf to mount on @@ -93,20 +114,15 @@ return interact_with_atom(get_step(get_turf(user), user.dir), user) /obj/item/wallframe/wrench_act(mob/living/user, obj/item/tool) - var/metal_amt = round(custom_materials[GET_MATERIAL_REF(/datum/material/iron)]/SHEET_MATERIAL_AMOUNT) //Replace this shit later - var/glass_amt = round(custom_materials[GET_MATERIAL_REF(/datum/material/glass)]/SHEET_MATERIAL_AMOUNT) //Replace this shit later - - if(!metal_amt && !glass_amt) - return FALSE to_chat(user, span_notice("You dismantle [src].")) - tool.play_tool_sound(src) - if(metal_amt) - new /obj/item/stack/sheet/iron(get_turf(src), metal_amt) - if(glass_amt) - new /obj/item/stack/sheet/glass(get_turf(src), glass_amt) - qdel(src) + deconstruct(TRUE) return ITEM_INTERACT_SUCCESS +/obj/item/wallframe/atom_deconstruct(disassembled) + var/atom/drop = drop_location() + for(var/datum/material/mat as anything in custom_materials) + new mat.sheet_type(drop, round(custom_materials[mat] / SHEET_MATERIAL_AMOUNT)) + /obj/item/electronics desc = "Looks like a circuit. Probably is." icon = 'icons/obj/devices/circuitry_n_data.dmi' diff --git a/code/game/objects/items/weaponry/melee/baton.dm b/code/game/objects/items/weaponry/melee/baton.dm index 639e3d2f0202..07a40afabdcd 100644 --- a/code/game/objects/items/weaponry/melee/baton.dm +++ b/code/game/objects/items/weaponry/melee/baton.dm @@ -758,6 +758,7 @@ abstract_type = /datum/atom_skin/stunsword change_inhand_icon_state = TRUE change_base_icon_state = TRUE + change_worn_icon_state = FALSE /datum/atom_skin/stunsword/default preview_name = "Default" diff --git a/code/game/objects/items/weaponry/melee/energy.dm b/code/game/objects/items/weaponry/melee/energy.dm index 8c4505490664..ca7f75e93a90 100644 --- a/code/game/objects/items/weaponry/melee/energy.dm +++ b/code/game/objects/items/weaponry/melee/energy.dm @@ -358,7 +358,7 @@ heat = 3500 w_class = WEIGHT_CLASS_BULKY /// Our linked spark system that emits from our sword. - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system var/list/alt_continuous = list("stabs", "pierces", "impales") var/list/alt_simple = list("stab", "pierce", "impale") @@ -368,8 +368,7 @@ alt_continuous = string_list(alt_continuous) alt_simple = string_list(alt_simple) AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -10) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) + spark_system = new(5, FALSE, src) spark_system.attach(src) START_PROCESSING(SSobj, src) ADD_TRAIT(src, TRAIT_TRANSFORM_ACTIVE, INNATE_TRAIT) // Functions as an extended esword @@ -557,6 +556,7 @@ armour_penetration = 0 wound_bonus = -10 demolition_mod = 1 + obj_flags = parent_type::obj_flags | UNIQUE_RENAME sword_color_icon = "blue" light_color = LIGHT_COLOR_LIGHT_CYAN active_force = 18 diff --git a/code/game/objects/items/weaponry/melee/katana.dm b/code/game/objects/items/weaponry/melee/katana.dm index 28747b8a0ef2..4f85e88d1319 100644 --- a/code/game/objects/items/weaponry/melee/katana.dm +++ b/code/game/objects/items/weaponry/melee/katana.dm @@ -4,20 +4,19 @@ icon = 'icons/obj/weapons/sword.dmi' icon_state = "katana" inhand_icon_state = "katana" - worn_icon_state = "katana" icon_angle = -45 lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi' obj_flags = CONDUCTS_ELECTRICITY - slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK force = 40 throwforce = 10 - w_class = WEIGHT_CLASS_HUGE + w_class = WEIGHT_CLASS_BULKY hitsound = 'sound/items/weapons/bladeslice.ogg' attack_verb_continuous = list("attacks", "slashes", "slices", "tears", "lacerates", "rips", "dices", "cuts") attack_verb_simple = list("attack", "slash", "slice", "tear", "lacerate", "rip", "dice", "cut") block_chance = 50 block_sound = 'sound/items/weapons/parry.ogg' + pickup_sound = 'sound/items/unsheath.ogg' sharpness = SHARP_EDGED max_integrity = 200 armor_type = /datum/armor/item_katana diff --git a/code/game/objects/items/wiki_manuals.dm b/code/game/objects/items/wiki_manuals.dm index e33f82e7ca0f..5c0c08024646 100644 --- a/code/game/objects/items/wiki_manuals.dm +++ b/code/game/objects/items/wiki_manuals.dm @@ -202,23 +202,26 @@ var/mob/living/carbon/human/H = user user.visible_message(span_suicide("[user] starts dancing to the Rhumba Beat! It looks like [user.p_theyre()] trying to commit suicide!")) playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - if (!QDELETED(H)) - H.emote("spin") - sleep(2 SECONDS) - for(var/obj/item/W in H) - H.dropItemToGround(W) - if(prob(50)) - step(W, pick(GLOB.alldirs)) - ADD_TRAIT(H, TRAIT_DISFIGURED, TRAIT_GENERIC) - for(var/obj/item/bodypart/part as anything in H.bodyparts) - part.adjustBleedStacks(5) - H.gib_animation() - sleep(0.3 SECONDS) - H.adjust_brute_loss(1000) //to make the body super-bloody - // if we use gib() then the body gets deleted - H.spawn_gibs() - H.spill_organs(DROP_ALL_REMAINS) - H.spread_bodyparts(DROP_BRAIN) + if(QDELETED(H)) + return + H.emote("spin") + sleep(2 SECONDS) + for(var/obj/item/W in H) + H.dropItemToGround(W) + if(prob(50)) + step(W, pick(GLOB.alldirs)) + var/obj/item/bodypart/head = H.get_bodypart(BODY_ZONE_HEAD) + if(head) + ADD_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC) + for(var/obj/item/bodypart/part as anything in H.bodyparts) + part.adjustBleedStacks(5) + H.gib_animation() + sleep(0.3 SECONDS) + H.adjust_brute_loss(1000) //to make the body super-bloody + // if we use gib() then the body gets deleted + H.spawn_gibs() + H.spill_organs(DROP_ALL_REMAINS) + H.spread_bodyparts(DROP_BRAIN) return BRUTELOSS /obj/item/book/manual/wiki/plumbing diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 6dd7f49ac611..2701dbd6d487 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -207,6 +207,10 @@ ///what happens when the obj's integrity reaches zero. /obj/atom_destruction(damage_flag) . = ..() + // DARKPACK EDIT ADD START + if(prevent_destruction) + return + // DARKPACK EDIT ADD END if(damage_flag == ACID) acid_melt() else if(damage_flag == FIRE) 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 3d7b9b0136f6..c128511fff27 100644 --- a/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm +++ b/code/game/objects/structures/cannons/mounted_guns/mounted_gun.dm @@ -148,9 +148,13 @@ /obj/structure/mounted_gun/atom_deconstruct(disassembled = TRUE) . = ..() dump_contents() + var/droploc = drop_location() for (var/type in debris) + if(ispath(type, /obj/item/stack)) + new type(droploc, debris[type]) + continue for (var/i in 1 to debris[type]) - new type(drop_location()) + new type(droploc) /obj/structure/mounted_gun/dump_contents() return // Generally we don't have contents to dump but some children do. diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 59459f73ead4..c36b22e5f2f0 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -1135,7 +1135,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) /obj/structure/closet/proc/togglelock(mob/living/user, silent) if(!secure || broken) - return + return FALSE if(locked) //only apply checks while unlocking else allow anyone to lock it var/error_msg = "" @@ -1147,8 +1147,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) id_card = null req_access = list() req_one_access = null - togglelock(user, silent) - return + return togglelock(user, silent) if(!can_unlock(user, user.get_idcard(), registered_id)) error_msg = "not your locker!" else if(!can_unlock(user, user.get_idcard())) @@ -1156,7 +1155,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) if(error_msg) if(!silent) balloon_alert(user, error_msg) - return + return TRUE if(iscarbon(user)) add_fingerprint(user) @@ -1167,6 +1166,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) span_notice("You [locked ? "locked" : "unlocked"] [src]."), ) update_appearance() + return TRUE /// toggles the lock state of a closet /obj/structure/closet/proc/lock() diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index 66f7e2780e56..11f23fb0cecf 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -46,9 +46,17 @@ return move_delay = TRUE var/oldloc = loc + set_glide_size(DELAY_TO_GLIDE_SIZE(CONFIG_GET(number/movedelay/walk_delay) * move_speed_multiplier)) // DARKPACK EDIT ADD try_step_multiz(direction) user.setDir(dir) if(oldloc != loc) + // DARKPACK EDIT ADD START + animate(src, pixel_z = 4, time = 0) + var/prev_trans = matrix(transform) + animate(pixel_z = 0, transform = turn(transform, pick(-6, 0, 6)), time=2) + animate(pixel_z = 0, transform = prev_trans, time = 0) + playsound(loc, 'modular_darkpack/modules/deprecated/sounds/snake_move.ogg', 25, FALSE) + // DARKPACK EDIT ADD END addtimer(CALLBACK(src, PROC_REF(ResetMoveDelay)), CONFIG_GET(number/movedelay/walk_delay) * move_speed_multiplier) else move_delay = FALSE diff --git a/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm b/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm index 009a8c6a843a..3a17fdc4b8b3 100644 --- a/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm +++ b/code/game/objects/structures/crates_lockers/crates/abandoned_crates/abandoned_crates.dm @@ -42,7 +42,7 @@ /obj/item/melee/baton = 2, /obj/item/clothing/gloves/boxing/evil = 1, /obj/item/melee/energy/sword/bananium = 1, - /obj/item/katana = 1, + /obj/item/storage/belt/sheath/katana = 1, ) = 5, list( // Clothing diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index f51bfafa2e5a..d8fde1fa91e9 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -12,20 +12,27 @@ smoothing_groups = SMOOTH_GROUP_GIRDER canSmoothWith = SMOOTH_GROUP_GIRDER + SMOOTH_GROUP_WALLS custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2) + + /// The stack type required to construct and dropped upon deconstructing the girder. + var/stack_type = /obj/item/stack/sheet/iron + /// The stack amount required to construct and dropped upon deconstructing the girder. + var/stack_amount = 2 + /// Always drops the stack in full, even when demolished rather than disassembled. + var/always_drop_stack = FALSE + + /// The current state of the girder. Used for construction. var/state = GIRDER_NORMAL - var/girderpasschance = 20 // percentage chance that a projectile passes through the girder. - var/can_displace = TRUE //If the girder can be moved around by wrenching it - var/next_beep = 0 //Prevents spamming of the construction sound - /// The material cost to construct something on the girder - var/static/list/construction_cost = list( - /obj/item/stack/sheet/iron = 2, - /obj/item/stack/rods = 5, - /obj/item/stack/sheet/plasteel = 2, - /obj/item/stack/sheet/bronze = 2, - /obj/item/stack/sheet/runed_metal = 1, - /obj/item/stack/sheet/titaniumglass = 2, - exotic_material = 2 // this needs to be refactored properly - ) + /// Whether the girder can be unanchored by wrenching it. + var/can_displace = TRUE + /// Whether the girder can be welded apart. (for cult and bronze girders) + var/can_weld_apart = FALSE + + /// The percentage chance (0-100) that a projectile passes through the girder. + var/projectile_pass_chance = 20 + +/obj/structure/girder/Initialize(mapload) + . = ..() + AddElement(/datum/element/uses_girder_wall_recipes) /obj/structure/girder/examine(mob/user) . = ..() @@ -39,378 +46,106 @@ . += span_notice("The bolts are wrenched in place.") if(GIRDER_DISPLACED) . += span_notice("The bolts are loosened, but the screws are holding [src] together.") - if(GIRDER_DISASSEMBLED) - . += span_notice("[src] is disassembled! You probably shouldn't be able to see this examine message.") if(GIRDER_TRAM) . += span_notice("[src] is designed for tram usage. Deconstructed with a screwdriver!") + if (can_weld_apart) + . += span_notice("The frame looks weak enough to be welded apart.") + else + . += span_notice("The frame could be sliced apart with a plasmacutter.") -/obj/structure/girder/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - - add_fingerprint(user) - - if(istype(W, /obj/item/gun/energy/plasmacutter)) - balloon_alert(user, "slicing apart...") - if(W.use_tool(src, user, 40, volume=100)) - if(state == GIRDER_TRAM) - var/obj/item/stack/sheet/mineral/titanium/M = new (user.loc, 2) - if(!QDELETED(M)) - M.add_fingerprint(user) - else - var/obj/item/stack/sheet/iron/M = new (loc, 2) - if(!QDELETED(M)) - M.add_fingerprint(user) - qdel(src) +/obj/structure/girder/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (user.combat_mode) return + if (istype(tool, /obj/item/stack/sheet/plasteel)) + if (try_construction_step(user, tool, 5 SECONDS, req_state = GIRDER_NORMAL, start_alert = "reinforcing frame...")) + replace_girder(/obj/structure/girder/reinforced) + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING - if(isstack(W)) - var/obj/item/stack/stack = W - if(!stack.usable_for_construction) - balloon_alert(user, "can't make walls with it!") - return - - if(iswallturf(loc) || (locate(/obj/structure/falsewall) in src.loc.contents)) - balloon_alert(user, "wall already present!") - return - if(!isfloorturf(src.loc) && state != GIRDER_TRAM) - balloon_alert(user, "need floor!") - return - if(state == GIRDER_TRAM) - if(!locate(/obj/structure/transport/linear/tram) in src.loc.contents) - balloon_alert(user, "need tram floors!") - return - - make_wall(stack, user) - return +/obj/structure/girder/screwdriver_act(mob/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + switch (state) + if (GIRDER_TRAM) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_TRAM, start_alert = "disassembling frame...")) + deconstruct(disassembled = TRUE) + return ITEM_INTERACT_SUCCESS + if (GIRDER_DISPLACED) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_DISPLACED, start_alert = "disassembling frame...")) + deconstruct(disassembled = TRUE) + return ITEM_INTERACT_SUCCESS + if (GIRDER_REINF) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_REINF, start_alert = "unsecuring support struts...")) + state = GIRDER_REINF_STRUTS + return ITEM_INTERACT_SUCCESS + if (GIRDER_REINF_STRUTS) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_REINF_STRUTS, start_alert = "securing support struts...")) + state = GIRDER_REINF + return ITEM_INTERACT_SUCCESS - if(istype(W, /obj/item/pipe)) - var/obj/item/pipe/P = W - if (P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds. - if(!user.transfer_item_to_turf(P, drop_location())) - return - balloon_alert(user, "inserted pipe") - return +/obj/structure/girder/wirecutter_act(mob/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_REINF_STRUTS, start_alert = "removing inner grille...")) + new /obj/item/stack/sheet/plasteel(get_turf(src)) + replace_girder(/obj/structure/girder) + return ITEM_INTERACT_SUCCESS - return ..() - -/obj/structure/girder/proc/make_wall(obj/item/stack/stack, mob/user) - var/speed_modifier = 1 - if(HAS_TRAIT(user, TRAIT_QUICK_BUILD)) - speed_modifier = 0.7 - if(next_beep <= world.time) - next_beep = world.time + 10 - playsound(src, 'sound/machines/clockcult/integration_cog_install.ogg', 50, TRUE) - - if(istype(stack, /obj/item/stack/rods)) - var/obj/item/stack/rods/rod = stack - var/amount = construction_cost[rod.type] - if(state == GIRDER_DISPLACED) - if(rod.get_amount() < amount) - balloon_alert(user, "need [amount] rods!") - return - balloon_alert(user, "concealing entrance...") - if(do_after(user, 2 SECONDS, target = src)) - if(rod.get_amount() < amount) - return - rod.use(amount) - var/obj/structure/falsewall/iron/FW = new (loc) - transfer_fingerprints_to(FW) - qdel(src) - return - - if(state == GIRDER_REINF) - balloon_alert(user, "need plasteel sheet!") - return - - if(rod.get_amount() < amount) - balloon_alert(user, "need [amount] rods!") - return - balloon_alert(user, "adding rods...") - if(do_after(user, 4 SECONDS, target = src)) - if(rod.get_amount() < amount) - return - rod.use(amount) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/mineral/iron) - transfer_fingerprints_to(T) - qdel(src) +/obj/structure/girder/wrench_act(mob/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if (!can_displace) + balloon_alert(user, "no bolts!") return - - else if(istype(stack, /obj/item/stack/sheet/iron)) - var/amount = construction_cost[/obj/item/stack/sheet/iron] - if(state == GIRDER_DISPLACED) - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "concealing entrance...") - if(do_after(user, 20 * speed_modifier, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/falsewall/F = new (loc) - transfer_fingerprints_to(F) - qdel(src) - return - else if(state == GIRDER_REINF) - balloon_alert(user, "need plasteel sheet!") - return - else if(state == GIRDER_TRAM) - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "adding plating...") - if (do_after(user, 4 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/tram/alt/iron/tram_wall = new(loc) - transfer_fingerprints_to(tram_wall) - qdel(src) - return - else - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "adding plating...") - if (do_after(user, 40 * speed_modifier, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall) - transfer_fingerprints_to(T) - qdel(src) - return - - else if(istype(stack, /obj/item/stack/sheet/titaniumglass) && state == GIRDER_TRAM) - var/amount = construction_cost[/obj/item/stack/sheet/titaniumglass] - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "adding panel...") - if (do_after(user, 2 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/tram/tram_wall = new(loc) - transfer_fingerprints_to(tram_wall) - qdel(src) + switch (state) + if (GIRDER_NORMAL) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_NORMAL, start_alert = "unsecuring frame...")) + replace_girder(/obj/structure/girder/displaced) + return ITEM_INTERACT_SUCCESS + if (GIRDER_DISPLACED) + if (try_construction_step(user, tool, 4 SECONDS, req_state = GIRDER_DISPLACED, start_alert = "securing frame...")) + replace_girder(/obj/structure/girder) + return ITEM_INTERACT_SUCCESS + +/obj/structure/girder/welder_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + // Plasmacutters can always slice apart girders. + if (!can_weld_apart && !istype(tool, /obj/item/gun/energy/plasmacutter)) + balloon_alert(user, "can't weld apart!") return + if (try_construction_step(user, tool, 4 SECONDS, start_alert = "slicing apart...")) + deconstruct(disassembled = TRUE) + return ITEM_INTERACT_SUCCESS - else if(istype(stack, /obj/item/stack/sheet/plasteel)) - var/amount = construction_cost[/obj/item/stack/sheet/plasteel] - if(state == GIRDER_DISPLACED) - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "concealing entrance...") - if(do_after(user, 2 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/falsewall/reinforced/FW = new (loc) - transfer_fingerprints_to(FW) - qdel(src) - return - else if(state == GIRDER_REINF) - amount = 1 // hur dur let's make plasteel have different construction amounts 4norasin - if(stack.get_amount() < amount) - return - balloon_alert(user, "adding plating...") - if(do_after(user, 50 * speed_modifier, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/r_wall) - transfer_fingerprints_to(T) - qdel(src) - return - else - amount = 1 // hur dur x2 - if(stack.get_amount() < amount) - return - balloon_alert(user, "reinforcing frame...") - if(do_after(user, 60 * speed_modifier, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/girder/reinforced/R = new (loc) - transfer_fingerprints_to(R) - qdel(src) - return - - else if(istype(stack, /obj/item/stack/sheet/mineral/plastitanium)) - if(state == GIRDER_REINF) - if(stack.get_amount() < 1) - return - balloon_alert(user, "adding plating...") - if(do_after(user, 50 * speed_modifier, target = src)) - if(stack.get_amount() < 1) - return - stack.use(1) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/r_wall/plastitanium) - transfer_fingerprints_to(T) - qdel(src) - return - // No return here because generic material construction handles making normal plastitanium walls - - else if(!stack.has_unique_girder && stack.material_type) - if(istype(src, /obj/structure/girder/reinforced)) - balloon_alert(user, "need plasteel or plastitanium!") - return - - var/material - if(istype(stack, /obj/item/stack/sheet)) - var/obj/item/stack/sheet/sheet = stack - material = sheet.construction_path_type - var/amount = construction_cost["exotic_material"] - if(state == GIRDER_TRAM) - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - var/tram_wall_type = text2path("/obj/structure/tram/alt/[material]") - if(!tram_wall_type) - balloon_alert(user, "need titanium glass or mineral!") - return - balloon_alert(user, "adding plating...") - if (do_after(user, 4 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - var/obj/structure/tram/tram_wall - tram_wall = new tram_wall_type(loc) - stack.use(amount) - transfer_fingerprints_to(tram_wall) - qdel(src) - return - if(state == GIRDER_DISPLACED) - var/falsewall_type = text2path("/obj/structure/falsewall/[material]") - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "concealing entrance...") - if(do_after(user, 2 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/obj/structure/falsewall/falsewall - if(falsewall_type) - falsewall = new falsewall_type (loc) - else - var/obj/structure/falsewall/material/mat_falsewall = new(loc) - var/list/material_list = list() - material_list[GET_MATERIAL_REF(stack.material_type)] = SHEET_MATERIAL_AMOUNT * 2 - if(material_list) - mat_falsewall.set_custom_materials(material_list) - falsewall = mat_falsewall - transfer_fingerprints_to(falsewall) - qdel(src) - return - else - if(stack.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "adding plating...") - if (do_after(user, 4 SECONDS, target = src)) - if(stack.get_amount() < amount) - return - stack.use(amount) - var/turf/T = get_turf(src) - if(stack.walltype) - T.place_on_top(stack.walltype) - else - var/turf/newturf = T.place_on_top(/turf/closed/wall/material) - var/list/material_list = list() - material_list[GET_MATERIAL_REF(stack.material_type)] = SHEET_MATERIAL_AMOUNT * 2 - if(material_list) - newturf.set_custom_materials(material_list) - - transfer_fingerprints_to(T) - qdel(src) - return - -// Screwdriver behavior for girders -/obj/structure/girder/screwdriver_act(mob/user, obj/item/tool) - if(..()) - return TRUE - - . = FALSE - if(state == GIRDER_TRAM) - balloon_alert(user, "disassembling frame...") - if(tool.use_tool(src, user, 4 SECONDS, volume=100)) - if(state != GIRDER_TRAM) - return - state = GIRDER_DISASSEMBLED - var/obj/item/stack/sheet/mineral/titanium/material = new (user.loc, 2) - if (!QDELETED(material)) - material.add_fingerprint(user) - qdel(src) - return TRUE - - if(state == GIRDER_DISPLACED) - balloon_alert(user, "disassembling frame...") - if(tool.use_tool(src, user, 40, volume=100)) - if(state != GIRDER_DISPLACED) - return - state = GIRDER_DISASSEMBLED - var/obj/item/stack/sheet/iron/M = new (loc, 2) - if (!QDELETED(M)) - M.add_fingerprint(user) - qdel(src) - return TRUE - - else if(state == GIRDER_REINF) - balloon_alert(user, "unsecuring support struts...") - if(tool.use_tool(src, user, 40, volume=100)) - if(state != GIRDER_REINF) - return - state = GIRDER_REINF_STRUTS - return TRUE - - else if(state == GIRDER_REINF_STRUTS) - balloon_alert(user, "securing support struts...") - if(tool.use_tool(src, user, 40, volume=100)) - if(state != GIRDER_REINF_STRUTS) - return - state = GIRDER_REINF - return TRUE +/obj/structure/girder/proc/try_construction_step(mob/living/user, obj/item/tool, delay, req_state, req_floor, start_alert, volume = 100) + if (!check_state(user, req_state, req_floor)) + return FALSE -// Wirecutter behavior for girders -/obj/structure/girder/wirecutter_act(mob/user, obj/item/tool) - . = ..() - if(state == GIRDER_REINF_STRUTS) - balloon_alert(user, "removing inner grille...") - if(tool.use_tool(src, user, 40, volume=100)) - new /obj/item/stack/sheet/plasteel(get_turf(src)) - var/obj/structure/girder/G = new (loc) - transfer_fingerprints_to(G) - qdel(src) - return TRUE + balloon_alert(user, start_alert) -/obj/structure/girder/wrench_act(mob/user, obj/item/tool) - . = ..() - if(state == GIRDER_DISPLACED) - if(!isfloorturf(loc)) - balloon_alert(user, "needs floor!") - - balloon_alert(user, "securing frame...") - if(tool.use_tool(src, user, 40, volume=100)) - var/obj/structure/girder/G = new (loc) - transfer_fingerprints_to(G) - qdel(src) - return TRUE - else if(state == GIRDER_NORMAL && can_displace) - balloon_alert(user, "unsecuring frame...") - if(tool.use_tool(src, user, 40, volume=100)) - var/obj/structure/girder/displaced/D = new (loc) - transfer_fingerprints_to(D) - qdel(src) - return TRUE + add_fingerprint(user) + tool.add_fingerprint(user) + + return tool.use_tool(src, user, delay, volume = volume, extra_checks = CALLBACK(src, PROC_REF(check_state), user, req_state, req_floor)) + +/obj/structure/girder/proc/check_state(mob/living/user, req_state, req_anchored, req_floor) + if (!isnull(req_state) && req_state != state) + return FALSE + if (req_floor && !isfloorturf(loc)) + balloon_alert(user, "needs a floor!") + return FALSE + return TRUE + +// We do this instead of state handling because of the large number of variables we would otherwise need to keep track of. +// That said, ultimately, it would be a better solution for all of it to just be [/obj/structure/girder] at base. +// For example right now if you paint a girder and use a wrench on it, it will magically lose that paint. +/obj/structure/girder/proc/replace_girder(girder_type) + var/obj/structure/girder/new_girder = new girder_type(loc) + transfer_fingerprints_to(new_girder) + new_girder.update_integrity(new_girder.max_integrity * (atom_integrity / max_integrity)) + qdel(src) /obj/structure/girder/CanAllowThrough(atom/movable/mover, border_dir) . = ..() if((mover.pass_flags & PASSGRILLE) || isprojectile(mover)) - return prob(girderpasschance) + return prob(projectile_pass_chance) /obj/structure/girder/CanAStarPass(to_dir, datum/can_pass_info/pass_info) if(!density) @@ -420,12 +155,14 @@ return FALSE /obj/structure/girder/atom_deconstruct(disassembled = TRUE) - var/remains = pick(/obj/item/stack/rods, /obj/item/stack/sheet/iron) - new remains(loc) + if (disassembled || always_drop_stack) + new stack_type(drop_location(), stack_amount) + else + var/remains = pick(/obj/item/stack/rods, stack_type) + new remains(drop_location()) /obj/structure/girder/narsie_act() - new /obj/structure/girder/cult(loc) - qdel(src) + replace_girder(/obj/structure/girder/cult) /obj/structure/girder/displaced name = "displaced girder" @@ -433,7 +170,7 @@ icon_state = "displaced" anchored = FALSE state = GIRDER_DISPLACED - girderpasschance = 25 + projectile_pass_chance = 25 max_integrity = 120 smoothing_flags = NONE smoothing_groups = null @@ -445,7 +182,7 @@ icon_state = "reinforced-0" base_icon_state = "reinforced" state = GIRDER_REINF - girderpasschance = 0 + projectile_pass_chance = 0 max_integrity = 350 /obj/structure/girder/tram @@ -458,6 +195,7 @@ smoothing_flags = NONE smoothing_groups = null canSmoothWith = null + stack_type = /obj/item/stack/sheet/mineral/titanium /obj/structure/girder/tram/corner name = "tram frame corner" @@ -474,43 +212,14 @@ smoothing_groups = null canSmoothWith = null custom_materials = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT) - -/obj/structure/girder/cult/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers) - add_fingerprint(user) - if(W.tool_behaviour == TOOL_WELDER) - if(!W.tool_start_check(user, amount=1)) - return - - balloon_alert(user, "slicing apart...") - if(W.use_tool(src, user, 40, volume=50)) - var/obj/item/stack/sheet/runed_metal/R = new(drop_location(), 1) - transfer_fingerprints_to(R) - qdel(src) - - else if(istype(W, /obj/item/stack/sheet/runed_metal)) - var/obj/item/stack/sheet/runed_metal/R = W - var/amount = construction_cost[R.type] - if(R.get_amount() < amount) - balloon_alert(user, "need [amount] sheet!") - return - balloon_alert(user, "adding plating...") - if(do_after(user, 5 SECONDS, target = src)) - if(R.get_amount() < amount) - return - R.use(amount) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/mineral/cult) - qdel(src) - - else - return ..() + stack_type = /obj/item/stack/sheet/runed_metal + stack_amount = 1 + always_drop_stack = TRUE + can_weld_apart = TRUE /obj/structure/girder/cult/narsie_act() return -/obj/structure/girder/cult/atom_deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/runed_metal(drop_location()) - /obj/structure/girder/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) switch(the_rcd.mode) if(RCD_TURF) @@ -550,32 +259,7 @@ smoothing_groups = null canSmoothWith = null custom_materials = list(/datum/material/bronze = SHEET_MATERIAL_AMOUNT * 2) - -/obj/structure/girder/bronze/attackby(obj/item/W, mob/living/user, list/modifiers, list/attack_modifiers) - add_fingerprint(user) - if(W.tool_behaviour == TOOL_WELDER) - if(!W.tool_start_check(user, amount = 0, heat_required = HIGH_TEMPERATURE_REQUIRED)) - return - balloon_alert(user, "slicing apart...") - if(W.use_tool(src, user, 40, volume=50)) - var/obj/item/stack/sheet/bronze/B = new(drop_location(), 2) - transfer_fingerprints_to(B) - qdel(src) - - else if(istype(W, /obj/item/stack/sheet/bronze)) - var/obj/item/stack/sheet/bronze/B = W - var/amount = construction_cost[B.type] - if(B.get_amount() < amount) - balloon_alert(user, "need [amount] sheets!") - return - balloon_alert(user, "adding plating...") - if(do_after(user, 5 SECONDS, target = src)) - if(B.get_amount() < amount) - return - B.use(amount) - var/turf/T = get_turf(src) - T.place_on_top(/turf/closed/wall/mineral/bronze) - qdel(src) - - else - return ..() + stack_type = /obj/item/stack/sheet/bronze + stack_amount = 1 + always_drop_stack = TRUE + can_weld_apart = TRUE diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index ed3aa733d662..f4a31fbe7137 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -343,10 +343,7 @@ return FALSE if(prob(50)) // Shocking hurts the grille (to weaken monkey powersinks) take_damage(1, BURN, FIRE, sound_effect = FALSE) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(3, 1, src) - sparks.start() - + do_sparks(3, TRUE, src) return TRUE /obj/structure/grille/should_atmos_process(datum/gas_mixture/air, exposed_temperature) diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm index 5dcfac5f77ea..f5045ca22c57 100644 --- a/code/game/objects/structures/headpike.dm +++ b/code/game/objects/structures/headpike.dm @@ -44,7 +44,7 @@ update_appearance() /obj/structure/headpike/update_name() - name = "[victim.real_name] on a [spear.name]" + name = "[victim.get_face_name()] on a [spear.name]" return ..() /obj/structure/headpike/update_overlays() diff --git a/code/game/objects/structures/hivebot.dm b/code/game/objects/structures/hivebot.dm index 19e35d30a5e6..99c4ca02a609 100644 --- a/code/game/objects/structures/hivebot.dm +++ b/code/game/objects/structures/hivebot.dm @@ -10,9 +10,7 @@ /obj/structure/hivebot_beacon/Initialize(mapload) . = ..() - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(2, holder = src, location = loc) - smoke.start() + do_smoke(2, src, loc) visible_message(span_bolddanger("[src] warps in!")) playsound(src.loc, 'sound/effects/empulse.ogg', 25, TRUE) addtimer(CALLBACK(src, PROC_REF(warpbots)), rand(1 SECONDS, 1 MINUTES)) diff --git a/code/game/objects/structures/maintenance.dm b/code/game/objects/structures/maintenance.dm index 3409c3a49c11..c6be3348e74f 100644 --- a/code/game/objects/structures/maintenance.dm +++ b/code/game/objects/structures/maintenance.dm @@ -325,9 +325,7 @@ at the cost of risking a vicious bite.**/ return if(!ismob(leaving)) return - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(range = 1, amount = 1, location = src) - smoke.start() + do_smoke(1, src, loc) playsound(src, 'sound/machines/steam_hiss.ogg', 75, TRUE, -2) COOLDOWN_START(src, steam_vent_interact, steam_speed) diff --git a/code/game/objects/structures/plaques/static_plaques.dm b/code/game/objects/structures/plaques/static_plaques.dm index 8d8dcade606d..3bb870c2088a 100644 --- a/code/game/objects/structures/plaques/static_plaques.dm +++ b/code/game/objects/structures/plaques/static_plaques.dm @@ -9,7 +9,7 @@ SET_PLANE_IMPLICIT(src, FLOOR_PLANE) layer = HIGH_TURF_LAYER -/obj/structure/plaque/static_plaque/get_moutable_objects() +/obj/structure/plaque/static_plaque/get_mountable_objects() return list() /obj/structure/plaque/static_plaque/find_and_mount_on_atom(mark_for_late_init, late_init) diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 443ba435397a..8e50f261145b 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -408,13 +408,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) return TRUE -/obj/structure/sinkframe/wrench_act_secondary(mob/living/user, obj/item/tool) - . = ..() - tool.play_tool_sound(src) - deconstruct() - return TRUE - - /obj/effect/mist name = "mist" icon = 'icons/obj/watercloset.dmi' diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index a0a79094c81d..8f490102cadd 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -393,8 +393,8 @@ // Items are centered by default, but we move them if click ICON_X and ICON_Y are available if(LAZYACCESS(modifiers, ICON_X) && LAZYACCESS(modifiers, ICON_Y)) // Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf) - x_offset = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X*0.5), ICON_SIZE_X*0.5) - y_offset = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y*0.5), ICON_SIZE_Y*0.5) + x_offset = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) + pixel_x - 16, -(ICON_SIZE_X*0.5), ICON_SIZE_X*0.5) // DARKPACK EDIT CHANGE + y_offset = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) + pixel_y - 16, -(ICON_SIZE_Y*0.5), ICON_SIZE_Y*0.5) // DARKPACK EDIT CHANGE if(!user.transfer_item_to_turf(tool, get_turf(src), x_offset, y_offset, silent = FALSE)) return ITEM_INTERACT_BLOCKING @@ -837,10 +837,7 @@ if(!electrocute_mob(user, cable_node, src, 1, TRUE)) return FALSE - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(3, TRUE, src) - sparks.start() - + do_sparks(3, TRUE, src) return TRUE /obj/structure/table/bronze diff --git a/code/game/objects/structures/toiletbong.dm b/code/game/objects/structures/toiletbong.dm index e4ff9776cea3..47e9c64f99c0 100644 --- a/code/game/objects/structures/toiletbong.dm +++ b/code/game/objects/structures/toiletbong.dm @@ -56,9 +56,10 @@ user.balloon_alert(user, "[item.name] is blocking the pipes!") continue playsound(src, 'sound/items/modsuit/flamethrower.ogg', 50) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/puff = new - puff.set_up(smokeradius, holder = src, location = user, carry = item.reagents, efficiency = 20) - puff.start() + + var/smoke_amount = DIAMOND_AREA(smokeradius) + do_chem_smoke(amount = smoke_amount, holder = src, location = loc, carry = reagents, carry_limit = 20, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/smoke_machine) + reagents.remove_all(smoke_amount / 20) if (prob(5) && !(obj_flags & EMAGGED)) if(user.get_liked_foodtypes() & GORE) user.balloon_alert(user, "a hidden treat!") diff --git a/code/game/objects/structures/traps.dm b/code/game/objects/structures/traps.dm index a14a5137b020..a1dfbd5a8305 100644 --- a/code/game/objects/structures/traps.dm +++ b/code/game/objects/structures/traps.dm @@ -16,14 +16,12 @@ var/list/mob/immune_minds = list() var/sparks = TRUE - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system /obj/structure/trap/Initialize(mapload) . = ..() flare_message = span_warning("[src] flares brightly!") - spark_system = new - spark_system.set_up(4,1,src) - spark_system.attach(src) + spark_system = new(src, 4, TRUE) var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered) @@ -37,9 +35,8 @@ )) /obj/structure/trap/Destroy() - qdel(spark_system) - spark_system = null - . = ..() + QDEL_NULL(spark_system) + return ..() /obj/structure/trap/examine(mob/user) . = ..() @@ -147,7 +144,7 @@ icon_state = "bounty_trap_off" var/obj/structure/trap/stun/hunter/stored_trap var/obj/item/radio/radio - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system /obj/item/bountytrap/Initialize(mapload) . = ..() @@ -155,8 +152,7 @@ radio.subspace_transmission = TRUE radio.canhear_range = 0 radio.recalculateChannels() - spark_system = new - spark_system.set_up(4,1,src) + spark_system = new(src, 4, TRUE) spark_system.attach(src) name = "[name] #[rand(1, 999)]" stored_trap = new(src) diff --git a/code/game/objects/structures/water_structures/sink.dm b/code/game/objects/structures/water_structures/sink.dm index 13d2d680ea78..480dda2a6a38 100644 --- a/code/game/objects/structures/water_structures/sink.dm +++ b/code/game/objects/structures/water_structures/sink.dm @@ -20,39 +20,70 @@ var/has_water_reclaimer = TRUE ///Units of water to reclaim per second var/reclaim_rate = 0.5 - ///Amount of shift the pixel for placement - var/pixel_shift = 14 MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14)) -/obj/structure/sink/Initialize(mapload, ndir = 0, has_water_reclaimer = null) +/obj/structure/sink/Initialize(mapload) . = ..() - if(ndir) - dir = ndir - - if(has_water_reclaimer != null) - src.has_water_reclaimer = has_water_reclaimer - - switch(dir) - if(NORTH) - pixel_x = 0 - pixel_y = -pixel_shift - if(SOUTH) - pixel_x = 0 - pixel_y = pixel_shift - if(EAST) - pixel_x = -pixel_shift - pixel_y = 0 - if(WEST) - pixel_x = pixel_shift - pixel_y = 0 - create_reagents(capacity, NO_REACT) - if(src.has_water_reclaimer) + if(has_water_reclaimer) reagents.add_reagent(dispensedreagent, capacity) AddComponent(/datum/component/plumbing/simple_demand/extended) + register_context() + + if(mapload && !find_and_mount_on_atom(mark_for_late_init = TRUE)) + return INITIALIZE_HINT_LATELOAD + +/obj/structure/sink/LateInitialize() + find_and_mount_on_atom(late_init = TRUE) + +/obj/structure/sink/atom_deconstruct(dissambled = TRUE) + if(buildstacktype) + new buildstacktype(loc,buildstackamount) + else + for(var/i in custom_materials) + var/datum/material/M = i + new M.sheet_type(loc, FLOOR(custom_materials[M] / SHEET_MATERIAL_AMOUNT, 1)) + if(has_water_reclaimer) + new /obj/item/stock_parts/water_recycler(drop_location()) + +/obj/structure/sink/is_mountable_turf(turf/target) + return !isgroundlessturf(target) + +/obj/structure/sink/get_turfs_to_mount_on() + return list(get_turf(src)) + +/obj/structure/sink/get_mountable_objects() + return list() + +/obj/structure/sink/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + . = NONE + if(isnull(held_item)) + context[SCREENTIP_CONTEXT_LMB] = "Wash hands" + return CONTEXTUAL_SCREENTIP_SET + + if(is_reagent_container(held_item) && held_item.is_refillable() && !held_item.reagents.holder_full()) + context[SCREENTIP_CONTEXT_LMB] = "Fill container" + return CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/mop) || astype(held_item, /obj/item/rag)?.blood_level == 0) + context[SCREENTIP_CONTEXT_LMB] = "Wet mop" + return CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/stock_parts/water_recycler) && !has_water_reclaimer) + context[SCREENTIP_CONTEXT_LMB] = "Install recycler" + return CONTEXTUAL_SCREENTIP_SET + + if(istype(held_item, /obj/item/storage/fancy/pickles_jar)) + context[SCREENTIP_CONTEXT_LMB] = "Clean pickle jar" + return CONTEXTUAL_SCREENTIP_SET + + if(!user.combat_mode || (held_item.item_flags & NOBLUDGEON)) + context[SCREENTIP_CONTEXT_LMB] = "Clean item" + return CONTEXTUAL_SCREENTIP_SET + /obj/structure/sink/examine(mob/user) . = ..() if(has_water_reclaimer) @@ -70,7 +101,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14)) if(!Adjacent(user)) return if(reagents.total_volume < 5) - to_chat(user, span_warning("The sink has no more contents left!")) + to_chat(user, span_warning("The sink is dry!")) return if(busy) to_chat(user, span_warning("Someone's already washing here!")) @@ -91,9 +122,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14)) return busy = FALSE - reagents.remove_all(5) reagents.expose(user, TOUCH, 5 / max(reagents.total_volume, 5)) - begin_reclamation() + reagents.remove_all(5) + START_PROCESSING(SSobj, src) if(washing_face) SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_WASH) else if(ishuman(user)) @@ -107,132 +138,116 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14)) user.visible_message(span_notice("[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src]."), \ span_notice("You wash your [washing_face ? "face" : "hands"] using [src].")) -/obj/structure/sink/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/structure/sink/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = NONE if(busy) to_chat(user, span_warning("Someone's already washing here!")) - return + return ITEM_INTERACT_FAILURE - if(is_reagent_container(attacking_item)) - var/obj/item/reagent_containers/RG = attacking_item - if(reagents.total_volume <= 0) + if(is_reagent_container(tool)) + var/obj/item/reagent_containers/RG = tool + if(!reagents.total_volume) to_chat(user, span_notice("\The [src] is dry.")) - return FALSE + return ITEM_INTERACT_FAILURE if(RG.is_refillable()) if(!RG.reagents.holder_full()) reagents.trans_to(RG, RG.amount_per_transfer_from_this, transferred_by = user) - begin_reclamation() + START_PROCESSING(SSobj, src) to_chat(user, span_notice("You fill [RG] from [src].")) - return TRUE + return ITEM_INTERACT_SUCCESS to_chat(user, span_notice("\The [RG] is full.")) - return FALSE - - if(istype(attacking_item, /obj/item/melee/baton/security)) - var/obj/item/melee/baton/security/baton = attacking_item - if(baton.cell?.charge && baton.active) - flick("baton_active", src) - user.Paralyze(baton.knockdown_time) - user.set_stutter(baton.knockdown_time) - baton.cell.use(baton.cell_hit_cost) - user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \ - span_userdanger("You unwisely attempt to wash [baton] while it's still on.")) - playsound(src, baton.on_stun_sound, 50, TRUE) - return + return ITEM_INTERACT_FAILURE - if(istype(attacking_item, /obj/item/mop) || astype(attacking_item, /obj/item/rag)?.blood_level == 0) - if(reagents.total_volume <= 0) + if(istype(tool, /obj/item/mop) || astype(tool, /obj/item/rag)?.blood_level == 0) + if(!reagents.total_volume) to_chat(user, span_notice("\The [src] is dry.")) - return FALSE - reagents.trans_to(attacking_item, 5, transferred_by = user) - begin_reclamation() - to_chat(user, span_notice("You wet [attacking_item] in [src].")) + return ITEM_INTERACT_FAILURE + reagents.trans_to(tool, 5, transferred_by = user) + START_PROCESSING(SSobj, src) + to_chat(user, span_notice("You wet [tool] in [src].")) playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE) - return + return ITEM_INTERACT_SUCCESS - if(attacking_item.tool_behaviour == TOOL_WRENCH) - attacking_item.play_tool_sound(src) - deconstruct() - return - - if(attacking_item.tool_behaviour == TOOL_CROWBAR) - if(!has_water_reclaimer) - to_chat(user, span_warning("There isn't a water recycler to remove.")) - return - - attacking_item.play_tool_sound(src) - has_water_reclaimer = FALSE - new/obj/item/stock_parts/water_recycler(get_turf(loc)) - to_chat(user, span_notice("You remove the water reclaimer from [src].")) - return - - if(istype(attacking_item, /obj/item/stock_parts/water_recycler)) + if(istype(tool, /obj/item/stock_parts/water_recycler)) if(has_water_reclaimer) to_chat(user, span_warning("There is already has a water recycler installed.")) - return + return ITEM_INTERACT_FAILURE playsound(src, 'sound/machines/click.ogg', 20, TRUE) - qdel(attacking_item) + qdel(tool) has_water_reclaimer = TRUE - begin_reclamation() - return + START_PROCESSING(SSobj, src) + return ITEM_INTERACT_SUCCESS - if(istype(attacking_item, /obj/item/storage/fancy/pickles_jar)) - if(attacking_item.contents.len) + if(istype(tool, /obj/item/storage/fancy/pickles_jar)) + if(tool.contents.len) to_chat(user, span_notice("Looks like there's something left in the jar")) - return - qdel(attacking_item) + return ITEM_INTERACT_FAILURE + qdel(tool) to_chat(user, span_notice("You washed the jar, ridding it of the brine.")) user.put_in_active_hand(new /obj/item/reagent_containers/cup/beaker/large(loc)) - return + return ITEM_INTERACT_SUCCESS - if(!istype(attacking_item)) - return - if(attacking_item.item_flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand. - return + if(!user.combat_mode || (tool.item_flags & NOBLUDGEON)) + if(reagents.total_volume < 5) + to_chat(user, span_warning("The sink is dry!")) + return ITEM_INTERACT_FAILURE - if(!user.combat_mode || (attacking_item.item_flags & NOBLUDGEON)) - to_chat(user, span_notice("You start washing [attacking_item]...")) + to_chat(user, span_notice("You start washing [tool]...")) playsound(src, 'sound/machines/sink-faucet.ogg', 50) + + var/obj/item/melee/baton/security/baton = tool + if(istype(baton) && baton.active && baton.cell?.use(baton.cell_hit_cost, force = TRUE)) + flick("baton_active", src) + user.Paralyze(baton.knockdown_time) + user.set_stutter(baton.knockdown_time) + user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \ + span_userdanger("You unwisely attempt to wash [baton] while it's still on.")) + playsound(src, baton.on_stun_sound, 50, TRUE) + return ITEM_INTERACT_FAILURE + busy = TRUE if(!do_after(user, 4 SECONDS, target = src)) busy = FALSE - return 1 + return ITEM_INTERACT_FAILURE busy = FALSE - attacking_item.wash(CLEAN_WASH) - reagents.expose(attacking_item, TOUCH, 5 / max(reagents.total_volume, 5)) - user.visible_message(span_notice("[user] washes [attacking_item] using [src]."), \ - span_notice("You wash [attacking_item] using [src].")) - return 1 - else - return ..() + tool.wash(CLEAN_WASH) + reagents.expose(tool, TOUCH, 5 / max(reagents.total_volume, 5)) + reagents.remove_all(5) + START_PROCESSING(SSobj, src) + user.visible_message(span_notice("[user] washes [tool] using [src]."), \ + span_notice("You wash [tool] using [src].")) + return ITEM_INTERACT_SUCCESS + +/obj/structure/sink/wrench_act(mob/living/user, obj/item/tool) + tool.play_tool_sound(src) + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS -/obj/structure/sink/atom_deconstruct(dissambled = TRUE) - drop_materials() - if(has_water_reclaimer) - new /obj/item/stock_parts/water_recycler(drop_location()) +/obj/structure/sink/crowbar_act(mob/living/user, obj/item/tool) + . = ..() + + if(!has_water_reclaimer) + to_chat(user, span_warning("There isn't a water recycler to remove.")) + return ITEM_INTERACT_FAILURE + + tool.play_tool_sound(src) + has_water_reclaimer = FALSE + new/obj/item/stock_parts/water_recycler(get_turf(loc)) + to_chat(user, span_notice("You remove the water reclaimer from [src].")) + return ITEM_INTERACT_SUCCESS /obj/structure/sink/process(seconds_per_tick) // Water reclamation complete? - if(!has_water_reclaimer || reagents.total_volume >= reagents.maximum_volume) + if(!has_water_reclaimer || reagents.holder_full()) return PROCESS_KILL reagents.add_reagent(dispensedreagent, reclaim_rate * seconds_per_tick) -/obj/structure/sink/proc/drop_materials() - if(buildstacktype) - new buildstacktype(loc,buildstackamount) - else - for(var/i in custom_materials) - var/datum/material/M = i - new M.sheet_type(loc, FLOOR(custom_materials[M] / SHEET_MATERIAL_AMOUNT, 1)) - -/obj/structure/sink/proc/begin_reclamation() - START_PROCESSING(SSobj, src) - /obj/structure/sink/kitchen name = "kitchen sink" icon_state = "sink_alt" pixel_z = 4 - pixel_shift = 16 MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16)) @@ -247,58 +262,56 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16)) icon_state = "sink_greyscale" material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS buildstacktype = null + has_water_reclaimer = FALSE + +/obj/structure/sink/greyscale/setDir(newdir) + return ..(REVERSE_DIR(newdir)) + +/obj/structure/sink/greyscale/filled + has_water_reclaimer = TRUE // DARKPACK EDIT ADD START // Please no more map varedits for this. /obj/structure/sink/basin icon_state = "basin" pixel_z = 0 - pixel_shift = 0 + // pixel_shift = 0 MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/basin, (0)) // DARKPACK EDIT ADD END -/obj/structure/sinkframe +/obj/item/wallframe/sinkframe name = "sink frame" icon = 'icons/obj/watercloset.dmi' icon_state = "sink_frame" desc = "A sink frame, that needs a water recycler to finish construction." - anchored = FALSE + result_path = /obj/structure/sink/greyscale material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS + pixel_shift = 16 + throw_range = 1 -/obj/structure/sinkframe/Initialize(mapload) - . = ..() - AddElement(/datum/element/simple_rotation) +/obj/item/wallframe/sinkframe/add_context(atom/source, list/context, obj/item/held_item, mob/user) + if(istype(held_item, /obj/item/stock_parts/water_recycler) && result_path == /obj/structure/sink/greyscale) + context[SCREENTIP_CONTEXT_LMB] = "Install recycler" + return CONTEXTUAL_SCREENTIP_SET -/obj/structure/sinkframe/attackby(obj/item/tool, mob/living/user, list/modifiers, list/attack_modifiers) - if(istype(tool, /obj/item/stock_parts/water_recycler)) - qdel(tool) - var/obj/structure/sink/greyscale/new_sink = new(loc, REVERSE_DIR(dir), TRUE) - new_sink.set_custom_materials(custom_materials) - qdel(src) - playsound(new_sink, 'sound/machines/click.ogg', 20, TRUE) - return return ..() -/obj/structure/sinkframe/wrench_act(mob/living/user, obj/item/tool) - . = ..() - - tool.play_tool_sound(src) - var/obj/structure/sink/greyscale/new_sink = new(loc, REVERSE_DIR(dir), FALSE) - new_sink.set_custom_materials(custom_materials) - qdel(src) - - return TRUE - -/obj/structure/sinkframe/wrench_act_secondary(mob/living/user, obj/item/tool) +/obj/item/wallframe/sinkframe/examine(mob/user) . = ..() - tool.play_tool_sound(src) - deconstruct() - return TRUE + if(result_path == /obj/structure/sink/greyscale/filled) + . += span_notice("It has a [EXAMINE_HINT("water recycler")] installed.") + else + . += span_notice("It can be fitted with a [EXAMINE_HINT("water recycler")].") -/obj/structure/sinkframe/atom_deconstruct(dissambled = TRUE) - drop_materials() +/obj/item/wallframe/sinkframe/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = NONE + if(istype(tool, /obj/item/stock_parts/water_recycler)) + qdel(tool) + result_path = /obj/structure/sink/greyscale/filled + playsound(src, 'sound/machines/click.ogg', 20, TRUE) + return ITEM_INTERACT_SUCCESS -/obj/structure/sinkframe/proc/drop_materials() - for(var/datum/material/material as anything in custom_materials) - new material.sheet_type(loc, FLOOR(custom_materials[material] / SHEET_MATERIAL_AMOUNT, 1)) +/obj/item/wallframe/sinkframe/after_attach(obj/structure/sink/greyscale/attached_to) + attached_to.set_custom_materials(custom_materials) + attached_to.update_appearance(UPDATE_OVERLAYS) diff --git a/code/game/say.dm b/code/game/say.dm index e7c96b7aa494..a170a2f08309 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -165,10 +165,10 @@ GLOBAL_LIST_INIT(freqtospan, list( var/atom/movable/virtualspeaker/fakespeaker = reliable_narrator reliable_narrator = fakespeaker.source - if(ismob(src) && (namepart != "Unknown") && ismob(reliable_narrator)) + if(ismob(src) && ishuman(reliable_narrator) && (namepart != "Unknown")) var/mob/receiver_mob = src if(receiver_mob.mind?.guestbook) - var/mob/speaker_human = reliable_narrator + var/mob/living/carbon/human/speaker_human = reliable_narrator var/known_name = receiver_mob.mind.guestbook.get_known_name(src, reliable_narrator, speaker_human.real_name) if(known_name) namepart = "[known_name]" diff --git a/code/game/sound/sound.dm b/code/game/sound/sound.dm index d57ca4754ad1..5cea9ea1066e 100644 --- a/code/game/sound/sound.dm +++ b/code/game/sound/sound.dm @@ -110,7 +110,7 @@ * * volume_preference - Optional: Will be checked to modify the volume of the sound. */ /mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/sound_to_use, max_distance, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = 1, use_reverb = TRUE, datum/preference/numeric/volume/volume_preference = null) - if(!client || !can_hear()) + if(!client || HAS_TRAIT(src, TRAIT_DEAF)) return if(!sound_to_use) diff --git a/code/game/turfs/closed/wall/mineral_walls.dm b/code/game/turfs/closed/wall/mineral_walls.dm index d06f83e68537..02e40a4054ec 100644 --- a/code/game/turfs/closed/wall/mineral_walls.dm +++ b/code/game/turfs/closed/wall/mineral_walls.dm @@ -190,6 +190,8 @@ desc = "A solidly wooden wall. It's a bit weaker than a wall made with metal." girder_type = /obj/structure/barricade/wooden hardness = 67 //a bit weaker than iron (60) + sheet_amount = 5 + make_delay = 5 SECONDS /turf/closed/wall/mineral/bamboo name = "bamboo wall" diff --git a/code/game/turfs/closed/wall/reinf_walls.dm b/code/game/turfs/closed/wall/reinf_walls.dm index 525b9540b655..2fd14fda8804 100644 --- a/code/game/turfs/closed/wall/reinf_walls.dm +++ b/code/game/turfs/closed/wall/reinf_walls.dm @@ -12,6 +12,8 @@ sheet_type = /obj/item/stack/sheet/plasteel sheet_amount = 1 girder_type = /obj/structure/girder/reinforced + girder_state = GIRDER_REINF + make_delay = 5 SECONDS explosive_resistance = 2 rad_insulation = RAD_HEAVY_INSULATION rust_resistance = RUST_RESISTANCE_REINFORCED diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm index ed29d3cebe42..2501b3767b38 100644 --- a/code/game/turfs/closed/walls.dm +++ b/code/game/turfs/closed/walls.dm @@ -24,16 +24,23 @@ ///lower numbers are harder. Used to determine the probability of a hulk smashing through. var/hardness = 40 var/slicing_duration = 100 //default time taken to slice the wall - var/sheet_type = /obj/item/stack/sheet/iron - var/sheet_amount = 2 - var/girder_type = /obj/structure/girder /// A turf that will replace this turf when this turf is destroyed var/decon_type /// If we added a leaning component to ourselves var/added_leaning = FALSE - var/list/dent_decals + /// The type of sheet this wall requires for construction and drops upon deconstruction. + var/sheet_type = /obj/item/stack/sheet/iron + /// The amount of sheets this wall requires for construction and drops upon deconstruction. + var/sheet_amount = 2 + /// The type of girder this wall requires for construction and drops upon deconstruction. + var/girder_type = /obj/structure/girder + /// The girder state this wall requires for construction and drops upon deconstruction. + var/girder_state = GIRDER_NORMAL + /// How long this wall takes to make by using its sheet type on a girder. + var/make_delay = 4 SECONDS + /turf/closed/wall/Initialize(mapload) . = ..() if(!can_engrave) diff --git a/code/game/world.dm b/code/game/world.dm index b687b25256db..9a81e19d97f1 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -29,7 +29,7 @@ GLOBAL_VAR(restart_counter) * - config.Load() * - world.InitTgs() => * - TgsNew() *may sleep - * - GLOB.rev_data.load_tgs_info() + * - GLOB.rev_data.load_tgs_info() *may sleep * - world.ConfigLoaded() => * - SSdbcore.InitializeRound() * - world.SetupLogs() diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm index 8070c5d6f340..0f1147cdc283 100644 --- a/code/modules/admin/verbs/ert.dm +++ b/code/modules/admin/verbs/ert.dm @@ -75,14 +75,14 @@ if (ertemplate) ertemplate = new ertemplate else - ertemplate = new /datum/ert/centcom_official + ertemplate = new /datum/ert/darkpack/swat // DARKPACK EDIT CHANGE - ORIGINAL: ertemplate = new /datum/ert/centcom_official var/human_authority_setting = CONFIG_GET(string/human_authority) var/list/settings = list( "preview_callback" = CALLBACK(src, PROC_REF(makeERTPreviewIcon)), "mainsettings" = list( - "template" = list("desc" = "Template", "callback" = CALLBACK(src, PROC_REF(makeERTTemplateModified)), "type" = "datum", "path" = "/datum/ert", "subtypesonly" = TRUE, "value" = ertemplate.type), + "template" = list("desc" = "Template", "callback" = CALLBACK(src, PROC_REF(makeERTTemplateModified)), "type" = "datum", "path" = "/datum/ert/darkpack", "subtypesonly" = TRUE, "value" = ertemplate.type), // DARKPACK EDIT CHANGE - Original : "template" = list("desc" = "Template", "callback" = CALLBACK(src, PROC_REF(makeERTTemplateModified)), "type" = "datum", "path" = "/datum/ert", "subtypesonly" = TRUE, "value" = ertemplate.type), "teamsize" = list("desc" = "Team Size", "type" = "number", "value" = ertemplate.teamsize), "mission" = list("desc" = "Mission", "type" = "string", "value" = ertemplate.mission), "polldesc" = list("desc" = "Ghost poll description", "type" = "string", "value" = ertemplate.polldesc), diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index a0877e803d82..e38f7da3b662 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -140,7 +140,12 @@ GLOBAL_VAR_INIT(web_sound_cooldown, 0) if(credit) to_chat_message += span_notice("
[credit]") - to_chat(world, fieldset_block("Now Playing: [span_bold(music_extra_data["title"])] by [span_bold(music_extra_data["artist"])]", jointext(to_chat_message, ""), "boxed_message")) + var/list/recipients = list() + for(var/client/client as anything in GLOB.clients) + if(client.prefs.read_preference(/datum/preference/numeric/volume/sound_midi) > 0) + recipients += client + recipients |= user.client + to_chat(recipients, fieldset_block("Now Playing: [span_bold(music_extra_data["title"])] by [span_bold(music_extra_data["artist"])]", jointext(to_chat_message, ""), "boxed_message")) SSblackbox.record_feedback("nested tally", "played_url", 1, list("[user.ckey]", "[input]")) log_admin("[key_name(user)] played web sound: [input]") diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 88f50b3cb3e2..aecb24dfd673 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -349,16 +349,6 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w airlock.req_one_access = list() message_admins("[key_name_admin(holder)] activated Egalitarian Station mode") priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, SSstation.announcer.get_rand_report_sound()) - if("ancap") - if(!is_funmin) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Anarcho-capitalist Station")) - SSeconomy.full_ancap = !SSeconomy.full_ancap - message_admins("[key_name_admin(holder)] toggled Anarcho-capitalist mode") - if(SSeconomy.full_ancap) - priority_announce("The NAP is now in full effect.", null, SSstation.announcer.get_rand_report_sound()) - else - priority_announce("The NAP has been revoked.", null, SSstation.announcer.get_rand_report_sound()) if("send_shuttle_back") if (!is_funmin) return @@ -687,6 +677,20 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w message_admins("[key_name_admin(holder)] healed everyone.") log_admin("[key_name(holder)] healed everyone.") + if("cascade") + if(!is_funmin) + return + message_admins("[key_name_admin(holder)] started a resonance cascade! You're supposed to be a scientist! Use your common sense!") + for(var/obj/machinery/power/supermatter_crystal/S in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/power/supermatter_crystal)) + if(!S.is_main_engine) + continue + S.explosion_point = 0 + S.set_delam(SM_DELAM_PRIO_IN_GAME, /datum/sm_delam/cascade) + S.external_damage_immediate += 200 + S.count_down() + return + return + if(holder) log_admin("[key_name(holder)] used secret: [action].") #undef THUNDERDOME_TEMPLATE_FILE diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm index b9fd0e6d2ad6..858af794365d 100644 --- a/code/modules/admin/view_variables/reference_tracking.dm +++ b/code/modules/admin/view_variables/reference_tracking.dm @@ -126,6 +126,7 @@ else if(islist(potential_container)) var/list/potential_cache = potential_container + var/is_alist = istype(potential_cache, /alist) for(var/element_in_list in potential_cache) //Check normal sublists if(islist(element_in_list)) @@ -162,7 +163,7 @@ log_reftracker("All references to [type] [text_ref(src)] found, exiting.") return - if(!isnum(element_in_list) && !is_special_list) + if((!isnum(element_in_list) || is_alist) && !is_special_list) // This exists to catch an error that throws when we access a special list // is_special_list is a hint, it can be wrong try diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 4e6ade226cd9..ac45ba082427 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -57,6 +57,8 @@ GLOBAL_LIST_EMPTY(antagonists) var/hardcore_random_bonus = FALSE /// A path to the audio stinger that plays upon gaining this datum. var/stinger_sound + /// Multiplicative modifier to the mind's desensitized level when this antagonist is applied. Minimum is 0.1. + var/desensitized_modifier = 1.0 //ANTAG UI @@ -262,6 +264,7 @@ GLOBAL_LIST_EMPTY(antagonists) if(type_policy) to_chat(owner.current, type_policy) + owner.desensitized_level *= max(DESENSITIZED_MINIMUM, desensitized_modifier) apply_innate_effects() give_antag_moodies() RegisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT, PROC_REF(pre_mindshield)) @@ -320,6 +323,7 @@ GLOBAL_LIST_EMPTY(antagonists) if(!owner) CRASH("Antag datum with no owner.") + owner.desensitized_level /= max(DESENSITIZED_MINIMUM, desensitized_modifier) if(owner.current) remove_innate_effects() clear_antag_moodies() diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 3cdbda71f79d..fd75708e57f1 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -484,9 +484,7 @@ Return to step 11 of normal process."} /obj/item/restraints/handcuffs/energy/on_uncuffed(datum/source, mob/living/wearer) . = ..() wearer.visible_message(span_danger("[wearer]'s [name] breaks in a discharge of energy!"), span_userdanger("[wearer]'s [name] breaks in a discharge of energy!")) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(4,0,wearer.loc) - sparks.start() + do_sparks(4, FALSE, wearer.loc) qdel(src) /obj/item/melee/baton/abductor/examine(mob/user) diff --git a/code/modules/antagonists/abductor/machinery/pad.dm b/code/modules/antagonists/abductor/machinery/pad.dm index 1610d9a6cff3..391ac36789aa 100644 --- a/code/modules/antagonists/abductor/machinery/pad.dm +++ b/code/modules/antagonists/abductor/machinery/pad.dm @@ -58,9 +58,7 @@ /obj/effect/temp_visual/teleport_abductor/Initialize(mapload) . = ..() - var/datum/effect_system/spark_spread/S = new - S.set_up(10,0,loc) - S.start() + do_sparks(10, FALSE, loc) /obj/effect/temp_visual/teleport_golem name = "bluespace silhouette" diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm index 017dbf49d6ef..0be336ff584d 100644 --- a/code/modules/antagonists/cult/cult_items.dm +++ b/code/modules/antagonists/cult/cult_items.dm @@ -593,6 +593,7 @@ Striking a noncultist, however, will tear their flesh."} lefthand_file = 'icons/mob/inhands/items/drinks_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi' list_reagents = list(/datum/reagent/fuel/unholywater = 50) + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/unholywater/Initialize(mapload) . = ..() diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm index 7cbd9fe6284a..130683a9046f 100644 --- a/code/modules/antagonists/cult/datums/cult_team.dm +++ b/code/modules/antagonists/cult/datums/cult_team.dm @@ -58,7 +58,6 @@ SEND_SOUND(mind.current, 'sound/music/antag/bloodcult/bloodcult_eyes.ogg') to_chat(mind.current, span_cult_large(span_warning("The veil weakens as your cult grows, your eyes begin to glow..."))) mind.current.AddElement(/datum/element/cult_eyes) - ADD_TRAIT(mind.current, TRAIT_DESENSITIZED, CULT_TRAIT) cult_risen = TRUE log_game("The blood cult has risen with [cultplayers] players.") diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm index 9ce4dc5b13af..c637b2b45cb3 100644 --- a/code/modules/antagonists/cult/datums/cultist.dm +++ b/code/modules/antagonists/cult/datums/cultist.dm @@ -8,6 +8,7 @@ pref_flag = ROLE_CULTIST antag_hud_name = "cult" stinger_sound = 'sound/music/antag/bloodcult/bloodcult_gain.ogg' + desensitized_modifier = DESENSITIZED_THRESHOLD ///Boolean on whether the starting equipment should be given to their inventory. var/give_equipment = FALSE @@ -65,7 +66,6 @@ if(cult_team.cult_risen) current.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS) - ADD_TRAIT(current, TRAIT_DESENSITIZED, CULT_TRAIT) if(cult_team.cult_ascendent) current.AddElement(/datum/element/cult_halo, initial_delay = 0 SECONDS) @@ -88,7 +88,6 @@ if (HAS_TRAIT(current, TRAIT_CULT_HALO)) current.RemoveElement(/datum/element/cult_halo) - REMOVE_TRAIT(current, TRAIT_DESENSITIZED, CULT_TRAIT) REMOVE_TRAIT(current, TRAIT_HEALS_FROM_CULT_PYLONS, CULT_TRAIT) /datum/antagonist/cult/on_mindshield(mob/implanter) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index fbadeba3518d..be3a5b92d2dd 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -242,7 +242,7 @@ structure_check() searches for nearby cultist structures required for the invoca if(!IS_CULTIST(non_cultist)) myriad_targets += non_cultist - if(!length(myriad_targets) && !try_spawn_sword()) + if(!length(myriad_targets) && !try_sacrifice_item()) fail_invoke() return @@ -400,57 +400,38 @@ structure_check() searches for nearby cultist structures required for the invoca sacrificial.investigate_log("has been sacrificially gibbed by the cult.", INVESTIGATE_DEATHS) sacrificial.gib(DROP_ALL_REMAINS) - try_spawn_sword() // after sharding and gibbing, which potentially dropped a null rod + try_sacrifice_item() // after sharding and gibbing, which potentially dropped a sacrificable item return TRUE -/// Tries to convert a null rod over the rune to a cult sword -/obj/effect/rune/convert/proc/try_spawn_sword() - for(var/obj/item/potential_rod in loc) - if(!HAS_TRAIT(potential_rod, TRAIT_NULLROD_ITEM)) +/// Tries to convert a valid item over the rune to something else +/obj/effect/rune/convert/proc/try_sacrifice_item() + for(var/obj/item/checked_item in loc) + if(checked_item.anchored || (checked_item.resistance_flags & INDESTRUCTIBLE)) continue - if(potential_rod.anchored || (potential_rod.resistance_flags & INDESTRUCTIBLE)) - continue - - var/num_slain = 0 - if (istype(potential_rod, /obj/item/nullrod)) - var/obj/item/nullrod/actual_rod = potential_rod - num_slain = LAZYLEN(actual_rod.cultists_slain) - - var/displayed_message = "[potential_rod] glows an unholy red and begins to transform..." - if(num_slain && GET_ATOM_BLOOD_DNA_LENGTH(potential_rod)) - displayed_message += " The blood of [num_slain] fallen cultist[num_slain == 1 ? "":"s"] is absorbed into [potential_rod]!" - - potential_rod.visible_message(span_cult_italic(displayed_message)) - switch(num_slain) - if(0) - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade/dagger) - if(1) - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade) - else - animate_spawn_sword(potential_rod, /obj/item/melee/cultblade/halberd) - return TRUE + if(SEND_SIGNAL(checked_item, COMSIG_ITEM_CULT_SACRIFICE, src) & COMPONENT_SACRIFICE_SUCCESSFUL) + return TRUE return FALSE -/// Does an animation of a null rod transforming into a cult sword -/obj/effect/rune/convert/proc/animate_spawn_sword(obj/item/former_rod, new_blade_typepath) +/// Does an animation of a sacrificable item transforming into something else +/obj/effect/rune/convert/proc/animate_convert_item(obj/item/old_item, new_movable_typepath) playsound(src, 'sound/effects/magic.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.66) - former_rod.anchored = TRUE - former_rod.Shake() - animate(former_rod, alpha = 0, transform = matrix(former_rod.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) - QDEL_IN(former_rod, 2 SECONDS) - - var/obj/item/new_blade = new new_blade_typepath(loc) - var/matrix/blade_matrix_on_spawn = matrix(new_blade.transform) - new_blade.name = "converted [new_blade.name]" - new_blade.anchored = TRUE - new_blade.alpha = 0 - new_blade.transform = matrix(new_blade.transform).Scale(0.01) - new_blade.Shake() - animate(new_blade, alpha = 255, transform = blade_matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) - addtimer(VARSET_CALLBACK(new_blade, anchored, FALSE), 2 SECONDS) + old_item.anchored = TRUE + old_item.Shake() + animate(old_item, alpha = 0, transform = matrix(old_item.transform).Scale(0.01), time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + QDEL_IN(old_item, 2 SECONDS) + + var/atom/movable/new_movable = new new_movable_typepath(loc) + var/matrix/matrix_on_spawn = matrix(new_movable.transform) + new_movable.name = "converted [new_movable.name]" + new_movable.anchored = TRUE + new_movable.alpha = 0 + new_movable.transform = matrix(new_movable.transform).Scale(0.01) + new_movable.Shake() + animate(new_movable, alpha = 255, transform = matrix_on_spawn, time = 2 SECONDS, easing = BOUNCE_EASING, flags = ANIMATION_PARALLEL) + addtimer(VARSET_CALLBACK(new_movable, anchored, FALSE), 2 SECONDS) /obj/effect/rune/empower cultist_name = "Empower" diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm index c0bf50819e01..baa59a14dfd1 100644 --- a/code/modules/antagonists/ert/ert.dm +++ b/code/modules/antagonists/ert/ert.dm @@ -13,6 +13,7 @@ suicide_cry = "FOR NANOTRASEN!!" // Not 'true' antags, this disables certain interactions that assume the owner is a baddie antag_flags = ANTAG_FAKE|ANTAG_SKIP_GLOBAL_LIST + desensitized_modifier = DESENSITIZED_THRESHOLD * 0.5 var/datum/team/ert/ert_team var/leader = FALSE var/datum/outfit/outfit = /datum/outfit/centcom/ert/security @@ -36,14 +37,6 @@ equipERT() . = ..() -/datum/antagonist/ert/apply_innate_effects(mob/living/mob_override) - var/mob/living/carbon/human/officer = mob_override || owner.current - ADD_TRAIT(officer, TRAIT_DESENSITIZED, REF(src)) - -/datum/antagonist/ert/remove_innate_effects(mob/living/mob_override) - var/mob/living/carbon/human/officer = mob_override || owner.current - REMOVE_TRAIT(officer, TRAIT_DESENSITIZED, REF(src)) - /datum/antagonist/ert/get_team() return ert_team @@ -258,7 +251,7 @@ to_chat(owner, "You are the [name].") - var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division." + var/missiondesc = "Your squad is being sent on a mission to [station_name()]." // DARKPACK EDIT CHANGE - Original : var/missiondesc = "Your squad is being sent on a mission to [station_name()] by Nanotrasen's Security Division." if(leader) //If Squad Leader missiondesc += " Lead your squad to ensure the completion of the mission. Board the shuttle when your team is ready." else diff --git a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm index 34d10b057890..dd829c4ce8a4 100644 --- a/code/modules/antagonists/fugitive/hunters/hunter_gear.dm +++ b/code/modules/antagonists/fugitive/hunters/hunter_gear.dm @@ -106,9 +106,7 @@ color = "#d6ad8b" /obj/item/clothing/suit/armor/reactive/psykerboost/cooldown_activation(mob/living/carbon/human/owner) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(1, 1, src) - sparks.start() + do_sparks(1, TRUE, src) return ..() /obj/item/clothing/suit/armor/reactive/psykerboost/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) diff --git a/code/modules/antagonists/heretic/heretic_curses.dm b/code/modules/antagonists/heretic/heretic_curses.dm index f9ae9fde801d..1893bc725587 100644 --- a/code/modules/antagonists/heretic/heretic_curses.dm +++ b/code/modules/antagonists/heretic/heretic_curses.dm @@ -3,7 +3,7 @@ */ /datum/heretic_knowledge/curse - abstract_parent_type = /datum/heretic_knowledge/curse + abstract_type = /datum/heretic_knowledge/curse /// How far can we curse people? var/max_range = 64 /// The duration of the curse @@ -134,7 +134,7 @@ //---- Curse of Paralysis /datum/heretic_knowledge/curse/paralysis - abstract_parent_type = /datum/heretic_knowledge/curse/paralysis + abstract_type = /datum/heretic_knowledge/curse/paralysis name = "Curse of Paralysis" desc = "Allows you to transmute a hatchet and both a left and right leg to cast a curse of immobility on a crew member. \ While cursed, the victim will be unable to walk. You can additionally supply an item that a victim has touched \ @@ -169,7 +169,7 @@ //---- Curse of Corrosion /datum/heretic_knowledge/curse/corrosion - abstract_parent_type = /datum/heretic_knowledge/curse/corrosion + abstract_type = /datum/heretic_knowledge/curse/corrosion name = "Curse of Corrosion" desc = "Allows you to transmute wirecutters, a pool of vomit, and a heart to cast a curse of sickness on a crew member. \ While cursed, the victim will repeatedly vomit while their organs will take constant damage. You can additionally supply an item \ @@ -198,7 +198,7 @@ //---- Curse of Transmutation /datum/heretic_knowledge/curse/transmutation - abstract_parent_type = /datum/heretic_knowledge/curse/transmutation + abstract_type = /datum/heretic_knowledge/curse/transmutation name = "Curse of Transmutation" duration = 0 // Infinite curse, it breaks when our codex is destroyed curse_color = NONE @@ -266,7 +266,7 @@ //---- Curse of Indulgence /datum/heretic_knowledge/curse/indulgence - abstract_parent_type = /datum/heretic_knowledge/curse/indulgence + abstract_type = /datum/heretic_knowledge/curse/indulgence name = "Curse of Indulgence" duration = 8 MINUTES curse_color = COLOR_MAROON diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm index af0381db3cc3..d1a1ee28bebe 100644 --- a/code/modules/antagonists/heretic/heretic_knowledge.dm +++ b/code/modules/antagonists/heretic/heretic_knowledge.dm @@ -9,14 +9,14 @@ * */ /datum/heretic_knowledge + /// The abstract parent type of the knowledge, used in determine mutual exclusivity in some cases + abstract_type = /datum/heretic_knowledge /// Name of the knowledge, shown to the heretic. var/name = "Basic knowledge" /// Description of the knowledge, shown to the heretic. Describes what it unlocks / does. var/desc = "Basic knowledge of forbidden arts." /// What's shown to the heretic when the knowledge is acquired 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 /// 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' @@ -191,7 +191,7 @@ * A knowledge subtype that grants the heretic a certain spell. */ /datum/heretic_knowledge/spell - abstract_parent_type = /datum/heretic_knowledge/spell + abstract_type = /datum/heretic_knowledge/spell /// Spell path we add to the heretic. Type-path. var/datum/action/action_to_add /// The spell we actually created. @@ -220,7 +220,7 @@ * created at once. */ /datum/heretic_knowledge/limited_amount - abstract_parent_type = /datum/heretic_knowledge/limited_amount + abstract_type = /datum/heretic_knowledge/limited_amount /// The limit to how many items we can create at once. var/limit = 1 /// A list of weakrefs to all items we've created. @@ -262,7 +262,7 @@ * and their ascension depends on whichever they chose. */ /datum/heretic_knowledge/limited_amount/starting - abstract_parent_type = /datum/heretic_knowledge/limited_amount/starting + abstract_type = /datum/heretic_knowledge/limited_amount/starting limit = 2 cost = 1 priority = MAX_KNOWLEDGE_PRIORITY - 5 @@ -362,7 +362,7 @@ * A heretic can only learn one /blade_upgrade type knowledge. */ /datum/heretic_knowledge/blade_upgrade - abstract_parent_type = /datum/heretic_knowledge/blade_upgrade + abstract_type = /datum/heretic_knowledge/blade_upgrade cost = 1 /datum/heretic_knowledge/blade_upgrade/on_gain(mob/user, datum/antagonist/heretic/our_heretic) @@ -411,7 +411,7 @@ * A knowledge subtype lets the heretic summon a monster with the ritual. */ /datum/heretic_knowledge/summon - abstract_parent_type = /datum/heretic_knowledge/summon + abstract_type = /datum/heretic_knowledge/summon /// Typepath of a mob to summon when we finish the recipe. var/mob/living/mob_to_summon @@ -476,7 +476,7 @@ name = "Ritual of Knowledge" 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 + abstract_type = /datum/heretic_knowledge/knowledge_ritual cost = 1 priority = MAX_KNOWLEDGE_PRIORITY - 10 // A pretty important midgame ritual. research_tree_icon_path = 'icons/obj/antags/eldritch.dmi' @@ -564,7 +564,7 @@ * The special final tier of knowledges that unlocks ASCENSION. */ /datum/heretic_knowledge/ultimate - abstract_parent_type = /datum/heretic_knowledge/ultimate + abstract_type = /datum/heretic_knowledge/ultimate 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) @@ -654,7 +654,7 @@ if(!isnull(ascension_achievement)) user.client?.give_award(ascension_achievement, user) heretic_datum.rust_strength = RUST_RESISTANCE_ORGANIC // Ascended heretics can rust whatever they want (below RUST_RESISTANCE_ABSOLUTE) - ADD_TRAIT(user, TRAIT_DESENSITIZED, type) + user.apply_status_effect(/datum/status_effect/desensitized, type, DESENSITIZED_THRESHOLD * 0.4) return TRUE /datum/heretic_knowledge/ultimate/cleanup_atoms(list/selected_atoms) diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index 659ed72854ba..71f79a51cbd1 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -147,8 +147,7 @@ else human_user.gib(DROP_ALL_REMAINS) human_user.investigate_log("has died from using telekinesis on a heretic influence.", INVESTIGATE_DEATHS) - var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(1, get_turf(human_user), 1) + var/datum/effect_system/reagents_explosion/explosion = new(get_turf(human_user), 1, 1, 1) explosion.start(src) /obj/effect/visible_heretic_influence/examine(mob/living/user) diff --git a/code/modules/antagonists/heretic/items/eldritch_flask.dm b/code/modules/antagonists/heretic/items/eldritch_flask.dm index 02adc82d9fa1..5ef62fa410b4 100644 --- a/code/modules/antagonists/heretic/items/eldritch_flask.dm +++ b/code/modules/antagonists/heretic/items/eldritch_flask.dm @@ -6,6 +6,7 @@ icon = 'icons/obj/antags/eldritch.dmi' icon_state = "eldritch_flask" list_reagents = list(/datum/reagent/eldritch = 50) + can_lid = FALSE // Unique bottle that lets you instantly draw blood from a victim /obj/item/reagent_containers/cup/phylactery diff --git a/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm index 5890e459c162..6bdda2d115c6 100644 --- a/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm +++ b/code/modules/antagonists/heretic/knowledge/_heretic_paths.dm @@ -5,14 +5,14 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) /proc/init_heretic_path_datums() var/list/paths = list() - for(var/datum/heretic_knowledge_tree_column/column_path as anything in subtypesof(/datum/heretic_knowledge_tree_column)) - if(initial(column_path.abstract_parent_type) == column_path) - continue + for(var/datum/heretic_knowledge_tree_column/column_path as anything in valid_subtypesof(/datum/heretic_knowledge_tree_column)) var/datum/heretic_knowledge_tree_column/heretic_route = new column_path() paths[heretic_route.route] += heretic_route return paths /datum/heretic_knowledge_tree_column + ///Used to determine if this is a side path or a main path + abstract_type = /datum/heretic_knowledge_tree_column ///Route that symbolizes what path this is, MUST be unique between paths var/route = PATH_START var/icon_state = "dark_blade" @@ -35,8 +35,6 @@ GLOBAL_LIST_INIT(heretic_path_datums, init_heretic_path_datums()) var/list/pros = list("Is bad", "Is very bad", "Is extremely bad") var/list/cons = list("Smells bad", "Looks bad", "Tastes bad") var/list/tips = list("Don't use it", "Don't touch it", "Don't look at it") - ///Used to determine if this is a side path or a main path - var/abstract_parent_type = /datum/heretic_knowledge_tree_column ///UI background var/ui_bgr = BGR_SIDE diff --git a/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm b/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm index 5bd0379cfe30..243c84b2ed91 100644 --- a/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/heretic_armor_knowledge.dm @@ -9,7 +9,7 @@ list(/obj/structure/table, /obj/item/clothing/suit) = 1, /obj/item/clothing/mask = 1, ) - abstract_parent_type = /datum/heretic_knowledge/armor + abstract_type = /datum/heretic_knowledge/armor result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch) cost = 1 diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm index f099050ecfb1..f3a9a9da7124 100644 --- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm +++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm @@ -303,8 +303,7 @@ var/obj/item/bodypart/head/head = locate() in carbon_view.bodyparts if(!head?.dismember()) carbon_view.gib(DROP_ALL_REMAINS) - var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(1, get_turf(carbon_view), 1) + var/datum/effect_system/reagents_explosion/explosion = new(get_turf(carbon_view), 1, 1, 1) explosion.start(src) else attempt_conversion(carbon_view, source) diff --git a/code/modules/antagonists/heretic/magic/rust_wave.dm b/code/modules/antagonists/heretic/magic/rust_wave.dm index 70e36471a21a..379a86322922 100644 --- a/code/modules/antagonists/heretic/magic/rust_wave.dm +++ b/code/modules/antagonists/heretic/magic/rust_wave.dm @@ -93,7 +93,7 @@ damage_type = TOX hitsound = 'sound/items/weapons/punch3.ogg' trigger_range = 0 - ignored_factions = list(FACTION_HERETIC) + faction = list(FACTION_HERETIC) range = 15 speed = 1 diff --git a/code/modules/antagonists/heretic/status_effects/ghoul.dm b/code/modules/antagonists/heretic/status_effects/ghoul.dm index 74e60c966a46..bc2f524e7565 100644 --- a/code/modules/antagonists/heretic/status_effects/ghoul.dm +++ b/code/modules/antagonists/heretic/status_effects/ghoul.dm @@ -62,9 +62,9 @@ on_made_callback?.Invoke(human_target) ADD_TRAIT(human_target, TRAIT_FAKEDEATH, TRAIT_STATUS_EFFECT(id)) ADD_TRAIT(human_target, TRAIT_HERETIC_SUMMON, TRAIT_STATUS_EFFECT(id)) - ADD_TRAIT(human_target, TRAIT_DESENSITIZED, TRAIT_STATUS_EFFECT(id)) human_target.become_husk(TRAIT_STATUS_EFFECT(id)) human_target.add_faction(FACTION_HERETIC) + human_target.apply_status_effect(/datum/status_effect/desensitized, TRAIT_STATUS_EFFECT(id), DESENSITIZED_THRESHOLD * 0.2) if(human_target.mind) var/datum/antagonist/heretic_monster/heretic_monster = human_target.mind.add_antag_datum(/datum/antagonist/heretic_monster) @@ -93,9 +93,9 @@ on_lost_callback?.Invoke(human_target) REMOVE_TRAIT(human_target, TRAIT_FAKEDEATH, TRAIT_STATUS_EFFECT(id)) REMOVE_TRAIT(human_target, TRAIT_HERETIC_SUMMON, TRAIT_STATUS_EFFECT(id)) - REMOVE_TRAIT(human_target, TRAIT_DESENSITIZED, TRAIT_STATUS_EFFECT(id)) human_target.cure_husk(TRAIT_STATUS_EFFECT(id)) human_target.remove_faction(FACTION_HERETIC) + human_target.remove_status_effect(/datum/status_effect/desensitized, TRAIT_STATUS_EFFECT(id)) human_target.mind?.remove_antag_datum(/datum/antagonist/heretic_monster) UnregisterSignal(human_target, COMSIG_LIVING_DEATH) diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm index d197f91f5732..39a6a52eef9a 100644 --- a/code/modules/antagonists/highlander/highlander.dm +++ b/code/modules/antagonists/highlander/highlander.dm @@ -6,9 +6,9 @@ can_elimination_hijack = ELIMINATION_ENABLED suicide_cry = "FOR SCOTLAND!!" // If they manage to lose their no-drop stuff somehow antag_flags = ANTAG_FAKE|ANTAG_SKIP_GLOBAL_LIST + desensitized_modifier = DESENSITIZED_THRESHOLD * 0.2 /// Traits we apply/remove to our target on-demand. var/static/list/applicable_traits = list( - TRAIT_DESENSITIZED, TRAIT_NOBREATH, TRAIT_NODISMEMBER, TRAIT_NOFIRE, diff --git a/code/modules/antagonists/nightmare/nightmare_equipment.dm b/code/modules/antagonists/nightmare/nightmare_equipment.dm index 56591e13748d..2f5d0c19383b 100644 --- a/code/modules/antagonists/nightmare/nightmare_equipment.dm +++ b/code/modules/antagonists/nightmare/nightmare_equipment.dm @@ -3,14 +3,14 @@ */ /obj/item/light_eater name = "light eater" //as opposed to heavy eater - icon = 'icons/obj/weapons/changeling_items.dmi' - icon_state = "arm_blade" - inhand_icon_state = "arm_blade" + icon = 'icons/obj/weapons/nightmare_items.dmi' + icon_state = "light_eater" + inhand_icon_state = "light_eater" icon_angle = 180 force = 25 armour_penetration = 35 - lefthand_file = 'icons/mob/inhands/antag/changeling_lefthand.dmi' - righthand_file = 'icons/mob/inhands/antag/changeling_righthand.dmi' + lefthand_file = 'icons/mob/inhands/antag/nightmare_lefthand.dmi' + righthand_file = 'icons/mob/inhands/antag/nightmare_righthand.dmi' item_flags = ABSTRACT | DROPDEL resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF | UNACIDABLE w_class = WEIGHT_CLASS_HUGE diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm index d3ab9cf1ac86..6ac1148a2224 100644 --- a/code/modules/antagonists/nightmare/nightmare_organs.dm +++ b/code/modules/antagonists/nightmare/nightmare_organs.dm @@ -78,11 +78,11 @@ name = "heart of darkness" desc = "An alien organ that twists and writhes when exposed to light." visual = TRUE - icon_state = "demon_heart-on" - base_icon_state = "demon_heart" + icon = 'icons/obj/medical/organs/shadow_organs.dmi' + icon_state = "dark_heart-on" + base_icon_state = "dark_heart" beat_noise = "the writhing pulses of a fear given form" // evil schmeevil - color = COLOR_CRAYON_BLACK decay_factor = 0 // No love is to be found in a heart so twisted. food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5) diff --git a/code/modules/antagonists/nukeop/datums/operative.dm b/code/modules/antagonists/nukeop/datums/operative.dm index 59b2ca2f4af3..abf385939903 100644 --- a/code/modules/antagonists/nukeop/datums/operative.dm +++ b/code/modules/antagonists/nukeop/datums/operative.dm @@ -9,6 +9,7 @@ hijack_speed = 2 //If you can't take out the station, take the shuttle instead. suicide_cry = "FOR THE SYNDICATE!!" stinger_sound = 'sound/music/antag/ops.ogg' + desensitized_modifier = DESENSITIZED_THRESHOLD * 0.5 /// Which nukie team are we on? var/datum/team/nuclear/nuke_team @@ -132,7 +133,6 @@ var/mob/living/carbon/human/operative = owner.current ADD_TRAIT(operative, TRAIT_NOFEAR_HOLDUPS, INNATE_TRAIT) - ADD_TRAIT(operative, TRAIT_DESENSITIZED, INNATE_TRAIT) if(!nukeop_outfit) // this variable is null in instances where an antagonist datum is granted via enslaving the mind (/datum/mind/proc/enslave_mind_to_creator), like in golems. return diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm index 10f92ce631d2..807f0e6e6905 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm @@ -124,10 +124,22 @@ GLOBAL_VAR(station_nuke_source) if(NUKESTATE_INTACT) if(istype(weapon, /obj/item/screwdriver/nuke)) to_chat(user, span_notice("You start removing [src]'s front panel's screws...")) - if(weapon.use_tool(src, user, 6 SECONDS, volume = 100)) - deconstruction_state = NUKESTATE_UNSCREWED - to_chat(user, span_notice("You remove the screws from [src]'s front panel.")) - update_appearance() + if(!weapon.use_tool(src, user, 6 SECONDS, volume = 100)) + return TRUE + deconstruction_state = NUKESTATE_UNSCREWED + to_chat(user, span_notice("You remove the screws from [src]'s front panel.")) + update_appearance() + return TRUE + + if(NUKESTATE_UNSCREWED) + if(istype(weapon, /obj/item/screwdriver/nuke)) + to_chat(user, span_notice("You start screwing [src]'s front panel back in...")) + if(!weapon.use_tool(src, user, 8 SECONDS, volume = 100)) + return TRUE + deconstruction_state = NUKESTATE_INTACT + to_chat(user, span_notice("You screw [src]'s front panel back into place.")) + deconstruction_state = NUKESTATE_INTACT + update_appearance() return TRUE if(NUKESTATE_PANEL_REMOVED) @@ -135,24 +147,26 @@ GLOBAL_VAR(station_nuke_source) if(!weapon.tool_start_check(user, amount = 1)) return TRUE to_chat(user, span_notice("You start cutting [src]'s inner plate...")) - if(weapon.use_tool(src, user, 8 SECONDS, volume=100)) - to_chat(user, span_notice("You cut [src]'s inner plate.")) - deconstruction_state = NUKESTATE_WELDED - update_appearance() + if(!weapon.use_tool(src, user, 8 SECONDS, volume=100)) + return TRUE + to_chat(user, span_notice("You cut [src]'s inner plate.")) + deconstruction_state = NUKESTATE_WELDED + update_appearance() return TRUE if(NUKESTATE_CORE_EXPOSED) if(istype(weapon, /obj/item/nuke_core_container)) var/obj/item/nuke_core_container/core_box = weapon to_chat(user, span_notice("You start loading the plutonium core into [core_box]...")) - if(do_after(user, 5 SECONDS, target = src, hidden = TRUE)) - if(core_box.load(core, user)) - to_chat(user, span_notice("You load the plutonium core into [core_box].")) - deconstruction_state = NUKESTATE_CORE_REMOVED - update_appearance() - core = null - else - to_chat(user, span_warning("You fail to load the plutonium core into [core_box]. [core_box] has already been used!")) + if(!do_after(user, 5 SECONDS, target = src, hidden = TRUE)) + return TRUE + if(core_box.load(core, user)) + to_chat(user, span_notice("You load the plutonium core into [core_box].")) + deconstruction_state = NUKESTATE_CORE_REMOVED + update_appearance() + core = null + else + to_chat(user, span_warning("You fail to load the plutonium core into [core_box]. [core_box] has already been used!")) return TRUE if(istype(weapon, /obj/item/stack/sheet/iron)) @@ -160,11 +174,37 @@ GLOBAL_VAR(station_nuke_source) return TRUE to_chat(user, span_notice("You begin repairing [src]'s inner metal plate...")) - if(weapon.use_tool(src, user, 10 SECONDS, amount = 20)) - to_chat(user, span_notice("You repair [src]'s inner metal plate. The radiation is contained.")) - deconstruction_state = NUKESTATE_PANEL_REMOVED - STOP_PROCESSING(SSobj, core) - update_appearance() + if(!weapon.use_tool(src, user, 10 SECONDS, amount = 20)) + return TRUE + to_chat(user, span_notice("You repair [src]'s inner metal plate. The radiation is contained.")) + deconstruction_state = NUKESTATE_PANEL_REMOVED + STOP_PROCESSING(SSobj, core) + update_appearance() + return TRUE + + if(NUKESTATE_CORE_REMOVED) + if(astype(weapon, /obj/item/nuke_core_container)?.core && !istype(weapon, /obj/item/nuke_core_container/supermatter)) + var/obj/item/nuke_core_container/core_box = weapon + to_chat(user, span_notice("You pry open [core_box] and begin placing [core_box.core] into [src]'s inner chamber...")) + if(!do_after(user, 15 SECONDS, src)) + return TRUE + core_box.core.forceMove(src) + core = core_box.core + to_chat(user, span_notice("You place [core_box.core] into [src]'s inner chamber.")) + deconstruction_state = NUKESTATE_CORE_EXPOSED + update_appearance() + core_box.icon_state = core_box::icon_state + core_box.core = null + return TRUE + if(istype(weapon, /obj/item/nuke_core) && !istype(weapon, /obj/item/nuke_core/supermatter_sliver)) + to_chat(user, span_notice("You begin placing [weapon] into [src]'s inner chamber...")) + if(!do_after(user, 6 SECONDS, src)) + return TRUE + weapon.forceMove(src) + core = weapon + to_chat(user, span_notice("You place [weapon] into [src]'s inner chamber.")) + deconstruction_state = NUKESTATE_CORE_EXPOSED + update_appearance() return TRUE return ..() @@ -173,22 +213,42 @@ GLOBAL_VAR(station_nuke_source) switch(deconstruction_state) if(NUKESTATE_UNSCREWED) to_chat(user, span_notice("You start removing [src]'s front panel...")) - if(tool.use_tool(src, user, 30, volume=100)) - to_chat(user, span_notice("You remove [src]'s front panel.")) - deconstruction_state = NUKESTATE_PANEL_REMOVED - update_appearance() + if(!tool.use_tool(src, user, 30, volume=100)) + return TRUE + to_chat(user, span_notice("You remove [src]'s front panel.")) + deconstruction_state = NUKESTATE_PANEL_REMOVED + update_appearance() return TRUE if(NUKESTATE_WELDED) to_chat(user, span_notice("You start prying off [src]'s inner plate...")) - if(tool.use_tool(src, user, 30, volume=100)) + if(!tool.use_tool(src, user, 30, volume=100)) + return TRUE + if(core) to_chat(user, span_notice("You pry off [src]'s inner plate. You can see the core's green glow!")) deconstruction_state = NUKESTATE_CORE_EXPOSED - update_appearance() START_PROCESSING(SSobj, core) + else + to_chat(user, span_notice("You pry off [src]'s inner plate. The inner chamber is empty, save for some beer stains.")) + deconstruction_state = NUKESTATE_CORE_REMOVED + update_appearance() + new /obj/item/stack/sheet/iron(loc, 15) + return TRUE + if(NUKESTATE_PANEL_REMOVED) + to_chat(user, span_notice("You start levering [src]'s inner panel back into place...")) + if(!tool.use_tool(src, user, 30, volume = 100)) + return TRUE + to_chat(user, span_notice("You lever [src]'s inner panel back into place.")) + deconstruction_state = NUKESTATE_UNSCREWED + update_appearance() return TRUE - return FALSE +/obj/machinery/nuclearbomb/attack_hand_secondary(mob/user, list/modifiers) + if(deconstruction_state != NUKESTATE_CORE_EXPOSED) + return ..() + to_chat(user, span_danger("You can't hold [core] with your bare hands!")) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + /obj/machinery/nuclearbomb/can_interact(mob/user) if(HAS_TRAIT(user, TRAIT_CAN_USE_NUKE)) return TRUE diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm index f2cc848a8b35..66798ae798c6 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/beer_nuke.dm @@ -32,13 +32,11 @@ weapon.interact_with_atom(keg, user) // redirect refillable containers to the keg, allowing them to be filled return TRUE // pretend we handled the attack, too. - if(istype(weapon, /obj/item/nuke_core_container)) - to_chat(user, span_notice("[src] has had its plutonium core removed as a part of being decommissioned.")) - return TRUE - return ..() /obj/machinery/nuclearbomb/beer/actually_explode() + if(core) + return ..() //Unblock roundend, we're not actually exploding. SSticker.roundend_check_paused = FALSE var/turf/bomb_location = get_turf(src) @@ -57,16 +55,12 @@ return ..() /obj/machinery/nuclearbomb/beer/proc/local_foam() - var/datum/reagents/tmp_holder = new/datum/reagents(1000) - tmp_holder.my_atom = src - tmp_holder.add_reagent(flood_reagent, 100) - - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(200, holder = src, location = get_turf(src), carry = tmp_holder) - foam.start() + do_foam(200, src, get_turf(src), flood_reagent, 100) disarm_nuke() /obj/machinery/nuclearbomb/beer/really_actually_explode(detonation_status) + if(core) + return ..() //if it's always hooked in it'll override admin choices RegisterSignal(overflow_control, COMSIG_CREATED_ROUND_EVENT, PROC_REF(on_created_round_event)) disarm_nuke() diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm index 7b09a2540ecb..e3f4d00d8ebe 100644 --- a/code/modules/antagonists/pirate/pirate.dm +++ b/code/modules/antagonists/pirate/pirate.dm @@ -8,6 +8,7 @@ suicide_cry = "FOR ME MATEYS!!" stinger_sound = 'sound/music/antag/pirate/pirate_start.ogg' hijack_speed = 2 // That is without doubt the worst pirate I have ever seen. + desensitized_modifier = DESENSITIZED_THRESHOLD var/datum/team/pirate/crew /datum/antagonist/pirate/greet() @@ -46,12 +47,10 @@ var/datum/language_holder/holder = owner_mob.get_language_holder() holder.grant_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE) holder.selected_language = /datum/language/piratespeak - ADD_TRAIT(owner_mob, TRAIT_DESENSITIZED, REF(src)) /datum/antagonist/pirate/remove_innate_effects(mob/living/mob_override) var/mob/living/owner_mob = mob_override || owner.current owner_mob.remove_language(/datum/language/piratespeak, source = LANGUAGE_PIRATE) - REMOVE_TRAIT(owner_mob, TRAIT_DESENSITIZED, REF(src)) /datum/team/pirate name = "\improper Pirate crew" diff --git a/code/modules/antagonists/space_ninja/equipment/energy_katana.dm b/code/modules/antagonists/space_ninja/equipment/energy_katana.dm index a03dfa66fd9c..f02df8b59264 100644 --- a/code/modules/antagonists/space_ninja/equipment/energy_katana.dm +++ b/code/modules/antagonists/space_ninja/equipment/energy_katana.dm @@ -24,7 +24,7 @@ throwforce = 30 block_chance = 50 armour_penetration = 50 - w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_BULKY hitsound = 'sound/items/weapons/bladeslice.ogg' pickup_sound = 'sound/items/unsheath.ogg' drop_sound = 'sound/items/sheath.ogg' @@ -36,14 +36,13 @@ max_integrity = 200 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF item_flags = NEEDS_PERMIT - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system var/datum/action/innate/dash/ninja/jaunt /obj/item/energy_katana/Initialize(mapload) . = ..() jaunt = new(src) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) + spark_system = new(src, 5, FALSE) spark_system.attach(src) /obj/item/energy_katana/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) diff --git a/code/modules/antagonists/space_ninja/outfit.dm b/code/modules/antagonists/space_ninja/outfit.dm index f1d42506fc82..4ffd89d674cf 100644 --- a/code/modules/antagonists/space_ninja/outfit.dm +++ b/code/modules/antagonists/space_ninja/outfit.dm @@ -5,10 +5,10 @@ mask = /obj/item/clothing/mask/gas/ninja ears = /obj/item/radio/headset shoes = /obj/item/clothing/shoes/jackboots + suit_store = /obj/item/storage/belt/sheath/ninja l_pocket = /obj/item/grenade/c4/ninja r_pocket = /obj/item/tank/internals/emergency_oxygen internals_slot = ITEM_SLOT_RPOCKET - belt = /obj/item/energy_katana back = /obj/item/mod/control/pre_equipped/ninja implants = list(/obj/item/implant/explosive) @@ -32,7 +32,7 @@ uniform = /obj/item/clothing/under/syndicate/ninja back = /obj/item/mod/control/pre_equipped/empty/ninja - belt = /obj/item/energy_katana + suit_store = /obj/item/storage/belt/sheath/ninja /datum/outfit/ninja/plasmaman name = "Space Ninja (Plasmaman)" diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm index 9cb98f928f91..5a1bed62c7fe 100644 --- a/code/modules/antagonists/space_ninja/space_ninja.dm +++ b/code/modules/antagonists/space_ninja/space_ninja.dm @@ -12,17 +12,10 @@ can_assign_self_objectives = TRUE ui_name = "AntagInfoNinja" default_custom_objective = "Destroy vital station infrastructure, without being seen." + desensitized_modifier = DESENSITIZED_THRESHOLD ///Whether or not this ninja will obtain objectives var/give_objectives = TRUE -/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override) - var/mob/ninja = mob_override || owner.current - ADD_TRAIT(ninja, TRAIT_DESENSITIZED, REF(src)) - -/datum/antagonist/ninja/remove_innate_effects(mob/living/mob_override) - var/mob/ninja = mob_override || owner.current - REMOVE_TRAIT(ninja, TRAIT_DESENSITIZED, REF(src)) - /** * Proc that equips the space ninja outfit on a given individual. By default this is the owner of the antagonist datum. * diff --git a/code/modules/antagonists/wizard/equipment/wizard_hammer.dm b/code/modules/antagonists/wizard/equipment/wizard_hammer.dm index 9c6f50392ef5..4a4b945632b9 100644 --- a/code/modules/antagonists/wizard/equipment/wizard_hammer.dm +++ b/code/modules/antagonists/wizard/equipment/wizard_hammer.dm @@ -105,9 +105,8 @@ /obj/item/mjollnir/proc/shock(mob/living/target) target.Stun(1.5 SECONDS) target.Knockdown(10 SECONDS) - var/datum/effect_system/lightning_spread/s = new /datum/effect_system/lightning_spread - s.set_up(5, 1, target.loc) - s.start() + var/datum/effect_system/basic/lightning_spread/lightning = new(target.loc, 5, TRUE) + lightning.start() target.visible_message(span_danger("[target.name] is shocked by [src]!"), \ span_userdanger("You feel a powerful shock course through your body sending you flying!"), \ span_hear("You hear a heavy electrical crack!")) diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm index c86c6a84bf3d..140b63cf92ab 100644 --- a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm +++ b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm @@ -247,9 +247,7 @@ /datum/grand_side_effect/smoke/trigger(potency, turf/ritual_location, mob/invoker) playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE) var/range = LERP(2, 4, potency/GRAND_RITUAL_FINALE_COUNT) - var/datum/effect_system/fluid_spread/smoke/colourful/smoke = new - smoke.set_up(range, holder = ritual_location, location = ritual_location) - smoke.start() + do_smoke(round(range), ritual_location, ritual_location, smoke_type = /datum/effect_system/fluid_spread/smoke/colourful) /// Spawns randomly coloured smoke /datum/effect_system/fluid_spread/smoke/colourful diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm index 1c62bc96cb24..45dfa4f179e8 100644 --- a/code/modules/art/paintings.dm +++ b/code/modules/art/paintings.dm @@ -1,4 +1,11 @@ -#define MAX_PAINTING_ZOOM_OUT 3 +GLOBAL_LIST_INIT(canvas_dimensions, init_canvas_dimensions()) + +/proc/init_canvas_dimensions() + . = list() + for(var/obj/item/canvas/canvas_type in typesof(/obj/item/canvas)) + var/width = canvas_type::width + var/height = canvas_type::height + .["[width]x[height]"] = list(width, height) /////////// // EASEL // @@ -16,15 +23,14 @@ var/obj/item/canvas/painting = null //Adding canvases -/obj/structure/easel/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(istype(I, /obj/item/canvas)) - var/obj/item/canvas/canvas = I +/obj/structure/easel/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/canvas)) + var/obj/item/canvas/canvas = tool user.transfer_item_to_turf(canvas, get_turf(src), silent = FALSE) painting = canvas canvas.layer = layer+0.1 user.visible_message(span_notice("[user] puts \the [canvas] on \the [src]."),span_notice("You place \the [canvas] on \the [src].")) - else - return ..() + return ITEM_INTERACT_SUCCESS //Stick to the easel like glue @@ -46,14 +52,13 @@ interaction_flags_atom = parent_type::interaction_flags_atom | INTERACT_ATOM_ALLOW_USER_LOCATION var/width = 11 var/height = 11 - var/list/grid /// empty canvas color var/canvas_color = "#ffffff" + /// The sprite editor workspace that carries the data for this canvas + var/datum/sprite_editor_workspace/workspace /// Is it clean canvas or was there something painted on it at some point, used to decide when to show wip splotch overlay var/used = FALSE var/finalized = FALSE //Blocks edits - /// Whether a grid should be shown in the UI if the canvas is editable and the viewer is holding a painting tool. - var/show_grid = TRUE var/icon_generated = FALSE var/icon/generated_icon ///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies. @@ -74,17 +79,20 @@ */ var/pixels_per_unit = 9 - ///A list that keeps track of the current zoom value for each current viewer. - var/list/zoom_by_observer - SET_BASE_PIXEL(11, 10) custom_price = PAYCHECK_CREW /obj/item/canvas/Initialize(mapload) . = ..() - reset_grid() - + workspace = new(width, + height, + color_mode = SPRITE_EDITOR_COLOR_MODE_RGB, + config_flags = NONE, + tool_flags = SPRITE_EDITOR_TOOL_PENCIL | SPRITE_EDITOR_TOOL_BUCKET, + initial_layer_color = "[canvas_color]ff" // To avoid needing to handle strings of mixed lengths, sprite editor workspaces always use the alpha channel + ) + RegisterSignal(workspace, COMSIG_SPRITE_EDITOR_VALIDATE_COLOR, PROC_REF(validate_color)) painting_metadata = new painting_metadata.title = "Untitled Artwork" painting_metadata.creation_round_id = GLOB.round_id @@ -100,12 +108,6 @@ painting_metadata = null return ..() -/obj/item/canvas/proc/reset_grid() - grid = new/list(width,height) - for(var/x in 1 to width) - for(var/y in 1 to height) - grid[x][y] = canvas_color - /obj/item/canvas/attack_self(mob/user) . = ..() ui_interact(user) @@ -116,56 +118,59 @@ return ..() /obj/item/canvas/ui_state(mob/user) - if(isobserver(user)) - return GLOB.observer_state if(finalized) return GLOB.hold_or_view_state return GLOB.default_state -/obj/item/canvas/ui_status(mob/user, datum/ui_state/state) - if(state == GLOB.default_state || !state) - return ..() - //Skip the can_interact() check from atom/ui_status() and let them zoom in/out! - var/src_object = ui_host(user) - return state.can_use_topic(src_object, user) - /obj/item/canvas/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Canvas", name) ui.open() -/obj/item/canvas/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers) - if(!user.combat_mode) - ui_interact(user) - else - return ..() +/obj/item/canvas/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + ui_interact(user) + return ITEM_INTERACT_SUCCESS /obj/item/canvas/ui_static_data(mob/user) . = ..() - .["px_per_unit"] = pixels_per_unit - .["max_zoom"] = MAX_PAINTING_ZOOM_OUT + .["year_offset"] = STATION_YEAR_OFFSET /obj/item/canvas/ui_data(mob/user) . = ..() - .["grid"] = grid - .["zoom"] = LAZYACCESS(zoom_by_observer, user.key) || (finalized ? 1 : MAX_PAINTING_ZOOM_OUT) - .["name"] = painting_metadata.title - .["author"] = painting_metadata.creator_name - .["patron"] = painting_metadata.patron_name - .["medium"] = painting_metadata.medium - .["date"] = painting_metadata.creation_date - .["finalized"] = finalized - .["editable"] = !finalized //Ideally you should be able to draw moustaches on existing paintings in the gallery but that's not implemented yet - .["show_plaque"] = istype(loc,/obj/structure/sign/painting) - .["show_grid"] = show_grid - .["paint_tool_palette"] = null - var/obj/item/painting_implement = user.get_active_held_item() - if(!painting_implement) - .["paint_tool_color"] = null - return - .["paint_tool_color"] = get_paint_tool_color(painting_implement) - SEND_SIGNAL(painting_implement, COMSIG_PAINTING_TOOL_GET_ADDITIONAL_DATA, .) + var/list/metadata = list( + "title" = painting_metadata.title, + "author" = painting_metadata.creator_name, + "patron" = painting_metadata.patron_name, + "medium" = painting_metadata.medium, + "date" = painting_metadata.creation_date, + ) + var/list/editor_data = workspace.sprite_editor_ui_data() + var/can_edit = TRUE + + var/obj/item/implement = user.get_active_held_item() + var/implement_color = get_paint_tool_color(implement) + var/can_change_implement_color = can_change_paint_tool_color(implement) + if(implement_color) + editor_data["serverSelectedColor"] = implement_color + editor_data["serverPalette"] = get_paint_tool_palette(implement) + editor_data["maxServerColors"] = get_paint_tool_palette_capacity(implement) + editor_data["onSelectServerColor"] = "onSelectColor" + editor_data["onAddServerColor"] = "onAddPaletteColor" + editor_data["onRemoveServerColor"] = "onRemovePaletteColor" + if(can_change_implement_color) + editor_data["toolFlags"] |= SPRITE_EDITOR_TOOL_DROPPER + else + can_edit = FALSE + return list( + "metadata" = metadata, + "editorData" = editor_data, + "pixelsPerUnit" = pixels_per_unit, + "finalized" = finalized, + "allowColorPicker" = can_change_implement_color, + "editable" = can_edit && !finalized, //Ideally you should be able to draw moustaches on existing paintings in the gallery but that's not implemented yet + "showPlaque" = istype(loc, /obj/structure/sign/painting) + ) /obj/item/canvas/examine(mob/user) . = ..() @@ -176,87 +181,56 @@ if(.) return var/mob/user = usr - //this is here to allow observers and viewers to zoom in and out regardless of adjacency. - //observers need this special check because we allow them to operate the UI in ui_state - if((action != "zoom_in" && action != "zoom_out") && (isobserver(user) || !can_interact(user))) - return + var/obj/item/implement = user.get_active_held_item() + var/datum/component/palette/palette_comp = implement?.GetComponent(/datum/component/palette) switch(action) - if("paint", "fill") + if("spriteEditorCommand") + . = TRUE if(finalized) - return TRUE - var/obj/item/I = user.get_active_held_item() - var/tool_color = get_paint_tool_color(I) - if(!tool_color) - return FALSE - if(action == "fill") - var/x = params["x"] - var/y = params["y"] - if(!canvas_fill(x, y, tool_color)) - return FALSE - else - var/list/data = params["data"] - for(var/point in data) - var/x = text2num(point["x"]) - var/y = text2num(point["y"]) - grid[x][y] = tool_color - var/medium = get_paint_tool_medium(I) + return + var/command = params["command"] + if(command != "transaction") // Painting only allows transactions, no undo/redo or layer visibility toggling + return + if(!workspace.new_transaction(params["transaction"])) + return + var/medium = get_paint_tool_medium(implement) if(medium && painting_metadata.medium && painting_metadata.medium != medium) painting_metadata.medium = "Mixed medium" else painting_metadata.medium = medium used = TRUE update_appearance() + if("onSelectColor") . = TRUE - if("select_color") - var/obj/item/painting_implement = user.get_active_held_item() - painting_implement?.set_painting_tool_color(params["selected_color"]) + var/paint_color = copytext(params["color"], 1, 8) + implement?.set_painting_tool_color(paint_color) + if("onAddPaletteColor") . = TRUE - if("select_color_from_coords") - var/obj/item/painting_implement = user.get_active_held_item() - if(!painting_implement) - return FALSE - var/x = text2num(params["x"]) - var/y = text2num(params["y"]) - painting_implement.set_painting_tool_color(grid[x][y]) + if(!palette_comp) + return + if(length(palette_comp.colors) >= palette_comp.max_colors) + return + var/paint_color = copytext(params["color"], 1, 8) + palette_comp.colors += paint_color + if("onRemovePaletteColor") . = TRUE - if("change_palette") - var/obj/item/painting_implement = user.get_active_held_item() - if(!painting_implement) - return FALSE - //I'd have this done inside the signal, but that'd have to be asynced, - //while we want the UI to be updated after the color is chosen, not before. - var/chosen_color = tgui_color_picker(user, "Pick new color", painting_implement, params["old_color"]) - if(!chosen_color || IS_DEAD_OR_INCAP(user) || !user.is_holding(painting_implement)) - return FALSE - SEND_SIGNAL(painting_implement, COMSIG_PAINTING_TOOL_PALETTE_COLOR_CHANGED, chosen_color, params["color_index"]) - . = TRUE - if("toggle_grid") - . = TRUE - show_grid = !show_grid + if(!palette_comp) + return + var/color_index = params["index"] + palette_comp.colors.Cut(color_index, color_index+1) if("finalize") . = TRUE finalize(user) if("patronage") . = TRUE patron(user) - if("zoom_in") - . = TRUE - LAZYINITLIST(zoom_by_observer) - if(!zoom_by_observer[user.key]) - zoom_by_observer[user.key] = 2 - else - zoom_by_observer[user.key] = min(zoom_by_observer[user.key] + 1, MAX_PAINTING_ZOOM_OUT) - if("zoom_out") - . = TRUE - LAZYINITLIST(zoom_by_observer) - if(!zoom_by_observer[user.key]) - zoom_by_observer[user.key] = MAX_PAINTING_ZOOM_OUT - 1 - else - zoom_by_observer[user.key] = max(zoom_by_observer[user.key] - 1, 1) -/obj/item/canvas/ui_close(mob/user) - . = ..() - LAZYREMOVE(zoom_by_observer, user.key) +/obj/item/canvas/proc/validate_color(_source, paint_color) + SIGNAL_HANDLER + paint_color = copytext(paint_color, 1, 8) + var/obj/item/implement = usr.get_active_held_item() + if(!implement || !((get_paint_tool_color(implement) == paint_color) || (paint_color in get_paint_tool_palette(implement)))) + return COLOR_IS_INVALID /obj/item/canvas/proc/finalize(mob/user) if(finalized || painting_metadata.loaded_from_json) @@ -401,9 +375,10 @@ /obj/item/canvas/proc/get_data_string() var/list/data = list() + var/list/grid = workspace.layers[1]["data"]["[SOUTH]"] for(var/y in 1 to height) for(var/x in 1 to width) - data += grid[x][y] + data += grid[y][x] return data.Join("") //Todo make this element ? @@ -419,9 +394,38 @@ else if(istype(painting_implement, /obj/item/pen)) var/obj/item/pen/pen = painting_implement return pen.colour + else if (istype(painting_implement, /obj/item/airlock_painter/decal)) + var/obj/item/airlock_painter/decal/painter = painting_implement + return painter.selected_custom_color else if(istype(painting_implement, /obj/item/soap) || istype(painting_implement, /obj/item/rag)) return canvas_color +/obj/item/canvas/proc/get_paint_tool_palette(obj/item/painting_implement) + if(!painting_implement) + return list() + var/datum/component/palette/palette_comp = painting_implement.GetComponent(/datum/component/palette) + if(!palette_comp) + var/implement_color = get_paint_tool_color(painting_implement) + return implement_color ? list(implement_color) : list() + return palette_comp.colors + +/obj/item/canvas/proc/get_paint_tool_palette_capacity(obj/item/painting_implement) + if(!painting_implement) + return + var/datum/component/palette/palette_comp = painting_implement.GetComponent(/datum/component/palette) + if(!palette_comp) + return get_paint_tool_color(painting_implement) ? 1 : 0 + return palette_comp.max_colors + +/obj/item/canvas/proc/can_change_paint_tool_color(obj/item/painting_implement) + if(!painting_implement) + return + if(istype(painting_metadata, /obj/item/paint_palette) || istype(painting_implement, /obj/item/airlock_painter/decal)) + return TRUE + if(istype(painting_implement, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/crayon = painting_implement + return crayon.can_change_colour + /// Generates medium description /obj/item/canvas/proc/get_paint_tool_medium(obj/item/painting_implement) if(!painting_implement) @@ -432,7 +436,7 @@ return "Spraycan on canvas" else if(istype(painting_implement, /obj/item/toy/crayon)) return "Crayon on canvas" - else if(istype(painting_implement, /obj/item/pen)) + else if(istype(painting_implement, /obj/item/pen) || istype(painting_implement, /obj/item/airlock_painter/decal)) return "Ink on canvas" else if(istype(painting_implement, /obj/item/soap) || istype(painting_implement, /obj/item/rag)) return //These are just for cleaning, ignore them @@ -457,147 +461,6 @@ return FALSE -///The pixel to the right matches the previous color we're flooding over -#define CANVAS_FILL_R_MATCH (1<<0) -///The pixel to the left matches the previous color we're flooding over -#define CANVAS_FILL_L_MATCH (1<<1) - -//a macro for the stringized key for coordinates to check later -#define CANVAS_COORD(x, y) "[x]-[y]" -///queues a coordinate on the canvas for future cycles. -#define QUEUE_CANVAS_COORD(x, y, queue) \ - if(y && !queue[CANVAS_COORD(x, y)]) {\ - queue[CANVAS_COORD(x, y)] = list(x, y);\ - } - -/** - * A proc that adopts a span-based, 4-dir (correct me if I'm wrong) flood fill algorithm used - * by the bucked tool in the UI, to facilitate coloring larger portions of the canvas. - * If you have never used the bucket/flood tool on an image editor, I suggest you do it - * now so you know what I'm basically talking about. - * - * @ param x The point on the x axys where we start flooding our canvas. The arg is later used to store the current x - * @ param y The point on the y axys where we start flooding the canvas. The arg is later used to store the current y - * @ param new_color The new color that floods over the old one - */ -/obj/item/canvas/proc/canvas_fill(x, y, new_color) - var/prev_color = grid[x][y] - //If the colors are the same, don't do anything. - if(prev_color == new_color) - return FALSE - - //The queue for coordinates to the right of the current line - var/list/queue_right = list() - //Inversely for those to our left - var/list/queue_left = list() - //Whether we're currently checking the right or left queue. - var/go_right = TRUE - - //The current coordinates. The only reason this is outside the loop - //is because we first go up, then reset our vertical position to just below - //the starting position and go down from there. - var/list/coords = list(x, y) - - //Basically, the way it works is that each cycle we first go up, then down until we - //either reach the vertical borders of the raster or find a pixel that is not of the color we want - //to flood. As we do this, we try to queue a minimum of coordinates to our - //left and right to use for future cycles, moving horizontally in one direction until there are no - //more queued coordinates for that dir. Then we turn around and repeat - //until both left and right queues are completely empty. - while(coords) - //The current vertical line, the right and the left ones. - var/list/curr_line = grid[x] - var/list/right_line = x < width ? grid[x+1] : null - var/list/left_line = x > 1 ? grid[x-1] : null - //the queue we're on, depending on direction - var/list/curr_queue = go_right ? queue_right : queue_left - //Instead of queueing every point to our left and right that shares our prevous color, - //Causing a lot of empty cycles, we only queue an extremity of a vertical segment - //delimited by pixels of other colors or the y boundaries of the raster. To do this, - //we need to track where the segment (called line for simplicity) starts (or ends). - var/r_line_start - var/l_line_start - - //go up first (y = 1 is the upper border is) - while(y >= 1 && curr_line[y] == prev_color) - var/return_flags = canvas_scan_step(x, y, queue_left, queue_right, left_line, right_line, l_line_start, r_line_start, prev_color) - if(return_flags & CANVAS_FILL_R_MATCH) - r_line_start = y - else - r_line_start = null - if(return_flags & CANVAS_FILL_L_MATCH) - l_line_start = y - else - l_line_start = null - curr_line[y] = new_color - curr_queue -= CANVAS_COORD(x, y) //remove it from the queue if possible. - y-- - - //Any unqueued coordinate is queued and cleared before the next half of the cycle - QUEUE_CANVAS_COORD(x + 1, r_line_start, queue_right) - QUEUE_CANVAS_COORD(x - 1, l_line_start, queue_left) - r_line_start = l_line_start = null - - //set y to the pixel immediately below the starting y - y = coords[2] + 1 - - //then go down (y = height is the bottom border) - while(y <= height && curr_line[y] == prev_color) - var/return_flags = canvas_scan_step(x, y, queue_left, queue_right, left_line, right_line, l_line_start, r_line_start, prev_color) - if(!(return_flags & CANVAS_FILL_R_MATCH)) - r_line_start = null - else if(!r_line_start) - r_line_start = y - if(!(return_flags & CANVAS_FILL_L_MATCH)) - l_line_start = null - else if(!l_line_start) - l_line_start = y - curr_line[y] = new_color - curr_queue -= CANVAS_COORD(x, y) - y++ - - QUEUE_CANVAS_COORD(x + 1, r_line_start, queue_right) - QUEUE_CANVAS_COORD(x - 1, l_line_start, queue_left) - - //Pick the next set of coords from the queue (and change direction if necessary) - if(!length(curr_queue)) - var/list/other_queue = go_right ? queue_left : queue_right - coords = other_queue[other_queue[1]] - other_queue.Cut(1, 2) - go_right = !go_right - else - coords = curr_queue[curr_queue[1]] - curr_queue.Cut(1, 2) - - x = coords?[1] - y = coords?[2] - - return TRUE - -/** - * The step of canvas_fill() that scans the pixels to the immediate right and left of our coord and see if they need to be queue'd or not. - * Kept as a separate proc to reduce copypasted code. - */ -/proc/canvas_scan_step(x, y, list/queue_left, list/queue_right, list/left_line, list/right_line, left_pos, right_pos, prev_color) - if(left_line) - if(left_line[y] == prev_color) - . += CANVAS_FILL_L_MATCH - else - QUEUE_CANVAS_COORD(x - 1, left_pos, queue_left) - - if(!right_line) - return - - if(right_line[y] == prev_color) - . += CANVAS_FILL_R_MATCH - else - QUEUE_CANVAS_COORD(x + 1, right_pos, queue_right) - -#undef CANVAS_FILL_R_MATCH -#undef CANVAS_FILL_L_MATCH -#undef CANVAS_COORD -#undef QUEUE_CANVAS_COORD - /obj/item/canvas/nineteen_nineteen name = "canvas (19x19)" icon_state = "19x19" @@ -725,14 +588,14 @@ . = ..() SSpersistent_paintings.painting_frames -= src -/obj/structure/sign/painting/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers) - if(!current_canvas && istype(I, /obj/item/canvas)) - frame_canvas(user,I) - else if(current_canvas && current_canvas.painting_metadata.title == initial(current_canvas.painting_metadata.title) && istype(I,/obj/item/pen)) +/obj/structure/sign/painting/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(!current_canvas && istype(tool, /obj/item/canvas)) + frame_canvas(user, tool) + return ITEM_INTERACT_SUCCESS + if(current_canvas && current_canvas.painting_metadata.title == initial(current_canvas.painting_metadata.title) && istype(tool, /obj/item/pen)) if(try_rename(user)) SStgui.update_uis(src) - else - return ..() + return ITEM_INTERACT_SUCCESS /obj/structure/sign/painting/atom_deconstruct(disassembled) var/turf/drop_turf = drop_location() @@ -849,7 +712,7 @@ if(!istype(new_canvas)) CRASH("Found painting size with no matching canvas type") new_canvas.painting_metadata = painting - new_canvas.fill_grid_from_icon(I) + fill_grid_from_icon(new_canvas.workspace.get_first_layer_pixel_data(), I) new_canvas.generated_icon = I new_canvas.icon_generated = TRUE new_canvas.finalized = TRUE @@ -907,10 +770,11 @@ SSpersistent_paintings.paintings += current_canvas.painting_metadata /obj/item/canvas/proc/fill_grid_from_icon(icon/I) + var/list/grid = workspace.layers[1]["data"]["[SOUTH]"] var/h = I.Height() + 1 for(var/x in 1 to width) for(var/y in 1 to height) - grid[x][y] = I.GetPixel(x,h-y) + grid[y][x] = I.GetPixel(x,h-y) /obj/item/wallframe/painting/large name = "large painting frame" @@ -1073,4 +937,3 @@ current_color = chosen_color #undef AVAILABLE_PALETTE_SPACE -#undef MAX_PAINTING_ZOOM_OUT diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 74f2f748f1b8..e9c57626bc67 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -125,7 +125,7 @@ /obj/item/assembly_holder/dropped(mob/user) . = ..() for(var/obj/item/assembly/assembly as anything in assemblies) - assembly.dropped() + assembly.dropped(user) /obj/item/assembly_holder/attack_hand(mob/living/user, list/modifiers)//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess . = ..() diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 11e037bf7ad0..adddd5dd1b3c 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -8,7 +8,7 @@ desc = "A small electronic device able to ignite combustible substances." icon_state = "igniter" custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*5, /datum/material/glass=SMALL_MATERIAL_AMOUNT*0.5) - var/datum/effect_system/spark_spread/sparks + var/datum/effect_system/basic/spark_spread/sparks heat = 1000 drop_sound = 'sound/items/handling/component_drop.ogg' pickup_sound = 'sound/items/handling/component_pickup.ogg' @@ -21,15 +21,12 @@ /obj/item/assembly/igniter/Initialize(mapload) . = ..() - sparks = new - sparks.set_up(2, 0, src) + sparks = new(src, 2, FALSE) sparks.attach(src) /obj/item/assembly/igniter/Destroy() - if(sparks) - qdel(sparks) - sparks = null - . = ..() + QDEL_NULL(sparks) + return ..() /obj/item/assembly/igniter/activate() if(!..()) @@ -38,14 +35,16 @@ if(location) location.hotspot_expose(heat, EXPOSED_VOLUME) if(holder) - SEND_SIGNAL(holder.loc, COMSIG_IGNITER_ACTIVATE) + SEND_SIGNAL(holder.loc, COMSIG_IGNITER_ACTIVATE, src) if(QDELETED(src)) return TRUE sparks.start() return TRUE /obj/item/assembly/igniter/attack_self(mob/user) - activate() + // Don't trigger when interacting with assemblies + if (!holder) + activate() add_fingerprint(user) /obj/item/assembly/igniter/ignition_effect(atom/A, mob/user) @@ -59,7 +58,7 @@ desc = "A small electronic device able to chill their surroundings." icon_state = "freezer" custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2.5, /datum/material/glass=SMALL_MATERIAL_AMOUNT * 3) - heat = 200 + heat = MIN_FREEZE_TEMP /obj/item/assembly/igniter/condenser/activate() . = ..() @@ -68,9 +67,8 @@ var/turf/location = get_turf(loc) if(location) var/datum/gas_mixture/enviro = location.return_air() - enviro.temperature = clamp(min(ROOM_TEMP, enviro.temperature*0.85),MIN_FREEZE_TEMP,MAX_FREEZE_TEMP) + enviro.temperature = clamp(min(ROOM_TEMP, enviro.temperature * 0.85), MIN_FREEZE_TEMP, MAX_FREEZE_TEMP) location.air_update_turf(FALSE, FALSE) - sparks.start() #undef EXPOSED_VOLUME #undef ROOM_TEMP diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm index 64cd36ca239e..d215c1c68030 100644 --- a/code/modules/atmospherics/gasmixtures/reactions.dm +++ b/code/modules/atmospherics/gasmixtures/reactions.dm @@ -887,9 +887,7 @@ var/obj/effect/particle_effect/fluid/foam/foam = locate() in location var/obj/structure/foamedmetal/resin = locate() in location if(heat_efficiency > HALON_COMBUSTION_MINIMUM_RESIN_MOLES && isopenturf(location) && !foam && !resin) // Don't resin if there is aleady resin or we are not in an open turf. - var/datum/effect_system/fluid_spread/foam/metal/resin/halon/foaming = new - foaming.set_up(amount = HALON_COMBUSTION_RESIN_VOLUME, holder = holder, location = location) - foaming.start() + do_foam(amount = HALON_COMBUSTION_RESIN_VOLUME, holder = holder, location = location, foam_type = /datum/effect_system/fluid_spread/foam/metal/resin/halon) . |= VOLATILE_REACTION . |= REACTING diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm index 943a10766bb6..9e3b30f08253 100644 --- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm +++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm @@ -167,13 +167,10 @@ return FALSE if(!prob(prb)) return FALSE //you lucked out, no shock for you - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, src) - s.start() //sparks always. + do_sparks(5, TRUE, src) if (electrocute_mob(user, get_area(src), src, 1, TRUE)) return TRUE - else - return FALSE + return FALSE /obj/item/electronics/airalarm name = "air alarm electronics" diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm index 0e6e899778cd..97d61b51103b 100644 --- a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm +++ b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm @@ -457,8 +457,10 @@ // If we have a preposterous amount of mass in the fusion mix, things get bad extremely fast if(internal_fusion.total_moles() >= HYPERTORUS_HYPERCRITICAL_MOLES) - var/hypercritical_damage_taken = max((internal_fusion.total_moles() - HYPERTORUS_HYPERCRITICAL_MOLES) * HYPERTORUS_HYPERCRITICAL_SCALE, 0) - critical_threshold_proximity = max(critical_threshold_proximity + min(hypercritical_damage_taken, HYPERTORUS_HYPERCRITICAL_MAX_DAMAGE), 0) * seconds_per_tick + var/moles_over_limit = internal_fusion.total_moles() - HYPERTORUS_HYPERCRITICAL_MOLES + var/hypercritical_damage_taken = max(moles_over_limit * HYPERTORUS_HYPERCRITICAL_SCALE, 0) + hypercritical_damage_taken = min(hypercritical_damage_taken, HYPERTORUS_HYPERCRITICAL_MAX_DAMAGE) * seconds_per_tick + critical_threshold_proximity = max(critical_threshold_proximity + hypercritical_damage_taken, 0) warning_damage_flags |= HYPERTORUS_FLAG_HIGH_FUEL_MIX_MOLE // High power fusion might create other matter other than helium, iron is dangerous inside the machine, damage can be seen diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm index 69bf48fe2aec..440be922316e 100644 --- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm +++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm @@ -48,12 +48,5 @@ /obj/item/nitrium_crystal/attack_self(mob/user) . = ..() - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - var/turf/location = get_turf(src) - create_reagents(5) - reagents.add_reagent(/datum/reagent/nitrium_low_metabolization, 3) - reagents.add_reagent(/datum/reagent/nitrium_high_metabolization, 2) - smoke.attach(location) - smoke.set_up(cloud_size, holder = src, location = location, carry = reagents, silent = TRUE) - smoke.start() + do_chem_smoke(cloud_size, src, get_turf(src), list(/datum/reagent/nitrium_low_metabolization = 3, /datum/reagent/nitrium_high_metabolization = 2)) qdel(src) diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm index 6d338e5ec269..2fa38219a1f0 100644 --- a/code/modules/awaymissions/super_secret_room.dm +++ b/code/modules/awaymissions/super_secret_room.dm @@ -29,9 +29,7 @@ SpeakPeace(list("Welcome to the error handling room.","Something's goofed up bad to send you here.","You should probably tell an admin what you were doing, or make a bug report.")) for(var/obj/structure/signpost/salvation/sign in orange(7)) sign.SetInvisibility(INVISIBILITY_NONE) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(1, holder = src, location = sign.loc) - smoke.start() + do_smoke(1, src, sign.loc) break if(1) SpeakPeace(list("Take that ladder up.","It'll send you back to the station.","Hopefully you'll never need to see this place again.")) diff --git a/code/modules/bitrunning/components/bitrunning_points.dm b/code/modules/bitrunning/components/bitrunning_points.dm index b800d586e941..039f3a9d3384 100644 --- a/code/modules/bitrunning/components/bitrunning_points.dm +++ b/code/modules/bitrunning/components/bitrunning_points.dm @@ -32,9 +32,5 @@ var/turf/tile = parent var/obj/structure/closet/crate/secure/bitrunning/encrypted/crate = new() crate.forceMove(tile) // Triggers any on-move effects on that turf - - var/datum/effect_system/spark_spread/quantum/sparks = new(tile) - sparks.set_up(number = 5, location = tile) - sparks.start() - + do_sparks(5, FALSE, tile, spark_type = /datum/effect_system/basic/spark_spread/quantum) qdel(src) diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm index a4ceb6fb534e..5fccb167a5cf 100644 --- a/code/modules/bitrunning/objects/byteforge.dm +++ b/code/modules/bitrunning/objects/byteforge.dm @@ -60,10 +60,7 @@ /obj/machinery/byteforge/proc/flash(atom/movable/thing) playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE) - var/datum/effect_system/spark_spread/quantum/sparks = new() - sparks.set_up(5, 1, loc) - sparks.start() - + do_sparks(5, TRUE, loc, spark_type = /datum/effect_system/basic/spark_spread/quantum) set_light(l_on = FALSE) /// Forge begins to process diff --git a/code/modules/bitrunning/objects/quantum_console.dm b/code/modules/bitrunning/objects/quantum_console.dm index d79ae60c3a70..37c4c25b66d8 100644 --- a/code/modules/bitrunning/objects/quantum_console.dm +++ b/code/modules/bitrunning/objects/quantum_console.dm @@ -76,13 +76,13 @@ switch(action) if("random_domain") - server.cold_boot_map(server.get_random_domain_id()) + server.cold_boot_map(server.get_random_domain_id(), was_random_selection = TRUE) return TRUE if("refresh") ui.send_full_update() return TRUE if("set_domain") - server.cold_boot_map(params["id"]) + server.cold_boot_map(params["id"], was_random_selection = FALSE) return TRUE if("stop_domain") server.begin_shutdown(usr) diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm index 58dd8dd4edaa..96a10d60bbf5 100644 --- a/code/modules/bitrunning/server/map_handling.dm +++ b/code/modules/bitrunning/server/map_handling.dm @@ -28,7 +28,7 @@ /// Links all the loading processes together - does validation for booting a map -/obj/machinery/quantum_server/proc/cold_boot_map(map_key) +/obj/machinery/quantum_server/proc/cold_boot_map(map_key, was_random_selection) if(!is_ready) return FALSE @@ -53,8 +53,20 @@ scrub_vdom() is_ready = TRUE return FALSE - - SSblackbox.record_feedback("tally", "bitrunning_domain_loaded", 1, map_key) + + + //We will want to record how the domain was selected. Either entirely randomly, with the name redacted, or with full information. + //Without this, it is difficult to determine what domains are selected more often intentionallly, vs unintentionally. + var/selection_type + + if(was_random_selection) + selection_type = "random selection" + else + if(generated_domain.can_view_name(scanner_tier, points)) + selection_type = "full information" + else + selection_type = "redacted information" + SSblackbox.record_feedback("nested tally", "bitrunning_domain_loaded", 1, list(selection_type, map_key)) is_ready = TRUE diff --git a/code/modules/bitrunning/server/signal_handlers.dm b/code/modules/bitrunning/server/signal_handlers.dm index 5b7156752e7a..f14db4509e3e 100644 --- a/code/modules/bitrunning/server/signal_handlers.dm +++ b/code/modules/bitrunning/server/signal_handlers.dm @@ -76,6 +76,11 @@ boss.make_virtual_megafauna() continue + if(istype(creature, /mob/living/basic/boss)) + var/mob/living/basic/boss/boss = creature + boss.make_virtual_megafauna() + continue + mutation_candidate_refs.Add(WEAKREF(creature)) continue diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm index ce79ee7aa5bd..ae15698f09da 100644 --- a/code/modules/bitrunning/server/util.dm +++ b/code/modules/bitrunning/server/util.dm @@ -135,9 +135,7 @@ /// Do some magic teleport sparks /obj/machinery/quantum_server/proc/spark_at_location(obj/cache) playsound(cache, 'sound/effects/magic/blink.ogg', 50, vary = TRUE) - var/datum/effect_system/spark_spread/quantum/sparks = new() - sparks.set_up(5, location = get_turf(cache)) - sparks.start() + do_sparks(5, FALSE, get_turf(cache), spark_type = /datum/effect_system/basic/spark_spread/quantum) /// Starts building a new avatar for the player. diff --git a/code/modules/bitrunning/util/virtual_megafauna.dm b/code/modules/bitrunning/util/virtual_megafauna.dm index 4daf00141978..811a7ca4acb0 100644 --- a/code/modules/bitrunning/util/virtual_megafauna.dm +++ b/code/modules/bitrunning/util/virtual_megafauna.dm @@ -1,4 +1,4 @@ -/// Removes all the loot and achievements from megafauna for bitrunning related +/// Removes all the loot and achievements from megafauna for bitrunning related (/simple_animal/hostile/megafauna version) /mob/living/simple_animal/hostile/megafauna/proc/make_virtual_megafauna() var/new_max = clamp(maxHealth * 0.5, 600, 1300) maxHealth = new_max @@ -26,3 +26,26 @@ loot.Cut() loot += /obj/structure/closet/crate/secure/bitrunning/encrypted + +/// Removes all the loot and achievements from megafauna for bitrunning related (/basic/boss version) +/mob/living/basic/boss/proc/make_virtual_megafauna() + var/new_max = clamp(maxHealth * 0.5, 600, 1300) + maxHealth = new_max + health = new_max + + // rebuild the achievement element's arguments to remove it appropriately + if (achievements) + var/list/achievements_list = list(/datum/award/achievement/boss/boss_killer, /datum/award/score/boss_score) + achievements_list += achievements + RemoveElement(/datum/element/kill_achievement, string_list(achievements_list), crusher_achievement_type, /datum/memory/megafauna_slayer) + + // remove the crusher loot element's arguments also to remove it appropriately + RemoveElement(\ + /datum/element/crusher_loot,\ + trophy_type = string_list(crusher_loot),\ + guaranteed_drop = 0.6,\ + drop_immediately = DEL_ON_DEATH,\ + ) + + RemoveElement(/datum/element/death_drops, string_list(regular_loot)) + AddElement(/datum/element/death_drops, /obj/structure/closet/crate/secure/bitrunning/encrypted) diff --git a/code/modules/bitrunning/virtual_domain/virtual_domain.dm b/code/modules/bitrunning/virtual_domain/virtual_domain.dm index 9ebd82bd43d8..8fc3173df951 100644 --- a/code/modules/bitrunning/virtual_domain/virtual_domain.dm +++ b/code/modules/bitrunning/virtual_domain/virtual_domain.dm @@ -85,6 +85,13 @@ /// The role that ghosts will get. Only used for poll text. var/spawner_role = "Antagonist" + +/datum/lazy_template/virtual_domain/proc/can_view_name(scanner_tier, server_points) + return difficulty < scanner_tier && cost <= server_points + 5 + +/datum/lazy_template/virtual_domain/proc/can_view_reward(scanner_tier, server_points) + return difficulty < (scanner_tier + 1) && cost <= server_points + 3 + /datum/lazy_template/virtual_domain/Destroy(force) QDEL_NULL(ghost_spawners) QDEL_NULL(ghost_mobs) diff --git a/code/modules/cargo/bounties/reagent.dm b/code/modules/cargo/bounties/reagent.dm index 4f34ee452a8d..2f707c11fe07 100644 --- a/code/modules/cargo/bounties/reagent.dm +++ b/code/modules/cargo/bounties/reagent.dm @@ -4,7 +4,7 @@ var/datum/reagent/wanted_reagent /datum/bounty/reagent/can_claim() - return ..() && shipped_volume >= required_volume + return shipped_volume >= required_volume /datum/bounty/reagent/print_required() return "[shipped_volume]/[required_volume] u" @@ -211,7 +211,7 @@ var/wanted_vol = 30 /datum/bounty/pill/can_claim() - return ..() && shipped_ammount >= required_ammount + return shipped_ammount >= required_ammount /datum/bounty/pill/applies_to(obj/shipped) if(!istype(shipped, /obj/item/reagent_containers/applicator/pill)) diff --git a/code/modules/cargo/bounties/virus.dm b/code/modules/cargo/bounties/virus.dm index caab0e7d924b..a021166c0600 100644 --- a/code/modules/cargo/bounties/virus.dm +++ b/code/modules/cargo/bounties/virus.dm @@ -17,7 +17,7 @@ return "At least 1u" /datum/bounty/virus/can_claim() - return ..() && shipped + return shipped /datum/bounty/virus/applies_to(obj/export) if(shipped) diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm index 445cd3296030..6383be5e95c5 100644 --- a/code/modules/cargo/markets/market_telepad.dm +++ b/code/modules/cargo/markets/market_telepad.dm @@ -366,11 +366,7 @@ receiving.post_purchase_effects(receiving.item) use_energy(energy_usage_per_teleport / power_efficiency) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(5, 1, get_turf(src)) - sparks.attach(receiving.item) - sparks.start() - + do_sparks(5, TRUE, src, receiving.item) transmitting = receiving receiving = null diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm index 21478178a096..1423122d5a10 100644 --- a/code/modules/cargo/orderconsole.dm +++ b/code/modules/cargo/orderconsole.dm @@ -51,9 +51,9 @@ if(!bank) return ITEM_INTERACT_BLOCKING var/dolla = tool.get_item_credit_value() - to_chat(user, span_notice("You insert [dolla] dollars into [src].")) + to_chat(user, span_notice("You insert [dolla] [MONEY_NAME] into [src].")) bank.adjust_money(dolla, "Supply Console: Deposit") - to_chat(usr, span_notice("You have deposited [dolla] dollars into the account. The new balance is [bank.account_balance] dollars.")) + to_chat(usr, span_notice("You have deposited [dolla] [MONEY_NAME] into the account. The new balance is [bank.account_balance] [MONEY_NAME].")) qdel(tool) return ITEM_INTERACT_SUCCESS // DARKPACK EDIT ADD END diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 3d6da8268a0e..82d83f3ebf85 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -47,7 +47,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) //Job preferences 2.0 - indexed by job title , no key or value implies never var/list/job_preferences = list() - // DARKPACK EDIT ADD START - STORYTELLR_STATS + // DARKPACK EDIT ADD START - STORYTELLER_STATS var/list/preference_storyteller_stats = list() // DARKPACK EDIT ADD END // DARKPACK EDIT ADD START - ALTERNATIVE_JOB_TITLES diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm index de01b73dc92c..c3fa4733a0e1 100644 --- a/code/modules/client/preferences/_preference.dm +++ b/code/modules/client/preferences/_preference.dm @@ -42,11 +42,11 @@ /// Preferences relating to World of Darkness TTRPG elements #define PREFERENCE_PRIORITY_WORLD_OF_DARKNESS 11 -#define PREFERENCE_PRIORITY_REQUIRES_CLAN 12 +#define PREFERENCE_PRIORITY_REQUIRES_SUBSPLAT 12 // DARKPACK EDIT ADD END - TTRPG preferences /// The maximum preference priority, keep this updated, but don't use it for `priority`. -#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_REQUIRES_CLAN // DARKPACK EDIT CHANGE - TTRPG Preferences +#define MAX_PREFERENCE_PRIORITY PREFERENCE_PRIORITY_REQUIRES_SUBSPLAT // DARKPACK EDIT CHANGE - TTRPG Preferences /// For choiced preferences, this key will be used to set display names in constant data. #define CHOICED_PREFERENCE_DISPLAY_NAMES "display_names" diff --git a/code/modules/client/preferences/middleware/quirks.dm b/code/modules/client/preferences/middleware/quirks.dm index b8ad5c462902..061ab6c4b806 100644 --- a/code/modules/client/preferences/middleware/quirks.dm +++ b/code/modules/client/preferences/middleware/quirks.dm @@ -62,6 +62,9 @@ data["default_quirk_balance"] = CONFIG_GET(number/default_quirk_points) data["species_disallowed_quirks"] = get_species_compatibility() data["splat_disallowed_quirks"] = get_splat_compatibility() // DARKPACK EDIT ADD - SPLATS + data["quirk_balance"] = get_quirk_balance() // DARKPACK EDIT ADD - MERITS/FLAWS + data["freebie_points"] = get_freebie_points() // DARKPACK EDIT ADD - MERITS/FLAWS + data["clan_disallowed_quirks"] = get_clan_compatibility() // DARKPACK EDIT ADD - MERITS/FLAWS return data @@ -73,6 +76,8 @@ data["selected_quirks"] = get_selected_quirks() data["species_disallowed_quirks"] = get_species_compatibility() data["splat_disallowed_quirks"] = get_splat_compatibility() // DARKPACK EDIT ADD - SPLATS + data["freebie_points"] = get_freebie_points() // DARKPACK EDIT ADD - MERITS/FLAWS + data["clan_disallowed_quirks"] = get_clan_compatibility() // DARKPACK EDIT ADD - MERITS/FLAWS return data @@ -112,7 +117,11 @@ /datum/preference_middleware/quirks/proc/give_quirk(list/params, mob/user) var/quirk_name = params["quirk"] - + // DARKPACK EDIT ADD - MERITS/FLAWS + var/datum/st_stat/freebie/freebie_points = preferences.preference_storyteller_stats["[STAT_FREEBIE_POINTS]"] + var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name] + freebie_points.decrease_points(quirk_type.value) + // DARKPACK EDIT END - MERITS/FLAWS preferences.validate_quirks() var/list/new_quirks = preferences.all_quirks | quirk_name if (SSquirks.filter_invalid_quirks(new_quirks) != new_quirks) @@ -130,7 +139,11 @@ /datum/preference_middleware/quirks/proc/remove_quirk(list/params, mob/user) var/quirk_name = params["quirk"] - + // DARKPACK EDIT ADD - MERITS/FLAWS + var/datum/st_stat/freebie/freebie_points = preferences.preference_storyteller_stats["[STAT_FREEBIE_POINTS]"] + var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name] + freebie_points.increase_points(quirk_type.value) + // DARKPACK EDIT END - MERITS/FLAWS var/list/new_quirks = preferences.all_quirks - quirk_name if ( \ !(quirk_name in preferences.all_quirks) \ @@ -155,3 +168,47 @@ selected_quirks += sanitize_css_class_name(quirk) return selected_quirks + +//DARKPACK EDIT ADD - MERITS/FLAWS + +/datum/preference_middleware/quirks/proc/get_freebie_points() + var/datum/st_stat/freebie/freebie_stat = preferences.preference_storyteller_stats["[STAT_FREEBIE_POINTS]"] + if(!freebie_stat) + return null + + var/base_points = 15 + var/spent_on_stats = freebie_stat.freebie_cost_spent + var/quirk_balance = get_quirk_balance() + + return base_points - spent_on_stats + quirk_balance + +/datum/preference_middleware/quirks/proc/get_quirk_balance() + var/total_cost = 0 + for(var/quirk_name in preferences.all_quirks) + var/datum/quirk/quirk_type = SSquirks.quirks[quirk_name] + total_cost -= quirk_type.value + return total_cost + +/datum/preference_middleware/quirks/proc/get_clan_compatibility() + var/list/clan_blacklist = list() + var/clan_name = preferences.read_preference(/datum/preference/choiced/vampire_clan) + + if(!clan_name) + return clan_blacklist + + //clan_name is clan.name which is "Brujah" vampire clan list is "name" ("Brujah") = typepath, vampire_clans is typepath = datum. we need the datum for the id, which is... just a lowercase name... + var/datum/vampire_clan/clan = GLOB.vampire_clans[GLOB.vampire_clan_list[clan_name]] + for(var/quirk_path in SSquirks.quirk_prototypes) + var/datum/quirk/quirk_prototype = SSquirks.quirk_prototypes[quirk_path] + + // clan exclusion is only going to reasonably appear on darkpack quirks + if(!istype(quirk_prototype, /datum/quirk/darkpack)) + continue + + var/datum/quirk/darkpack/darkpack_quirk = quirk_prototype + if(!darkpack_quirk.is_clan_appropriate(clan)) + clan_blacklist += quirk_prototype.name + + return clan_blacklist + +//DARKPACK EDIT END - MERITS/FLAWS diff --git a/code/modules/client/preferences/persistent_scars.dm b/code/modules/client/preferences/persistent_scars.dm index db3402fb6b78..4db17afdce26 100644 --- a/code/modules/client/preferences/persistent_scars.dm +++ b/code/modules/client/preferences/persistent_scars.dm @@ -1,5 +1,5 @@ /datum/preference/toggle/persistent_scars - category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES // DARKPACK EDIT, ORIGINAL: category = PREFERENCE_CATEGORY_NON_CONTEXTUAL savefile_key = "persistent_scars" savefile_identifier = PREFERENCE_CHARACTER diff --git a/code/modules/client/preferences/sounds.dm b/code/modules/client/preferences/sounds.dm index 3fa9c16ca34d..e6434afdd328 100644 --- a/code/modules/client/preferences/sounds.dm +++ b/code/modules/client/preferences/sounds.dm @@ -20,6 +20,11 @@ savefile_key = "sound_breathing" savefile_identifier = PREFERENCE_PLAYER +/datum/preference/toggle/sound_breathing/apply_to_client_updated(client/client, value) + var/mob/living/carbon/carbon_mob = client.mob + if(istype(carbon_mob) && !value) + carbon_mob.breathing_loop.stop() + /// Controls hearing announcement sounds /datum/preference/toggle/sound_announcements category = PREFERENCE_CATEGORY_GAME_PREFERENCES diff --git a/code/modules/clothing/chameleon/_chameleon_action.dm b/code/modules/clothing/chameleon/_chameleon_action.dm index 88ca8e51fe86..fd05f481bad3 100644 --- a/code/modules/clothing/chameleon/_chameleon_action.dm +++ b/code/modules/clothing/chameleon/_chameleon_action.dm @@ -8,7 +8,7 @@ check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_INCAPACITATED|AB_CHECK_HANDS_BLOCKED /// Typecache of all item types we explicitly cannot pick /// Note that abstract items are already excluded - VAR_FINAL/list/chameleon_blacklist = list() + VAR_FINAL/list/chameleon_blacklist /// Typecache of typepaths we can turn into VAR_FINAL/list/chameleon_typecache /// Assoc list of item name + icon state to item typepath @@ -31,10 +31,18 @@ qdel(src) return + name = "Change [chameleon_name] Appearance" + build_all_button_icons() + + LAZYINITLIST(chameleon_blacklist) + LAZYINITLIST(chameleon_typecache) + LAZYINITLIST(chameleon_list) + initialize_blacklist() initialize_disguises() + if(active_type) - if(chameleon_blacklist[active_type]) + if(is_type_in_typecache(active_type, chameleon_blacklist)) stack_trace("[type] has an active type defined in init which is blacklisted ([active_type])") active_type = null else @@ -73,31 +81,40 @@ var/datum/action/chameleon_outfit/outfit_action = locate() in remove_from.actions QDEL_NULL(outfit_action) +/// Basic initialization of the chameleon items we cannot pick from /datum/action/item_action/chameleon/change/proc/initialize_blacklist() chameleon_blacklist |= typecacheof(target.type) +/// Basic initialization of the chameleon items we can pick from /datum/action/item_action/chameleon/change/proc/initialize_disguises() - name = "Change [chameleon_name] Appearance" - build_all_button_icons() - - LAZYINITLIST(chameleon_typecache) - LAZYINITLIST(chameleon_list) - if(!ispath(chameleon_type, /obj/item)) stack_trace("Non-item chameleon type defined on [type] ([chameleon_type])") return add_chameleon_items(chameleon_type) -/datum/action/item_action/chameleon/change/proc/add_chameleon_items(type_to_add) - - chameleon_typecache |= typecacheof(type_to_add) - for(var/obj/item/item_type as anything in chameleon_typecache) - if(chameleon_blacklist[item_type] || (item_type::item_flags & ABSTRACT) || item_type == item_type::abstract_type || !item_type::icon_state) +/// Used for formatting a typepath into something human readable for selection +/datum/action/item_action/chameleon/change/proc/format_readable_name(datum/format_type) + if(ispath(format_type, /obj/item)) + var/obj/item/format_item = format_type + return "[format_item::name] ([replacetext(format_item::post_init_icon_state || format_item::icon_state, "_", " ")])" + + return "[format_type]" + +/** + * Adds items of the given type or types to the chameleon selection list + * + * * type_or_types_to_add: A single typepath or a list of typepaths to add to the chameleon selection + * * only_root: If TRUE, only add the literal type or types passed, not their children + * * ignore_root: If TRUE, only add children of the type or types passed, not the literal types + */ +/datum/action/item_action/chameleon/change/proc/add_chameleon_items(type_or_types_to_add, only_root = FALSE, ignore_root = FALSE) + var/list/new_items = typecacheof(type_or_types_to_add, only_root_path = only_root, ignore_root_path = ignore_root) + for(var/obj/item/item_type as anything in new_items - chameleon_typecache) + if(is_type_in_typecache(item_type, chameleon_blacklist) || (item_type::item_flags & ABSTRACT) || item_type == item_type::abstract_type || !item_type::icon_state) continue - var/chameleon_item_name = "[item_type::name] ([item_type::post_init_icon_state || item_type::icon_state])" - chameleon_list[chameleon_item_name] = item_type - + chameleon_list[format_readable_name(item_type)] = item_type + chameleon_typecache |= new_items /datum/action/item_action/chameleon/change/proc/select_look(mob/user) var/picked_name = tgui_input_list(user, "Select [chameleon_name] to change into", "Chameleon Settings", sort_list(chameleon_list, GLOBAL_PROC_REF(cmp_typepaths_asc))) diff --git a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm index 956b0892e335..6f86cb487987 100644 --- a/code/modules/clothing/chameleon/chameleon_action_subtypes.dm +++ b/code/modules/clothing/chameleon/chameleon_action_subtypes.dm @@ -136,6 +136,45 @@ stack_trace("Adding chameleon ID action to non-chameleon id ([target])") qdel(src) +/datum/action/item_action/chameleon/change/id/format_readable_name(datum/format_type) + if(ispath(format_type, /obj/item/card/id/advanced)) + var/obj/item/card/id/advanced/format_card = format_type + var/list/basesplit_state = splittext(format_card::post_init_icon_state || format_card::icon_state, "_") + if(basesplit_state[1] == "card") // kind of evil, but so is making icon state user visible + basesplit_state.Cut(1, 2) + + var/list/assignedsplit_state = splittext(format_card::assigned_icon_state, "_") + if(assignedsplit_state[1] == "assigned") // kind of evil, but so is making icon state user visible + assignedsplit_state.Cut(1, 2) + + var/final_state_string = "[jointext(basesplit_state, " ")], [jointext(assignedsplit_state, " ") || "unassigned"]" + + return "[format_card::name] ([final_state_string])" + + return ..() + +/datum/action/item_action/chameleon/change/id/initialize_disguises() + // basic id types + add_chameleon_items(list( + /obj/item/card/id/advanced, + /obj/item/card/id/advanced/black, + /obj/item/card/id/advanced/bountyhunter, + /obj/item/card/id/advanced/centcom, + /obj/item/card/id/advanced/gold, + /obj/item/card/id/advanced/platinum, + /obj/item/card/id/advanced/prisoner, + /obj/item/card/id/advanced/rainbow, + /obj/item/card/id/advanced/robotic, + /obj/item/card/id/advanced/silver, + ), only_root = TRUE) + + // all black cards (they're all syndie cards, which are IC anyways) + add_chameleon_items(/obj/item/card/id/advanced/black) + // all centcom cards (they should all be IC anyways) + add_chameleon_items(/obj/item/card/id/advanced/centcom) + // explicitly include the captain's spare id, even though it could be manually constructed from the gold id, for ease + add_chameleon_items(/obj/item/card/id/advanced/gold/captains_spare, only_root = TRUE) + /datum/action/item_action/chameleon/change/id/update_item(obj/item/picked_item) . = ..() var/obj/item/card/id/advanced/chameleon/agent_card = target @@ -151,15 +190,14 @@ // If there has not, we set the assignment to the copied card's default as well as copying over the the // default registered name from the copied card. if(!agent_card.forged) - if(!agent_card.assignment) - agent_card.assignment = initial(copied_card.assignment) - + agent_card.assignment ||= initial(copied_card.assignment) agent_card.registered_name = initial(copied_card.registered_name) agent_card.icon_state = initial(copied_card.icon_state) if(ispath(copied_card, /obj/item/card/id/advanced)) var/obj/item/card/id/advanced/copied_advanced_card = copied_card agent_card.assigned_icon_state = initial(copied_advanced_card.assigned_icon_state) + agent_card.inherent_assigned_name = initial(copied_advanced_card.inherent_assigned_name) agent_card.update_label() agent_card.update_appearance(UPDATE_ICON) @@ -204,26 +242,40 @@ stack_trace("Adding chameleon ID trim action to non-chameleon id ([target])") qdel(src) +/datum/action/item_action/chameleon/change/id_trim/format_readable_name(datum/format_type) + if(istype(format_type, /datum/id_trim)) + var/datum/id_trim/trim = format_type + var/list/trimstate_split = splittext(trim.trim_state, "_") + if(trimstate_split[1] == "trim") // kind of evil, but so is making icon state user visible + trimstate_split.Cut(1, 2) + + return "[trim.assignment] ([jointext(trimstate_split, " ")])" + + return ..() + /datum/action/item_action/chameleon/change/id_trim/initialize_blacklist() - return + chameleon_blacklist |= typecacheof(/datum/id_trim/centcom/corpse) /datum/action/item_action/chameleon/change/id_trim/initialize_disguises() - // Little bit of copypasta but we only use trim datums rather than item paths - name = "Change [chameleon_name] Appearance" - build_all_button_icons() - - LAZYINITLIST(chameleon_typecache) - LAZYINITLIST(chameleon_list) + var/list/chameleon_whitelist = typecacheof(list( + /datum/id_trim/battlecruiser, + /datum/id_trim/bit_avatar, + /datum/id_trim/bounty_hunter, + /datum/id_trim/centcom, + /datum/id_trim/job, + /datum/id_trim/pirate, + /datum/id_trim/syndicom, + /datum/id_trim/technician_id, + )) - for(var/datum/id_trim/trim_path as anything in typesof(/datum/id_trim)) - if(chameleon_blacklist[trim_path]) + for(var/trim_path in chameleon_whitelist) + if(is_type_in_typecache(trim_path, chameleon_blacklist)) continue var/datum/id_trim/trim = SSid_access.trim_singletons_by_path[trim_path] if(trim && trim.trim_state && trim.assignment) - var/chameleon_item_name = "[trim.assignment] ([trim.trim_state])" - chameleon_list[chameleon_item_name] = trim_path + chameleon_list[format_readable_name(trim)] = trim_path chameleon_typecache[trim_path] = TRUE /datum/action/item_action/chameleon/change/id_trim/update_item(picked_trim_path) diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 258fd7540426..eeba73207354 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -589,7 +589,10 @@ /obj/item/clothing/glasses/thermal/xray name = "syndicate xray goggles" desc = "A pair of xray goggles manufactured by the Syndicate." + icon_state = "material" + color_cutoffs = null vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS + glass_colour_type = /datum/client_colour/glass_colour/lightblue /obj/item/clothing/glasses/thermal/xray/equipped(mob/living/carbon/human/user, slot) . = ..() diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index 90f85d352f04..ee8e2b49e43f 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -73,6 +73,7 @@ post_init_icon_state = "beret" greyscale_config = /datum/greyscale_config/beret greyscale_config_worn = /datum/greyscale_config/beret/worn + greyscale_config_onfloor = /datum/greyscale_config/beret/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#972A2A" dog_fashion = /datum/dog_fashion/head/beret hair_mask = /datum/hair_mask/standard_hat_middle @@ -104,6 +105,7 @@ post_init_icon_state = "beret_flat" greyscale_config = /datum/greyscale_config/beret greyscale_config_worn = /datum/greyscale_config/beret/worn + greyscale_config_onfloor = /datum/greyscale_config/beret/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#8F7654" inhand_icon_state = null flags_1 = parent_type::flags_1 | NO_NEW_GAGS_PREVIEW_1 diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm index 93c085f9fc1a..7a307d37b540 100644 --- a/code/modules/clothing/head/hat.dm +++ b/code/modules/clothing/head/hat.dm @@ -107,6 +107,7 @@ post_init_icon_state = "beret_flat" greyscale_config = /datum/greyscale_config/beret greyscale_config_worn = /datum/greyscale_config/beret/worn + greyscale_config_onfloor = /datum/greyscale_config/beret/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#8F7654" inhand_icon_state = null diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 44c04771dbc3..11689e4b1b35 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -132,6 +132,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#0070B7#FFCE5B" hair_mask = /datum/hair_mask/standard_hat_middle @@ -393,6 +394,7 @@ post_init_icon_state = "beret" greyscale_config = /datum/greyscale_config/beret greyscale_config_worn = /datum/greyscale_config/beret/worn + greyscale_config_onfloor = /datum/greyscale_config/beret/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#972A2A" flags_1 = IS_PLAYER_COLORABLE_1 hair_mask = /datum/hair_mask/standard_hat_middle @@ -454,6 +456,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#39393f#f0cc8f" hair_mask = /datum/hair_mask/standard_hat_middle @@ -571,6 +574,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#a52f29#F2F2F2" armor_type = /datum/armor/cosmetic_sec strip_delay = 6 SECONDS @@ -613,6 +617,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#7e1980#c9cbcb" //Medical @@ -797,6 +802,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#C5D4F3#ECF1F8" armor_type = /datum/armor/beret_durathread @@ -826,6 +832,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#46b946#f2c42e" armor_type = /datum/armor/beret_centcom_formal strip_delay = 10 SECONDS @@ -855,5 +862,6 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS greyscale_colors = "#43523d#a2abb0" armor_type = /datum/armor/cosmetic_sec diff --git a/code/modules/clothing/head/perceptomatrix.dm b/code/modules/clothing/head/perceptomatrix.dm index b2cdb061bdf0..f37532241383 100644 --- a/code/modules/clothing/head/perceptomatrix.dm +++ b/code/modules/clothing/head/perceptomatrix.dm @@ -164,17 +164,6 @@ 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) . = ..() @@ -216,11 +205,8 @@ /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() + do_sparks(2, TRUE, get_turf(owner), spark_type = /datum/effect_system/basic/spark_spread/quantum) + do_sparks(4, TRUE, get_turf(owner), spark_type = /datum/effect_system/basic/spark_spread/quantum) /datum/action/cooldown/spell/pointed/percept_hallucination/cast(mob/living/carbon/human/cast_on) . = ..() diff --git a/code/modules/clothing/head/tophat.dm b/code/modules/clothing/head/tophat.dm index 1efa8210db7b..add97bc164a2 100644 --- a/code/modules/clothing/head/tophat.dm +++ b/code/modules/clothing/head/tophat.dm @@ -22,7 +22,7 @@ COOLDOWN_START(src, rabbit_cooldown, RABBIT_CD_TIME) playsound(get_turf(src), 'sound/items/weapons/emitter.ogg', 70) - do_smoke(amount = DIAMOND_AREA(1), holder = src, location = src, smoke_type=/obj/effect/particle_effect/fluid/smoke/quick) + do_smoke(1, src, src, effect_type = /obj/effect/particle_effect/fluid/smoke/quick) if(prob(10)) magician.visible_message(span_danger("[magician] taps [src] with [hitby_wand], then reaches in and pulls out a bu- wait, those are bees!"), span_danger("You tap [src] with your [hitby_wand.name] and pull out... BEES!")) diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 49f5a7412b73..7d6df307725e 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -270,7 +270,7 @@ switch (body_part) if(BODY_ZONE_CHEST)//Listening to the chest user.visible_message(span_notice("[user] places [src] against [carbon_patient]'s [body_part] and listens attentively."), ignored_mobs = user) - if(!user.can_hear()) + if(HAS_TRAIT(user, TRAIT_DEAF)) to_chat(user, span_notice("You place [src] against [carbon_patient]'s [body_part]. Fat load of good it does you though, since you can't hear.")) return else diff --git a/code/modules/clothing/spacesuits/specialops.dm b/code/modules/clothing/spacesuits/specialops.dm index d0f06f89a67e..dc55e3ec0d50 100644 --- a/code/modules/clothing/spacesuits/specialops.dm +++ b/code/modules/clothing/spacesuits/specialops.dm @@ -6,6 +6,7 @@ post_init_icon_state = "beret_badge" greyscale_config = /datum/greyscale_config/beret_badge greyscale_config_worn = /datum/greyscale_config/beret_badge/worn + greyscale_config_onfloor = /datum/greyscale_config/beret_badge/onfloor // DARKPACK EDIT ADD - ONFLOOR_ICONS inhand_icon_state = null greyscale_colors = "#397F3F#FFCE5B" clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | SNUG_FIT diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm index e74144abedc3..2f5249158204 100644 --- a/code/modules/clothing/suits/reactive_armour.dm +++ b/code/modules/clothing/suits/reactive_armour.dm @@ -240,9 +240,7 @@ var/zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE /obj/item/clothing/suit/armor/reactive/tesla/cooldown_activation(mob/living/carbon/human/owner) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(1, 1, src) - sparks.start() + do_sparks(1, TRUE, src) ..() /obj/item/clothing/suit/armor/reactive/tesla/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) @@ -337,9 +335,7 @@ clothing_traits = list(TRAIT_MADNESS_IMMUNE) /obj/item/clothing/suit/armor/reactive/hallucinating/cooldown_activation(mob/living/carbon/human/owner) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(1, 1, src) - sparks.start() + do_sparks(1, TRUE, src) return ..() /obj/item/clothing/suit/armor/reactive/hallucinating/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) @@ -392,9 +388,7 @@ r_legs = typesof(/obj/item/bodypart/leg/right) /obj/item/clothing/suit/armor/reactive/bioscrambling/cooldown_activation(mob/living/carbon/human/owner) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(1, 1, src) - sparks.start() + do_sparks(1, TRUE, src) ..() /obj/item/clothing/suit/armor/reactive/bioscrambling/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) @@ -508,8 +502,7 @@ owner.visible_message(span_danger("The reactive armor alters the weather around [owner], shielding [owner.p_them()] from [attack_text]!")) playsound(src, 'sound/effects/magic/lightningshock.ogg', 33, TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) - var/datum/effect_system/steam_spread/steam = new() - steam.set_up(10, FALSE, owner.loc) + var/datum/effect_system/basic/steam_spread/steam = new(owner.loc, 10, FALSE) steam.start() var/list/affected_turfs = list() @@ -531,8 +524,7 @@ owner.visible_message(span_danger("The reactive armor malfunctions, calling down a storm upon [owner.p_them()]!")) playsound(src, 'sound/effects/magic/lightningshock.ogg', 33, TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) - var/datum/effect_system/steam_spread/steam = new() - steam.set_up(2, FALSE, owner.loc) + var/datum/effect_system/basic/steam_spread/steam = new(owner.loc, 2, FALSE) steam.start() owner.adjust_wet_stacks(10) diff --git a/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm index 96254bba8fd2..9f64535e13b7 100644 --- a/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm +++ b/code/modules/clothing/under/jobs/Plasmaman/civilian_service.dm @@ -176,8 +176,4 @@ addtimer(CALLBACK(src, PROC_REF(check_fire_state)), extinguish_cooldown) owner.visible_message(span_warning("[owner]'s suit spews space lube everywhere!"), span_warning("Your suit spews space lube everywhere!")) owner.extinguish_mob() - var/datum/effect_system/fluid_spread/foam/foam = new - var/datum/reagents/foamreagent = new /datum/reagents(15) - foamreagent.add_reagent(/datum/reagent/lube, 15) - foam.set_up(4, holder = src, location = get_turf(owner), carry = foamreagent) - foam.start() //Truly terrifying. + do_foam(4, src, get_turf(owner), /datum/reagent/lube, 15) diff --git a/code/modules/clothing/under/jobs/civilian/clown_mime.dm b/code/modules/clothing/under/jobs/civilian/clown_mime.dm index bb0c33e7e2f7..a452c6c98e04 100644 --- a/code/modules/clothing/under/jobs/civilian/clown_mime.dm +++ b/code/modules/clothing/under/jobs/civilian/clown_mime.dm @@ -31,7 +31,7 @@ inhand_icon_state = "clown" female_sprite_flags = FEMALE_UNIFORM_TOP_ONLY can_adjust = FALSE - supports_variations_flags = CLOTHING_NO_VARIATION + supports_variations_flags = NONE /obj/item/clothing/under/rank/civilian/clown/Initialize(mapload) . = ..() diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index eb06c86fbe49..7110fab35bca 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -10,7 +10,7 @@ gender = PLURAL body_parts_covered = GROIN female_sprite_flags = NO_FEMALE_UNIFORM - supports_variations_flags = CLOTHING_NO_VARIATION + supports_variations_flags = NONE can_adjust = FALSE species_exception = list(/datum/species/golem) flags_1 = IS_PLAYER_COLORABLE_1 diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm index 797dd9d3856b..5c0f8b3fe9d1 100644 --- a/code/modules/economy/account.dm +++ b/code/modules/economy/account.dm @@ -244,7 +244,7 @@ if(!card_holder.client || (!(get_chat_toggles(card_holder.client) & CHAT_BANKCARD) && !force)) return - if(card_holder.can_hear()) + if(!HAS_TRAIT(card_holder, TRAIT_DEAF)) card_holder.playsound_local(get_turf(card_holder), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(card_holder, "[icon2html(icon_source, card_holder)] [span_notice("[message]")]") else if(isturf(card.loc)) //If on the ground @@ -252,7 +252,7 @@ for(var/mob/potential_hearer in hearers(1,card_location)) if(!potential_hearer.client || (!(get_chat_toggles(potential_hearer.client) & CHAT_BANKCARD) && !force)) continue - if(potential_hearer.can_hear()) + if(!HAS_TRAIT(potential_hearer, TRAIT_DEAF)) potential_hearer.playsound_local(card_location, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]") else @@ -262,7 +262,7 @@ continue if(!sound_atom) sound_atom = card.drop_location() //in case we're inside a bodybag in a crate or something. doing this here to only process it if there's a valid mob who can hear the sound. - if(potential_hearer.can_hear()) + if(!HAS_TRAIT(potential_hearer, TRAIT_DEAF)) potential_hearer.playsound_local(get_turf(sound_atom), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE) to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]") diff --git a/code/modules/events/immovable_rod/immovable_rod.dm b/code/modules/events/immovable_rod/immovable_rod.dm index 3a52689c8c1a..e2404bbc0906 100644 --- a/code/modules/events/immovable_rod/immovable_rod.dm +++ b/code/modules/events/immovable_rod/immovable_rod.dm @@ -164,9 +164,7 @@ // they ALSO collapse into a singulo. if(istype(clong, /obj/effect/immovablerod)) visible_message(span_danger("[src] collides with [clong]! This cannot end well.")) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(2, holder = src, location = get_turf(src)) - smoke.start() + do_smoke(2, src, get_turf(src)) var/obj/singularity/bad_luck = new(get_turf(src)) bad_luck.energy = 800 qdel(clong) diff --git a/code/modules/events/radiation_leak.dm b/code/modules/events/radiation_leak.dm index a5aee22a8b26..56e6e87b9a9b 100644 --- a/code/modules/events/radiation_leak.dm +++ b/code/modules/events/radiation_leak.dm @@ -135,13 +135,9 @@ /// Helper to shoot some smoke into the air around the passed atom /datum/round_event/radiation_leak/proc/puff_some_smoke(atom/where) + // Causes radioation and mutations var/turf/below_where = get_turf(where) - var/datum/effect_system/fluid_spread/smoke/chem/gross_smoke = new() - gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/polonium, 10) // Polonium (it causes radiation) - gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/mutagen, 10) // Mutagen (it causes mutations. Also it's green... Primarily because it's green.) - gross_smoke.attach(below_where) - gross_smoke.set_up(2, holder = where, location = below_where, silent = TRUE) - gross_smoke.start() + do_chem_smoke(2, where, below_where, list(/datum/reagent/toxin/polonium = 10, /datum/reagent/toxin/mutagen = 10)) playsound(below_where, 'sound/effects/smoke.ogg', 50, vary = TRUE) /** diff --git a/code/modules/events/wisdomcow.dm b/code/modules/events/wisdomcow.dm index fbe824c06e6b..18f0213c5d70 100644 --- a/code/modules/events/wisdomcow.dm +++ b/code/modules/events/wisdomcow.dm @@ -32,7 +32,7 @@ else targetloc = get_safe_random_station_turf() var/mob/living/basic/cow/wisdom/wise = new(targetloc, selected_wisdom, selected_experience, forced_reagent_type) - do_smoke(1, holder = wise, location = targetloc) + do_smoke(1, wise, targetloc) announce_to_ghosts(wise) /datum/event_admin_setup/set_location/wisdom_cow diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index ab305bbff99b..e05bd86b6816 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -83,9 +83,7 @@ victims += target for(var/mob/living/carbon/human/victim as anything in victims) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = victim, location = victim.loc) - smoke.start() + do_smoke(0, victim, victim.loc) #undef BIG_FAT_DOOBIE #undef BOXING diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm index 43517ec26dd4..e2c9416c5fc3 100644 --- a/code/modules/events/wizard/rpgloot.dm +++ b/code/modules/events/wizard/rpgloot.dm @@ -34,10 +34,9 @@ can_backfire = TRUE return ..() - /obj/item/upgradescroll/pre_attack(obj/item/target, mob/living/user, list/modifiers, list/attack_modifiers) . = ..() - if(. || !istype(target) || !user.combat_mode) + if(. || !istype(target)) return target.AddComponent(/datum/component/fantasy, upgrade_amount, null, null, can_backfire, TRUE) uses -= 1 diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 460fe7b8a2f6..b7961fa4cfa9 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -34,9 +34,7 @@ moblocs.len -= 1 for(var/mob/living/carbon/human/victim in GLOB.alive_mob_list) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = victim, location = victim.loc) - smoke.start() + do_smoke(0, victim, victim.loc) //---// @@ -69,9 +67,7 @@ mobnames.len -= 1 for(var/mob/living/carbon/human/victim in GLOB.alive_mob_list) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = victim, location = victim.loc) - smoke.start() + do_smoke(0, victim, victim.loc) //---// @@ -107,6 +103,4 @@ qdel(swapper) for(var/mob/living/carbon/human/alive_human in GLOB.alive_mob_list) - var/datum/effect_system/fluid_spread/smoke/smoke = new() - smoke.set_up(0, holder = alive_human, location = alive_human.loc) - smoke.start() + do_smoke(0, alive_human, alive_human.loc) diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm index 08fc0ae9142a..ad2a55592e5b 100644 --- a/code/modules/explorer_drone/exodrone.dm +++ b/code/modules/explorer_drone/exodrone.dm @@ -437,7 +437,7 @@ GLOBAL_LIST_EMPTY(exodrone_launchers) */ /obj/machinery/exodrone_launcher/proc/launch_effect() playsound(src,'sound/effects/podwoosh.ogg',50, FALSE) - do_smoke(1, holder = src, location = get_turf(src)) + do_smoke(1, src, get_turf(src)) /obj/machinery/exodrone_launcher/Exited(atom/movable/gone, direction) . = ..() diff --git a/code/modules/fishing/fish/types/rift.dm b/code/modules/fishing/fish/types/rift.dm index 641336f0dbee..626be1ecde0f 100644 --- a/code/modules/fishing/fish/types/rift.dm +++ b/code/modules/fishing/fish/types/rift.dm @@ -636,7 +636,7 @@ to_chat(screeched, span_notice("You resist the psychic wail!")) continue var/power = 1 - if(!screeched.can_hear()) // bit weaker if deaf. but its still psychic + if(HAS_TRAIT(screeched, TRAIT_DEAF)) // bit weaker if deaf. but its still psychic power *= 0.5 var/affect_time = 15 SECONDS * power // it really fucks you up diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm index 01d1d3c0c12a..d3c9088aec5f 100644 --- a/code/modules/fishing/fishing_rod.dm +++ b/code/modules/fishing/fishing_rod.dm @@ -371,7 +371,7 @@ user = user || loc if (!isliving(user) || !user.mind || !user.is_holding(src)) return - . += round(user.st_get_stat(STAT_STRENGTH) * 0.3) // DARKPACK EDIT CHANGE - STORYTELLR_STATS + . += round(user.st_get_stat(STAT_STRENGTH) * 0.3) // DARKPACK EDIT CHANGE - STORYTELLER_STATS return max(., 1) /obj/item/fishing_rod/dropped(mob/user, silent) diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm index 050ebd9ba49d..0152332dac43 100644 --- a/code/modules/food_and_drinks/machinery/gibber.dm +++ b/code/modules/food_and_drinks/machinery/gibber.dm @@ -297,7 +297,7 @@ for(var/obj/item/result as anything in results) if (victim.reagents) victim.reagents.trans_to(result, victim.reagents.total_volume / reagents_in_produced, remove_blacklisted = TRUE) - result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, victim.nutrition / 15 / reagents_in_produced) + result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, victim.nutrition / /datum/reagent/consumable/nutriment/fat::nutriment_factor / reagents_in_produced) /obj/machinery/gibber/proc/finish_gibbing(list/obj/item/results, gibs_type, list/datum/disease/diseases) playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) diff --git a/code/modules/food_and_drinks/machinery/grill.dm b/code/modules/food_and_drinks/machinery/grill.dm index 75c8a06359dd..e6e664eb3461 100644 --- a/code/modules/food_and_drinks/machinery/grill.dm +++ b/code/modules/food_and_drinks/machinery/grill.dm @@ -43,9 +43,7 @@ new /obj/item/stack/rods(loc, 5) if(grill_fuel > 0) - var/datum/effect_system/fluid_spread/smoke/bad/smoke = new - smoke.set_up(1, holder = src, location = loc) - smoke.start() + do_smoke(1, src, loc, smoke_type = /datum/effect_system/fluid_spread/smoke/bad) /obj/machinery/grill/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = NONE @@ -273,9 +271,7 @@ //use fuel, create smoke puffs for immersion grill_fuel -= fuel_usage if(SPT_PROB(0.5, seconds_per_tick)) - var/datum/effect_system/fluid_spread/smoke/bad/smoke = new - smoke.set_up(1, holder = src, location = loc) - smoke.start() + do_smoke(1, src, loc, smoke_type = /datum/effect_system/fluid_spread/smoke/bad) fuel_usage = GRILL_FUELUSAGE_ACTIVE * seconds_per_tick if(!QDELETED(grilled_item)) diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm index 2235b5b243bc..fce07350527f 100644 --- a/code/modules/food_and_drinks/machinery/microwave.dm +++ b/code/modules/food_and_drinks/machinery/microwave.dm @@ -643,9 +643,7 @@ /obj/machinery/microwave/proc/spark() visible_message(span_warning("Sparks fly around [src]!")) - var/datum/effect_system/spark_spread/sparks = new - sparks.set_up(2, 1, src) - sparks.start() + do_sparks(2, TRUE, src) /** * The start of the cook loop diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm index ff9e49dbffcc..f046278142d1 100644 --- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm @@ -205,10 +205,7 @@ if(holder.total_volume >= holder.maximum_volume * 0.95) below_pot.visible_message(span_warning("[pot] starts to boil over!")) // Create a spread of dirty foam - var/datum/effect_system/fluid_spread/foam/dirty/soup_mess = new() - soup_mess.reagent_scale = 0.1 // (Just a little) - soup_mess.set_up(range = 1, holder = pot, location = below_pot, carry = holder, stop_reactions = TRUE) - soup_mess.start() + do_foam(1, pot, below_pot, carry = holder, foam_type = /datum/effect_system/fluid_spread/foam/dirty, stop_reactions = TRUE, reagent_scale = 0.1) // Loses a bit from the foam for(var/datum/reagent/reagent as anything in holder.reagent_list) reagent.volume *= 0.5 diff --git a/code/modules/hallucination/battle.dm b/code/modules/hallucination/battle.dm index 985709b90d02..60e2555e3873 100644 --- a/code/modules/hallucination/battle.dm +++ b/code/modules/hallucination/battle.dm @@ -5,7 +5,7 @@ hallucination_tier = HALLUCINATION_TIER_COMMON /datum/hallucination/battle/start() - if(!hallucinator.can_hear()) + if(HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE // for subtypes diff --git a/code/modules/hallucination/fake_sound.dm b/code/modules/hallucination/fake_sound.dm index 7b2e2ee88255..b78095486305 100644 --- a/code/modules/hallucination/fake_sound.dm +++ b/code/modules/hallucination/fake_sound.dm @@ -11,7 +11,7 @@ var/sound_type /datum/hallucination/fake_sound/start() - if(!hallucinator.can_hear()) + if(HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE var/sound_to_play = islist(sound_type) ? pick(sound_type) : sound_type diff --git a/code/modules/hallucination/station_message.dm b/code/modules/hallucination/station_message.dm index a8bddedcdad3..33f20cb10146 100644 --- a/code/modules/hallucination/station_message.dm +++ b/code/modules/hallucination/station_message.dm @@ -8,7 +8,7 @@ var/require_hearing = TRUE /datum/hallucination/station_message/start() - if(require_hearing && !hallucinator.can_hear()) + if(require_hearing && HAS_TRAIT(hallucinator, TRAIT_DEAF)) return FALSE if(do_fake_alert() == CANCEL_FAKE_ALERT) return FALSE diff --git a/code/modules/holodeck/holo_effect.dm b/code/modules/holodeck/holo_effect.dm index fbff1b5a861e..55535d4b5707 100644 --- a/code/modules/holodeck/holo_effect.dm +++ b/code/modules/holodeck/holo_effect.dm @@ -36,9 +36,7 @@ /obj/effect/holodeck_effect/sparks/activate(obj/machinery/computer/holodeck/HC) var/turf/T = get_turf(src) if(T) - var/datum/effect_system/spark_spread/s = new - s.set_up(3, 1, T) - s.start() + do_sparks(3, TRUE, T) T.temperature = 5000 //Why? not quite sure to be honest with you T.hotspot_expose(50000,50000,1) @@ -63,7 +61,7 @@ our_mob.flags_1 |= HOLOGRAM_1 // these vars are not really standardized but all would theoretically create stuff on death - our_mob.add_traits(list(TRAIT_PERMANENTLY_MORTAL, TRAIT_NO_BLOOD_OVERLAY, TRAIT_NOBLOOD, TRAIT_NOHUNGER), INNATE_TRAIT) + our_mob.add_traits(list(TRAIT_PERMANENTLY_MORTAL, TRAIT_NO_BLOOD_OVERLAY, TRAIT_NOBLOOD, TRAIT_NOHUNGER, TRAIT_SPAWNED_MOB), INNATE_TRAIT) RegisterSignal(our_mob, COMSIG_QDELETING, PROC_REF(handle_mob_delete)) return our_mob diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm index 45f4d1251ded..326c4ac761ae 100644 --- a/code/modules/hydroponics/grown/onion.dm +++ b/code/modules/hydroponics/grown/onion.dm @@ -50,12 +50,8 @@ AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/onion_slice/red, 2, 15, screentip_verb = "Cut") /obj/item/food/grown/onion/UsedforProcessing(mob/living/user, obj/item/I, list/chosen_option, list/created_atoms) - var/datum/effect_system/fluid_spread/smoke/chem/cry_about_it = new //Since the onion is destroyed when it's sliced, var/splat_location = get_turf(src) //we need to set up the smoke beforehand - cry_about_it.attach(splat_location) - cry_about_it.set_up(0, holder = src, location = splat_location, carry = reagents, silent = FALSE) - cry_about_it.start() - qdel(cry_about_it) + do_chem_smoke(0, src, splat_location, carry = reagents, silent = FALSE) return ..() /obj/item/food/onion_slice diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 13f001b337c1..aa379ce01a8b 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -382,18 +382,16 @@ //This is where stability mutations exist now. if(myseed.instability >= 80) - var/mutation_chance = myseed.instability - 75 - mutate(0, 0, 0, 0, 0, 0, 0, mutation_chance, 0) //Scaling odds of a random trait or chemical + traitmutate(myseed.instability - 75) //Scaling odds of a random trait or chemical if(myseed.instability >= 60) if(prob((myseed.instability)/2) && !self_sustaining && LAZYLEN(myseed.mutatelist) && !myseed.get_gene(/datum/plant_gene/trait/never_mutate)) //Minimum 30%, Maximum 50% chance of mutating every age tick when not on autogrow or having Prosophobic Inclination trait. mutatespecie() myseed.set_instability(myseed.instability/2) - if(myseed.instability >= 40) - if(prob(myseed.instability) && !myseed.get_gene(/datum/plant_gene/trait/stable_stats)) //No hardmutation if Symbiotic Resilience trait is present. - hardmutate() - if(myseed.instability >= 20 ) - if(prob(myseed.instability) && !myseed.get_gene(/datum/plant_gene/trait/stable_stats)) //No mutation if Symbiotic Resilience trait is present. - mutate() + if(myseed.instability >= 20 && prob(myseed.instability) && !myseed.get_gene(/datum/plant_gene/trait/stable_stats)) //No hardmutation if Symbiotic Resilience trait is present. + if(myseed.instability >= 40) + hardmutate(stabmut = myseed.instability >= 80 ? 5 : 0) + else + mutate(stabmut = 0) //Health & Age/////////////////////////////////////////////////////////// @@ -677,15 +675,50 @@ set_pestlevel(0) // Reset visible_message(span_warning("The [oldPlantName] is overtaken by some [myseed.plantname]!")) +/// Mutates the stats of the current seed /obj/machinery/hydroponics/proc/mutate(lifemut = 2, endmut = 5, productmut = 1, yieldmut = 2, potmut = 25, wrmut = 2, wcmut = 5, traitmut = 0, stabmut = 3) // Mutates the current seed - if(!myseed) - return - myseed.mutate(lifemut, endmut, productmut, yieldmut, potmut, wrmut, wcmut, traitmut, stabmut) + myseed?.mutate( + lifemut = lifemut, + endmut = endmut, + productmut = productmut, + yieldmut = yieldmut, + potmut = potmut, + wrmut = wrmut, + wcmut = wcmut, + traitmut = traitmut, + stabmut = stabmut, + ) +/// Mutate but with higher default values /obj/machinery/hydroponics/proc/hardmutate(lifemut = 4, endmut = 10, productmut = 2, yieldmut = 4, potmut = 50, wrmut = 4, wcmut = 10, traitmut = 0, stabmut = 4) - mutate(lifemut, endmut, productmut, yieldmut, potmut, wrmut, wcmut, traitmut, stabmut) + myseed?.mutate( + lifemut = lifemut, + endmut = endmut, + productmut = productmut, + yieldmut = yieldmut, + potmut = potmut, + wrmut = wrmut, + wcmut = wcmut, + traitmut = traitmut, + stabmut = stabmut, + ) + +/// Mutate but only introduce a random trait +/obj/machinery/hydroponics/proc/traitmutate(traitmut = 1) + myseed?.mutate( + lifemut = 0, + endmut = 0, + productmut = 0, + yieldmut = 0, + potmut = 0, + wrmut = 0, + wcmut = 0, + traitmut = traitmut, + stabmut = 0, + ) -/obj/machinery/hydroponics/proc/mutatespecie() // Mutagent produced a new plant! +/// Mutate the species of the plant into one of its mutations +/obj/machinery/hydroponics/proc/mutatespecie() if(!myseed || plant_status == HYDROTRAY_PLANT_DEAD || !LAZYLEN(myseed.mutatelist)) return @@ -701,7 +734,8 @@ var/message = span_warning("[oldPlantName] suddenly mutates into [myseed.plantname]!") addtimer(CALLBACK(src, PROC_REF(after_mutation), message), 0.5 SECONDS) -/obj/machinery/hydroponics/proc/polymorph() // Polymorph a plant into another plant +/// Transform the plant into a completely random species +/obj/machinery/hydroponics/proc/polymorph() if(!myseed || plant_status == HYDROTRAY_PLANT_DEAD) return @@ -717,7 +751,8 @@ var/message = span_warning("[oldPlantName] suddenly polymorphs into [myseed.plantname]!") addtimer(CALLBACK(src, PROC_REF(after_mutation), message), 0.5 SECONDS) -/obj/machinery/hydroponics/proc/mutateweed() // If the weeds gets the mutagent instead. Mind you, this pretty much destroys the old plant +/// Mutates the weeds in the tray into a random weed plant (which can overtake existing plants) +/obj/machinery/hydroponics/proc/mutateweed() if(weedlevel <= 5) visible_message(span_warning("The few weeds in [src] seem to react, but only for a moment...")) return diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index 0844708b3ff8..2835d49908f2 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -691,13 +691,10 @@ SIGNAL_HANDLER our_plant.investigate_log("made smoke at [AREACOORD(target)]. Last touched by: [our_plant.fingerprintslast].", INVESTIGATE_BOTANY) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new () var/obj/item/seeds/our_seed = our_plant.get_plant_seed() var/splat_location = get_turf(target) var/range = sqrt(our_seed.potency * 0.1) - smoke.attach(splat_location) - smoke.set_up(round(range), holder = our_plant, location = splat_location, carry = our_plant.reagents, silent = FALSE) - smoke.start(log = TRUE) + do_chem_smoke(round(range), our_plant, splat_location, carry = our_plant.reagents, silent = FALSE, log = TRUE) our_plant.reagents.clear_reagents() /// Makes the plant and its seeds fireproof. From lavaland plants. diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm index 8d6dfcf1853e..831986982ce4 100644 --- a/code/modules/jobs/job_types/_job.dm +++ b/code/modules/jobs/job_types/_job.dm @@ -136,6 +136,9 @@ /// If set, look for a policy with this instead of the job title var/policy_override + /// How desensitized this job is to seeing death as a base - applied with the job + var/desensitized_base = 1.0 + /datum/job/New() . = ..() // DARKPACK EDIT ADD START - ALTERNATIVE_JOB_TITLES @@ -155,6 +158,8 @@ if(length(mind_traits)) spawned.mind.add_traits(mind_traits, JOB_TRAIT) + spawned.mind.desensitized_level = clamp(desensitized_base, DESENSITIZED_MINIMUM, spawned.mind.desensitized_level) + var/obj/item/organ/liver/liver = spawned.get_organ_slot(ORGAN_SLOT_LIVER) if(liver && length(liver_traits)) liver.add_traits(liver_traits, JOB_TRAIT) diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm index 718751bf7876..a3ad2d52aa85 100644 --- a/code/modules/jobs/job_types/captain.dm +++ b/code/modules/jobs/job_types/captain.dm @@ -22,7 +22,8 @@ paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_SEC - mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS, TRAIT_DESENSITIZED) + mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_ROYAL_METABOLISM) display_order = JOB_DISPLAY_ORDER_CAPTAIN diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm index 75fd0a7c00a7..d4c39db5beea 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain.dm @@ -12,7 +12,8 @@ outfit = /datum/outfit/job/chaplain plasmaman_outfit = /datum/outfit/plasmaman/chaplain - mind_traits = list(TRAIT_SPIRITUAL, TRAIT_DESENSITIZED) + mind_traits = list(TRAIT_SPIRITUAL) + desensitized_base = DESENSITIZED_THRESHOLD paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_SRV diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm index d02915d63196..5e67c8de35bf 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm @@ -19,6 +19,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) Harms you if you dismiss the scythe without first causing harm to a creature. \ The shard also causes you to become Morbid, shifting your interests towards the macabre." rods[/obj/item/melee/skateboard/holyboard] = "A skateboard that grants you flight and anti-magic abilities while ridden. Fits in your bag." + rods[/obj/item/storage/belt/sheath/hanzo_katana] = "A sharp katana which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt, wearing the sheath on belt allows for a swift counterattack." for(var/obj/item/melee/energy/sword/nullrod/energy_nullrod_type as anything in typesof(/obj/item/melee/energy/sword/nullrod)) rods[energy_nullrod_type] = "An energy sword, but with a lower force, no armour penetration and a low chance of blocking. Can be switched on and off. \ @@ -46,8 +47,6 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) var/chaplain_spawnable = TRUE /// Short description of what this item is capable of, for radial menu uses. var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt." - /// Lazylist, tracks refs()s to all cultists which have been crit or killed by this nullrod. - var/list/cultists_slain /// Affects GLOB.holy_weapon_type. Disable to allow null rods to change at will and without affecting the station's type. var/station_holy_item = TRUE @@ -71,26 +70,6 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) user.visible_message(span_suicide("[user] is killing [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to get closer to god!")) return (BRUTELOSS|FIRELOSS) -/obj/item/nullrod/attack(mob/living/target_mob, mob/living/user, list/modifiers, list/attack_modifiers) - if(!user.mind?.holy_role) - return ..() - if(!IS_CULTIST(target_mob) || istype(target_mob, /mob/living/carbon/human/cult_ghost)) - return ..() - - var/old_stat = target_mob.stat - . = ..() - if(old_stat < target_mob.stat) - LAZYOR(cultists_slain, REF(target_mob)) - return . - -/obj/item/nullrod/examine(mob/user) - . = ..() - if(!IS_CULTIST(user) || !GET_ATOM_BLOOD_DNA_LENGTH(src)) - return - - var/num_slain = LAZYLEN(cultists_slain) - . += span_cult_italic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \ - Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.") /obj/item/nullrod/non_station station_holy_item = FALSE @@ -174,8 +153,9 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) desc = "Capable of cutting clean through a holy claymore." icon_state = "katana" inhand_icon_state = "katana" - worn_icon_state = "katana" - menu_description = "A sharp katana which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt." + pickup_sound = 'sound/items/unsheath.ogg' + slot_flags = NONE + chaplain_spawnable = FALSE /obj/item/nullrod/claymore/multiverse name = "extradimensional blade" @@ -611,6 +591,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants()) lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' righthand_file = 'icons/mob/inhands/items_righthand.dmi' force = 15 + obj_flags = parent_type::obj_flags | UNIQUE_RENAME offspring_type = /obj/item/toy/plush/carpplushie divine = TRUE 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 f2c7ed53d40f..91823a073c8b 100644 --- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm +++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm @@ -45,6 +45,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and armour_penetration = 50 //Very good armor penetration to make up for our abysmal force reach = 2 //why yes, this does have reach slot_flags = null + obj_flags = UNIQUE_RENAME sharpness = SHARP_EDGED attack_verb_continuous = list("chops", "slices", "cuts", "reaps") attack_verb_simple = list("chop", "slice", "cut", "reap") diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm index 4cab7531da43..3b4a856a1189 100644 --- a/code/modules/jobs/job_types/chief_medical_officer.dm +++ b/code/modules/jobs/job_types/chief_medical_officer.dm @@ -26,7 +26,8 @@ paycheck = PAYCHECK_COMMAND paycheck_department = ACCOUNT_MED - mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS, MEDICAL_MIND_TRAITS) + mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_MEDICAL_METABOLISM, TRAIT_ROYAL_METABOLISM) display_order = JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm index 672d2f616e59..aebe1b9ca803 100644 --- a/code/modules/jobs/job_types/cook.dm +++ b/code/modules/jobs/job_types/cook.dm @@ -15,7 +15,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_SRV - mind_traits = list(TRAIT_DESENSITIZED) // butcher + desensitized_base = DESENSITIZED_THRESHOLD // butcher liver_traits = list(TRAIT_CULINARY_METABOLISM) display_order = JOB_DISPLAY_ORDER_COOK @@ -37,6 +37,7 @@ /obj/item/reagent_containers/condiment/flour = 7, /obj/item/reagent_containers/condiment/rice = 7, /obj/item/reagent_containers/condiment/ketchup = 7, + /obj/item/reagent_containers/condiment/mustard = 7, /obj/item/reagent_containers/condiment/enzyme = 7, /obj/item/reagent_containers/condiment/soymilk = 7, /obj/item/kitchen/spoon/soup_ladle = 6, diff --git a/code/modules/jobs/job_types/coroner.dm b/code/modules/jobs/job_types/coroner.dm index ddb1ad7e83f3..150d029d35cd 100644 --- a/code/modules/jobs/job_types/coroner.dm +++ b/code/modules/jobs/job_types/coroner.dm @@ -12,7 +12,8 @@ outfit = /datum/outfit/job/coroner plasmaman_outfit = /datum/outfit/plasmaman/coroner - mind_traits = list(TRAIT_MORBID, MEDICAL_MIND_TRAITS) + mind_traits = list(TRAIT_MORBID) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_CORONER_METABOLISM) paycheck = PAYCHECK_CREW diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm index e56225c42a1a..4926749c6367 100644 --- a/code/modules/jobs/job_types/detective.dm +++ b/code/modules/jobs/job_types/detective.dm @@ -22,7 +22,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_SEC - mind_traits = list(SECURITY_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) display_order = JOB_DISPLAY_ORDER_DETECTIVE diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm index 4fcad1b97af9..936028f86f01 100644 --- a/code/modules/jobs/job_types/head_of_security.dm +++ b/code/modules/jobs/job_types/head_of_security.dm @@ -23,7 +23,8 @@ /datum/job_department/command, ) - mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS, SECURITY_MIND_TRAITS) + mind_traits = list(HEAD_OF_STAFF_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM, TRAIT_ROYAL_METABOLISM) paycheck = PAYCHECK_COMMAND diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm index d7963faeedf2..f357bcac8aca 100644 --- a/code/modules/jobs/job_types/medical_doctor.dm +++ b/code/modules/jobs/job_types/medical_doctor.dm @@ -15,7 +15,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_MED - mind_traits = list(MEDICAL_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_MEDICAL_METABOLISM) display_order = JOB_DISPLAY_ORDER_MEDICAL_DOCTOR diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm index 85aae2f1f945..ad9244333b87 100644 --- a/code/modules/jobs/job_types/paramedic.dm +++ b/code/modules/jobs/job_types/paramedic.dm @@ -15,7 +15,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_MED - mind_traits = list(MEDICAL_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_MEDICAL_METABOLISM) display_order = JOB_DISPLAY_ORDER_PARAMEDIC @@ -79,4 +79,3 @@ mask = /obj/item/clothing/mask/breath/medical r_pocket = /obj/item/flashlight/pen internals_slot = ITEM_SLOT_SUITSTORE - diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm index 78ea30f1fc16..06c78c25f1c7 100644 --- a/code/modules/jobs/job_types/security_officer.dm +++ b/code/modules/jobs/job_types/security_officer.dm @@ -19,7 +19,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_SEC - mind_traits = list(SECURITY_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) display_order = JOB_DISPLAY_ORDER_SECURITY_OFFICER diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm index c759552c0497..bcc07dfd2d66 100644 --- a/code/modules/jobs/job_types/warden.dm +++ b/code/modules/jobs/job_types/warden.dm @@ -20,7 +20,7 @@ paycheck = PAYCHECK_CREW paycheck_department = ACCOUNT_SEC - mind_traits = list(SECURITY_MIND_TRAITS) + desensitized_base = DESENSITIZED_THRESHOLD liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM, TRAIT_PRETENDER_ROYAL_METABOLISM) display_order = JOB_DISPLAY_ORDER_WARDEN diff --git a/code/modules/language/_language_manuals.dm b/code/modules/language/_language_manuals.dm index 507d663ef869..9cb4cc19e036 100644 --- a/code/modules/language/_language_manuals.dm +++ b/code/modules/language/_language_manuals.dm @@ -45,10 +45,9 @@ /obj/item/language_manual/proc/use_charge(mob/user) charges-- if(!charges) - var/turf/T = get_turf(src) - T.visible_message(span_warning("The cover and contents of [src] start shifting and changing! It slips out of your hands!")) - - new /obj/item/book/manual/random(T) + user.visible_message(span_notice("The cover of [user]'s book start shifting and changing! It falls out of [user.p_their()] hands!"), + span_warning("The cover and contents of [src] start shifting and changing! It slips out of your hands!")) + new /obj/item/book/manual/random(get_turf(src)) qdel(src) /obj/item/language_manual/codespeak_manual diff --git a/code/modules/library/skill_learning/generic_skillchips/acrobatics.dm b/code/modules/library/skill_learning/generic_skillchips/acrobatics.dm index dee9f5d53c54..c025b21069a4 100644 --- a/code/modules/library/skill_learning/generic_skillchips/acrobatics.dm +++ b/code/modules/library/skill_learning/generic_skillchips/acrobatics.dm @@ -12,7 +12,7 @@ max_integrity = 100 /// list of emotes whose cd is overridden by this skillchip. can be edited in mapping or ingame var/list/affected_emotes = list("spin", "flip", "backflip") - var/datum/effect_system/spark_spread/sparks + var/datum/effect_system/basic/spark_spread/sparks /// you can use this without lowering integrity! let's be honest. nobody's doing that var/allowed_usage = 5 /// How many seconds does it take for it to recover one allowed usage @@ -49,7 +49,8 @@ take_damage(1, sound_effect = FALSE) if(!sparks) - sparks = new(src) + sparks = new(src, 5, FALSE) + sparks.attach(src) // minimum roll is by default capped at 50, with the min value lowering as integrity is reduced. var/mintegrity = clamp(50 - (100 - get_integrity()), 1, 100) @@ -84,8 +85,7 @@ // does not necessarily kill you directly. instead it causes cranial fissure + something to drop from your head. could be eyes, tongue, ears, brain, even implants new /obj/effect/gibspawner/generic(get_turf(bozo), bozo) - - sparks.set_up(15, cardinals_only = FALSE, location = get_turf(src)) + sparks.amount = 15 sparks.start() qdel(src) @@ -101,8 +101,7 @@ bozo.set_eye_blur_if_lower(10 SECONDS) // but the rest of the effects will happen either way bozo.adjust_organ_loss(ORGAN_SLOT_BRAIN, 20 - get_integrity()) - - sparks.set_up(5, cardinals_only = FALSE, location = get_turf(src)) + sparks.amount = 5 sparks.start() // brain Smoking. you should probably stop now @@ -142,8 +141,7 @@ particle_effect.set_particle_position(-2, 12, 0) bozo.apply_status_effect(/datum/status_effect/temperature_over_time/chip_overheat, 15 SECONDS) QDEL_IN(particle_effect, 15 SECONDS) - - sparks.set_up(10, cardinals_only = FALSE, location = get_turf(src)) + sparks.amount = 10 sparks.start() // hey, something isn't right... @@ -152,7 +150,7 @@ span_warning("[bozo]'s head sparks."), ) - sparks.set_up(rand(1,2), cardinals_only = TRUE, location = get_turf(src)) + sparks.amount = rand(1, 2) sparks.start() return COMPONENT_EMOTE_COOLDOWN_BYPASS diff --git a/code/modules/library/skill_learning/job_skillchips/_job.dm b/code/modules/library/skill_learning/job_skillchips/_job.dm index 23381d06066b..de3a75056273 100644 --- a/code/modules/library/skill_learning/job_skillchips/_job.dm +++ b/code/modules/library/skill_learning/job_skillchips/_job.dm @@ -2,5 +2,5 @@ skillchip_flags = SKILLCHIP_RESTRICTED_CATEGORIES chip_category = SKILLCHIP_CATEGORY_JOB incompatibility_list = list(SKILLCHIP_CATEGORY_JOB) - abstract_parent_type = /obj/item/skillchip/job + abstract_type = /obj/item/skillchip/job slot_use = 2 diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm index e4fce399a760..c00dd40878be 100644 --- a/code/modules/library/skill_learning/skillchip.dm +++ b/code/modules/library/skill_learning/skillchip.dm @@ -6,6 +6,10 @@ icon_state = "skillchip" custom_price = PAYCHECK_CREW * 3 w_class = WEIGHT_CLASS_SMALL + /// Used to determine if this is an abstract type or not. + /// If this is meant to be an abstract type, set it to the type's path. + /// Will be overridden by subsequent abstract parents. + abstract_type = /obj/item/skillchip /// Traits automatically granted by this chip, optional. Lazylist. var/list/auto_traits @@ -35,10 +39,6 @@ var/cooldown = 5 MINUTES /// Cooldown for chip actions. COOLDOWN_DECLARE(chip_cooldown) - /// Used to determine if this is an abstract type or not. - /// If this is meant to be an abstract type, set it to the type's path. - /// Will be overridden by subsequent abstract parents. - var/abstract_parent_type = /obj/item/skillchip /// Set to TRUE when the skill chip's effects are applied. Set to FALSE when they're not. var/active = FALSE /// Brain that holds this skillchip. diff --git a/code/modules/loadout/loadout_items.dm b/code/modules/loadout/loadout_items.dm index defb470356c2..7f93eec9c93d 100644 --- a/code/modules/loadout/loadout_items.dm +++ b/code/modules/loadout/loadout_items.dm @@ -140,6 +140,10 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories()) allowed_configs += "[initial(item_path.greyscale_config_inhand_left)]" if(initial(item_path.greyscale_config_inhand_right)) allowed_configs += "[initial(item_path.greyscale_config_inhand_right)]" + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + if(initial(item_path.greyscale_config_onfloor)) + allowed_configs += "[initial(item_path.greyscale_config_onfloor)]" + // DARKPACK EDIT ADD END var/datum/greyscale_modify_menu/menu = new( manager, diff --git a/code/modules/lootpanel/_lootpanel.dm b/code/modules/lootpanel/_lootpanel.dm index ab13a7c21167..f7cfab8d8cd4 100644 --- a/code/modules/lootpanel/_lootpanel.dm +++ b/code/modules/lootpanel/_lootpanel.dm @@ -38,9 +38,14 @@ ui.open() +/datum/lootpanel/ui_host(mob/user) + return source_turf + + /datum/lootpanel/ui_close(mob/user) . = ..() + UnregisterSignal(source_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON)) source_turf = null reset_contents() @@ -55,13 +60,6 @@ return data -/datum/lootpanel/ui_status(mob/user, datum/ui_state/state) - if(user.incapacitated) - return UI_DISABLED - - return UI_INTERACTIVE - - /datum/lootpanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) @@ -70,7 +68,5 @@ switch(action) if("grab") return grab(usr, params) - if("refresh") - return populate_contents() return FALSE diff --git a/code/modules/lootpanel/contents.dm b/code/modules/lootpanel/contents.dm index 2fcd4201d8a7..1fa72ae1372b 100644 --- a/code/modules/lootpanel/contents.dm +++ b/code/modules/lootpanel/contents.dm @@ -21,19 +21,38 @@ if(!istype(thing)) stack_trace("Non-atom in the contents of [source_turf]!") continue - if(QDELETED(thing)) - continue - if(thing.mouse_opacity == MOUSE_OPACITY_TRANSPARENT) - continue - if(thing.IsObscured()) - continue - if(thing.invisibility > owner.mob.see_invisible) - continue - // convert - var/datum/search_object/index = new(owner, thing) - add_to_index(index) + add_new_searchable(thing, FALSE) + + queue_update() + +/datum/lootpanel/proc/on_source_turf_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) + SIGNAL_HANDLER + + // async because the same move handler we register with this can trigger on the /datum/search_object later in this event + // deleting it just as we added it + addtimer(CALLBACK(src, PROC_REF(add_new_searchable), arrived, TRUE), 0) + +/datum/lootpanel/proc/add_new_searchable(atom/movable/thing, from_signal) + if(QDELETED(thing)) + return + if(thing.mouse_opacity == MOUSE_OPACITY_TRANSPARENT) + return + if(thing.IsObscured()) + return + if(thing.invisibility > owner.mob.see_invisible) + return + if(from_signal && (!source_turf || !(thing in source_turf.contents))) + return + + // convert + var/datum/search_object/index = new(owner, thing) + add_to_index(index) + + if(from_signal) + queue_update() +/datum/lootpanel/proc/queue_update() var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src) window?.send_update() diff --git a/code/modules/lootpanel/misc.dm b/code/modules/lootpanel/misc.dm index 4fc3af2da29b..8f510cd43313 100644 --- a/code/modules/lootpanel/misc.dm +++ b/code/modules/lootpanel/misc.dm @@ -1,5 +1,10 @@ /// Helper to open the panel /datum/lootpanel/proc/open(turf/tile) + if (tile != source_turf) + if (source_turf) + UnregisterSignal(source_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON)) + RegisterSignals(tile, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON), PROC_REF(on_source_turf_entered)) + source_turf = tile #if !defined(OPENDREAM) && !defined(UNIT_TESTS) diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm index 84e74858c2a4..0674ff97cb83 100644 --- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm +++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm @@ -91,7 +91,6 @@ icon_state = "puddle-oil" capacity = 20 dispensedreagent = /datum/reagent/fuel/oil - pixel_shift = 0 /obj/structure/sink/oil_well/Initialize(mapload) . = ..() @@ -99,34 +98,30 @@ //Thankfully, the user can cast the line from a distance. AddComponent(/datum/component/fishing_spot, /datum/fish_source/oil_well) +/obj/structure/sink/oil_well/find_and_mount_on_atom(mark_for_late_init, late_init) + ///Oil wells exist indepent of any wall structure + return FALSE + /obj/structure/sink/oil_well/attack_hand(mob/user, list/modifiers) flick("puddle-oil-splash",src) reagents.expose(user, TOUCH, 20) //Covers target in 20u of oil. to_chat(user, span_notice("You touch the pool of oil, only to get oil all over yourself. It would be wise to wash this off with water.")) -/obj/structure/sink/oil_well/attackby(obj/item/O, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/structure/sink/oil_well/wrench_act(mob/living/user, obj/item/tool) + //we deconstruct with a shovel + return NONE + +/obj/structure/sink/oil_well/item_interaction(mob/living/user, obj/item/tool, list/modifiers) flick("puddle-oil-splash",src) - if(O.tool_behaviour == TOOL_SHOVEL) //attempt to deconstruct the puddle with a shovel + if(tool.tool_behaviour == TOOL_SHOVEL) //attempt to deconstruct the puddle with a shovel to_chat(user, "You fill in the oil well with soil.") - O.play_tool_sound(src) - deconstruct() - return 1 - if(is_reagent_container(O)) //Refilling bottles with oil - var/obj/item/reagent_containers/RG = O - if(RG.is_refillable()) - if(!RG.reagents.holder_full()) - RG.reagents.add_reagent(dispensedreagent, min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this)) - to_chat(user, span_notice("You fill [RG] from [src].")) - return TRUE - to_chat(user, span_notice("\The [RG] is full.")) - return FALSE - if(!user.combat_mode) - to_chat(user, span_notice("You won't have any luck getting \the [O] out if you drop it in the oil.")) - return 1 - else - return ..() + tool.play_tool_sound(src) + deconstruct(TRUE) + return ITEM_INTERACT_SUCCESS + + return ..() -/obj/structure/sink/oil_well/drop_materials() +/obj/structure/sink/oil_well/atom_deconstruct(dissambled = TRUE) new /obj/effect/decal/cleanable/blood/oil(loc) //***Grave mounds. diff --git a/code/modules/meteors/meteor_dark_matteor.dm b/code/modules/meteors/meteor_dark_matteor.dm index acf21a3c434b..051f53c412d7 100644 --- a/code/modules/meteors/meteor_dark_matteor.dm +++ b/code/modules/meteors/meteor_dark_matteor.dm @@ -14,7 +14,7 @@ /// distortion to really give you that sense of oh shit var/atom/movable/warp_effect/warp /// and another oh shit in the form of quantum sparks - var/datum/effect_system/spark_spread/quantum/spark_system + var/datum/effect_system/basic/spark_spread/quantum/spark_system /// in case we miss, we can go back to the previous security level var/previous_security_level @@ -26,8 +26,7 @@ SSsecurity_level.set_level(SEC_LEVEL_RED) warp = new(src) vis_contents += warp - spark_system = new /datum/effect_system/spark_spread/quantum() - spark_system.set_up(4, TRUE, src) + spark_system = new /datum/effect_system/basic/spark_spread/quantum(src, 4, TRUE) spark_system.attach(src) START_PROCESSING(SSobj, src) diff --git a/code/modules/mining/equipment/miningradio.dm b/code/modules/mining/equipment/miningradio.dm index 61d80bfce6fa..69da0ada1af2 100644 --- a/code/modules/mining/equipment/miningradio.dm +++ b/code/modules/mining/equipment/miningradio.dm @@ -19,5 +19,6 @@ state_normal = "weatherwarning", \ state_warning = "urgentwarning", \ state_danger = "direwarning", \ + radar_z_trait = ZTRAIT_MINING, \ ) set_frequency(FREQ_SUPPLY) diff --git a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm index 27729c240313..b76bcaba2ebe 100644 --- a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm +++ b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm @@ -47,7 +47,7 @@ /// Make a cloud which applies brimdust to everyone nearby /obj/item/organ/monster_core/brimdust_sac/on_triggered_internal() var/turf/origin_turf = get_turf(owner) - do_smoke(range = 2, holder = owner, location = origin_turf, smoke_type = /obj/effect/particle_effect/fluid/smoke/bad/brimdust) + do_smoke(2, owner, origin_turf, effect_type = /obj/effect/particle_effect/fluid/smoke/bad/brimdust) /// Smoke which applies brimdust to you, and is also bad for your lungs /obj/effect/particle_effect/fluid/smoke/bad/brimdust diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm index b4100a73cc16..636021dac1ac 100644 --- a/code/modules/mob/living/basic/basic_defense.dm +++ b/code/modules/mob/living/basic/basic_defense.dm @@ -21,8 +21,19 @@ if(HAS_TRAIT(user, TRAIT_PACIFISM)) to_chat(user, span_warning("You don't want to hurt [src]!")) return TRUE - var/obj/item/bodypart/arm/active_arm = user.get_active_hand() - var/damage = (basic_mob_flags & IMMUNE_TO_FISTS) ? 0 : rand(active_arm.unarmed_damage_low, active_arm.unarmed_damage_high) + + // DARKPACK EDIT CHANGE START - STORYTELLER_STATS + // ROLL TO HIT // DARKPACK TODO + // var/successes = SSroll.storyteller_roll(user.st_get_stat(STAT_DEXTERITY) + user.st_get_stat(STAT_BRAWL), 6, user) + // ROLL TO DAMAGE + var/damage_output + if(HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER)) + damage_output = user.st_get_stat(STAT_STRENGTH) + else + var/datum/storyteller_roll/damage/damage_roll = new() + damage_output = damage_roll.st_roll(user, src) + var/damage = (basic_mob_flags & IMMUNE_TO_FISTS) ? 0 : damage_output TTRPG_DAMAGE // DARKPACK EDIT CHANGE - STORYTELLER_STATS + // DARKPACK EDIT CHANGE END if(check_block(user, damage, "[user]'s punch", UNARMED_ATTACK, 0, BRUTE)) return user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm index 16a7f6e4c173..7462974381e0 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm @@ -43,9 +43,7 @@ Difficulty: Medium victor_memory_type = /datum/memory/megafauna_slayer crusher_loot = list(/obj/item/crusher_trophy/miner_eye, /obj/item/knife/hunting/wildhunter) - - /// Loot dropped on death in normal circumstances - var/list/regular_loot = list(/obj/item/melee/cleaving_saw, /obj/item/gun/energy/recharge/kinetic_accelerator) + regular_loot = list(/obj/item/melee/cleaving_saw, /obj/item/gun/energy/recharge/kinetic_accelerator) /// Their little saw var/obj/item/melee/cleaving_saw/miner/miner_saw @@ -71,7 +69,6 @@ Difficulty: Medium ai_controller.set_blackboard_key(BB_BDM_RANGED_ATTACK_COOLDOWN, ranged_attack_cooldown_duration) RegisterSignals(ai_controller, list(AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/basic_melee_attack), AI_CONTROLLER_BEHAVIOR_QUEUED(/datum/ai_behavior/targeted_mob_ability)), PROC_REF(handle_saw_transformation)) - AddElement(/datum/element/death_drops, string_list(regular_loot)) RegisterSignal(src, COMSIG_LIVING_DROP_LOOT, PROC_REF(death_effect)) AddComponent(/datum/component/boss_music, 'sound/music/boss/bdm_boss.ogg', COMSIG_AI_BLACKBOARD_KEY_SET(BB_BASIC_MOB_CURRENT_TARGET)) diff --git a/code/modules/mob/living/basic/boss/boss.dm b/code/modules/mob/living/basic/boss/boss.dm index bf3b4676f582..5db790bd9583 100644 --- a/code/modules/mob/living/basic/boss/boss.dm +++ b/code/modules/mob/living/basic/boss/boss.dm @@ -23,6 +23,8 @@ /// What crusher trophy/trophies this mob drops, if any /// Should be wrapped in a list for sanity when we pass it to the element. var/list/crusher_loot = null + /// Loot dropped on death in normal circumstances + var/list/regular_loot = list() /// What achievements do we give our defeater? var/list/achievements = null @@ -40,6 +42,7 @@ add_traits(list(TRAIT_NO_TELEPORT, TRAIT_MARTIAL_ARTS_IMMUNE, TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE, TRAIT_NO_FLOATING_ANIM), MEGAFAUNA_TRAIT) AddComponent(/datum/component/seethrough_mob) AddElement(/datum/element/simple_flying) + AddElement(/datum/element/death_drops, string_list(regular_loot)) handle_crusher_loot() handle_achievements() diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm index 9c334b413389..0073f472e5d3 100644 --- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm @@ -29,8 +29,6 @@ /datum/action/cooldown/mob_cooldown/bot/foam/Activate(mob/living/firer, atom/target) owner.visible_message(span_danger("[owner] whirs and bubbles violently, before releasing a plume of froth!")) - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(foam_range, holder = owner, location = owner.loc) - foam.start() + do_foam(foam_range, owner, owner.loc) StartCooldown() return TRUE diff --git a/code/modules/mob/living/basic/bots/firebot/firebot.dm b/code/modules/mob/living/basic/bots/firebot/firebot.dm index b6ef076c6293..b49aa011bbbf 100644 --- a/code/modules/mob/living/basic/bots/firebot/firebot.dm +++ b/code/modules/mob/living/basic/bots/firebot/firebot.dm @@ -134,9 +134,7 @@ /mob/living/basic/bot/firebot/atmos_expose(datum/gas_mixture/air, exposed_temperature) if(!COOLDOWN_FINISHED(src, foam_cooldown)) return - var/datum/effect_system/fluid_spread/foam/firefighting/foam = new - foam.set_up(3, holder = src, location = loc) - foam.start() + do_foam(3, src, loc, foam_type = /datum/effect_system/fluid_spread/foam/firefighting) COOLDOWN_START(src, foam_cooldown, FOAM_INTERVAL) /mob/living/basic/bot/firebot/proc/spray_water(atom/attacked_atom, list/modifiers) diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm index eb4dd4565fd2..4c8d04c63913 100644 --- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm @@ -68,9 +68,7 @@ RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack)) /mob/living/basic/bot/hygienebot/explode() - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(2, holder = src, location = loc) - foam.start() + do_foam(2, src, loc) return ..() /mob/living/basic/bot/hygienebot/generate_speak_list() diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chick.dm b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm index cc9b33d3e803..3c888145005a 100644 --- a/code/modules/mob/living/basic/farm_animals/chicken/chick.dm +++ b/code/modules/mob/living/basic/farm_animals/chicken/chick.dm @@ -34,6 +34,7 @@ var/grow_as = /mob/living/basic/chicken /datum/emote/chick + abstract_type = /datum/emote/chick mob_type_allowed_typecache = /mob/living/basic/chick mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm index 0032353dca31..533f1c1a75b3 100644 --- a/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm +++ b/code/modules/mob/living/basic/farm_animals/chicken/chicken.dm @@ -39,6 +39,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) var/fertile = TRUE /datum/emote/chicken + abstract_type = /datum/emote/chicken mob_type_allowed_typecache = /mob/living/basic/chicken mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm index 72d1d892e623..d55869f751dc 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm @@ -37,6 +37,7 @@ var/milked_reagent = /datum/reagent/consumable/milk /datum/emote/cow + abstract_type = /datum/emote/cow mob_type_allowed_typecache = /mob/living/basic/cow mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm index 674077dbc281..4452ae3100f2 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_wisdom.dm @@ -44,7 +44,7 @@ if(!stat && !user.combat_mode) to_chat(user, span_nicegreen("[src] whispers you some intense wisdoms and then disappears!")) user.mind?.adjust_experience(granted_wisdom, granted_experience) - do_smoke(1, holder = src, location = get_turf(src)) + do_smoke(1, src, get_turf(src)) qdel(src) return return ..() diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm index 063e6bfb6599..41aea3b7f72a 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm @@ -1,4 +1,5 @@ /datum/emote/gorilla + abstract_type = /datum/emote/gorilla mob_type_allowed_typecache = /mob/living/basic/gorilla mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm index 7654933c0d0f..6762c7badf7e 100644 --- a/code/modules/mob/living/basic/farm_animals/pig.dm +++ b/code/modules/mob/living/basic/farm_animals/pig.dm @@ -29,6 +29,7 @@ ai_controller = /datum/ai_controller/basic_controller/pig /datum/emote/pig + abstract_type = /datum/emote/pig mob_type_allowed_typecache = /mob/living/basic/pig mob_type_blacklist_typecache = list() @@ -39,6 +40,7 @@ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE vary = TRUE sound = SFX_PIG_OINK + /mob/living/basic/pig/Initialize(mapload) . = ..() AddElement(/datum/element/pet_bonus, "oink") diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm index 6598b70606c1..0989fabae0ff 100644 --- a/code/modules/mob/living/basic/farm_animals/pony.dm +++ b/code/modules/mob/living/basic/farm_animals/pony.dm @@ -34,6 +34,7 @@ var/list/ponycolors = list("#cc8c5d", "#cc8c5d") /datum/emote/pony + abstract_type = /datum/emote/pony mob_type_allowed_typecache = /mob/living/basic/pony mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/farm_animals/rabbit.dm b/code/modules/mob/living/basic/farm_animals/rabbit.dm index 6e1940f4a289..ca09405ca230 100644 --- a/code/modules/mob/living/basic/farm_animals/rabbit.dm +++ b/code/modules/mob/living/basic/farm_animals/rabbit.dm @@ -40,10 +40,11 @@ var/icon_prefix = "rabbit" /datum/emote/rabbit + abstract_type = /datum/emote/rabbit mob_type_allowed_typecache = /mob/living/basic/rabbit mob_type_blacklist_typecache = list() -/datum/emote/rabbit +/datum/emote/rabbit/hop key = "hop" key_third_person = "hops" message = "hops around happily!" diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm index 7a5cdef49eaa..c2c597dfd31b 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm @@ -96,7 +96,7 @@ . += span_info("Someone appears to have attached a saddle to this one.") // Goliaths can summon tentacles more frequently as they take damage, scary. -/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item) +/mob/living/basic/mining/goliath/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing) . = ..() if (. <= 0) return diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm index 640cdc103388..5c5107162cfb 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm @@ -171,6 +171,7 @@ var/was_tamed = FALSE /datum/emote/lobstrosity_juvenile + abstract_type = /datum/emote/lobstrosity_juvenile mob_type_allowed_typecache = /mob/living/basic/mining/lobstrosity/juvenile mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/minebots/minebot_abilities.dm b/code/modules/mob/living/basic/minebots/minebot_abilities.dm index 95948b328f78..58ee70181c77 100644 --- a/code/modules/mob/living/basic/minebots/minebot_abilities.dm +++ b/code/modules/mob/living/basic/minebots/minebot_abilities.dm @@ -153,9 +153,7 @@ /obj/effect/temp_visual/falling_rocket/proc/create_explosion() playsound(src, 'sound/items/weapons/minebot_rocket.ogg', 100, FALSE) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(1, holder = src) - smoke.start() + do_smoke(1, src, loc) for(var/mob/living/living_target in oview(explosion_radius, src)) if(living_target.incorporeal_move) continue @@ -171,9 +169,7 @@ /obj/effect/mine/minebot/mineEffect(mob/living/victim) if(!istype(victim)) return - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src) - smoke.start() + do_smoke(0, src, loc) playsound(src, 'sound/effects/explosion/explosion3.ogg', 100) victim.apply_damage(damage_to_apply) diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm index aea56dbf8788..560d61af23c2 100644 --- a/code/modules/mob/living/basic/pets/cat/cat.dm +++ b/code/modules/mob/living/basic/pets/cat/cat.dm @@ -69,6 +69,7 @@ /datum/emote/cat + abstract_type = /datum/emote/cat mob_type_allowed_typecache = /mob/living/basic/pet/cat mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm index 2a92ee494ecd..b9cdb078cfac 100644 --- a/code/modules/mob/living/basic/pets/dog/_dog.dm +++ b/code/modules/mob/living/basic/pets/dog/_dog.dm @@ -92,6 +92,7 @@ // DARKPACK EDIT ADD END /datum/emote/dog + abstract_type = /datum/emote/dog mob_type_allowed_typecache = /mob/living/basic/pet/dog mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/pets/fox.dm b/code/modules/mob/living/basic/pets/fox.dm index 88dd9d5342b3..16b3b6662175 100644 --- a/code/modules/mob/living/basic/pets/fox.dm +++ b/code/modules/mob/living/basic/pets/fox.dm @@ -39,6 +39,7 @@ ) /datum/emote/fox + abstract_type = /datum/emote/fox mob_type_allowed_typecache = /mob/living/basic/pet/fox mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/pets/penguin/penguin.dm b/code/modules/mob/living/basic/pets/penguin/penguin.dm index 9e0be0681619..beb9fab31db7 100644 --- a/code/modules/mob/living/basic/pets/penguin/penguin.dm +++ b/code/modules/mob/living/basic/pets/penguin/penguin.dm @@ -20,6 +20,7 @@ var/obj/carried_egg /datum/emote/penguin + abstract_type = /datum/emote/penguin mob_type_allowed_typecache = /mob/living/basic/pet/penguin mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/pets/sloth.dm b/code/modules/mob/living/basic/pets/sloth.dm index fac460da7323..a6d6b4f6a479 100644 --- a/code/modules/mob/living/basic/pets/sloth.dm +++ b/code/modules/mob/living/basic/pets/sloth.dm @@ -38,6 +38,7 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth) ai_controller = /datum/ai_controller/basic_controller/sloth /datum/emote/sloth + abstract_type = /datum/emote/sloth mob_type_allowed_typecache = /mob/living/basic/sloth mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/slime/emote.dm b/code/modules/mob/living/basic/slime/emote.dm index 617d33b9809c..ca72f726d656 100644 --- a/code/modules/mob/living/basic/slime/emote.dm +++ b/code/modules/mob/living/basic/slime/emote.dm @@ -1,4 +1,5 @@ /datum/emote/slime + abstract_type = /datum/emote/slime mob_type_allowed_typecache = /mob/living/basic/slime mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/slime/slime.dm b/code/modules/mob/living/basic/slime/slime.dm index dd6c2bd112bc..91a508b87cd8 100644 --- a/code/modules/mob/living/basic/slime/slime.dm +++ b/code/modules/mob/living/basic/slime/slime.dm @@ -157,7 +157,7 @@ /mob/living/basic/slime/random /mob/living/basic/slime/random/Initialize(mapload, new_colour, new_life_stage) - return ..(mapload, null, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY) + return ..(mapload, SLIME_TYPE_RANDOM, prob(50) ? SLIME_LIFE_STAGE_ADULT : SLIME_LIFE_STAGE_BABY) ///Friendly docile subtype /mob/living/basic/slime/pet @@ -197,10 +197,11 @@ if(slime_type.transparent) alpha = SLIME_TRANSPARENCY_ALPHA + icon_living = "[slime_type.colour]-[life_stage]" icon_dead = !cores ? "[slime_type.colour]-cut" : "[slime_type.colour]-[life_stage]-dead" if(stat != DEAD) - icon_state = "[slime_type.colour]-[life_stage]" + icon_state = icon_living if(current_mood && current_mood != SLIME_MOOD_NONE && !stat) add_overlay("aslime-[current_mood]") else @@ -273,8 +274,8 @@ /// Sets the slime's type, name and its icons. /// If not provided with a type it will instead be random -/mob/living/basic/slime/proc/set_slime_type(new_type = null) - if(isnull(new_type)) +/mob/living/basic/slime/proc/set_slime_type(new_type = SLIME_TYPE_RANDOM) + if(new_type == SLIME_TYPE_RANDOM) new_type = pick(subtypesof(/datum/slime_type)) slime_type = possible_slime_types[new_type] diff --git a/code/modules/mob/living/basic/space_fauna/ant.dm b/code/modules/mob/living/basic/space_fauna/ant.dm index 60b91c5dcc21..5dc63c95d5a1 100644 --- a/code/modules/mob/living/basic/space_fauna/ant.dm +++ b/code/modules/mob/living/basic/space_fauna/ant.dm @@ -37,6 +37,7 @@ ai_controller = /datum/ai_controller/basic_controller/ant /datum/emote/ant + abstract_type = /datum/emote/ant mob_type_allowed_typecache = /mob/living/basic/ant mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index 1bfb0071fa35..ea679a382350 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -84,6 +84,7 @@ )) /datum/emote/carp + abstract_type = /datum/emote/carp mob_type_allowed_typecache = /mob/living/basic/carp mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index c484a8b977c1..e04649353ffc 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -119,9 +119,7 @@ continue light.visible_message(span_boldwarning("[light] suddenly flares brightly and begins to spark!")) - var/datum/effect_system/spark_spread/light_sparks = new /datum/effect_system/spark_spread() - light_sparks.set_up(4, 0, light) - light_sparks.start() + do_sparks(4, FALSE, light) new /obj/effect/temp_visual/revenant(get_turf(light)) addtimer(CALLBACK(src, PROC_REF(overload_shock), light, caster), 2 SECONDS) diff --git a/code/modules/mob/living/basic/space_fauna/roro.dm b/code/modules/mob/living/basic/space_fauna/roro.dm index 5cb306d62927..b9c4c9e759c2 100644 --- a/code/modules/mob/living/basic/space_fauna/roro.dm +++ b/code/modules/mob/living/basic/space_fauna/roro.dm @@ -37,6 +37,7 @@ ai_controller = /datum/ai_controller/basic_controller/simple/simple_retaliate /datum/emote/roro + abstract_type = /datum/emote/roro mob_type_allowed_typecache = /mob/living/basic/roro mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/space_fauna/spider/spider.dm b/code/modules/mob/living/basic/space_fauna/spider/spider.dm index 9d3df866c551..8cf805697669 100644 --- a/code/modules/mob/living/basic/space_fauna/spider/spider.dm +++ b/code/modules/mob/living/basic/space_fauna/spider/spider.dm @@ -55,6 +55,7 @@ var/apply_spider_antag = TRUE /datum/emote/spider + abstract_type = /datum/emote/spider mob_type_allowed_typecache = /mob/living/basic/spider mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/vermin/lizard.dm b/code/modules/mob/living/basic/vermin/lizard.dm index 3d2d529697f6..3c5852699013 100644 --- a/code/modules/mob/living/basic/vermin/lizard.dm +++ b/code/modules/mob/living/basic/vermin/lizard.dm @@ -47,6 +47,7 @@ )) /datum/emote/lizard + abstract_type = /datum/emote/lizard mob_type_allowed_typecache = /mob/living/basic/lizard mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm index 75508c3e5427..94acd65a3dac 100644 --- a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm +++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm @@ -42,6 +42,7 @@ ) /datum/emote/mothroach + abstract_type = /datum/emote/mothroach mob_type_allowed_typecache = /mob/living/basic/mothroach mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm index 05efcfc5cef3..698eb30c752c 100644 --- a/code/modules/mob/living/basic/vermin/mouse.dm +++ b/code/modules/mob/living/basic/vermin/mouse.dm @@ -45,6 +45,7 @@ ) /datum/emote/mouse + abstract_type = /datum/emote/mouse mob_type_allowed_typecache = /mob/living/basic/mouse mob_type_blacklist_typecache = list() diff --git a/code/modules/mob/living/blood_types.dm b/code/modules/mob/living/blood_types.dm index f9d84a4cc9d8..7335bd2204f1 100644 --- a/code/modules/mob/living/blood_types.dm +++ b/code/modules/mob/living/blood_types.dm @@ -349,6 +349,7 @@ // Ensures that lighter slimefolk look half-decent when wounded and bleeding /datum/blood_type/slime/get_wound_color(mob/living/carbon/victim) return victim.dna?.features?[FEATURE_MUTANT_COLOR] || get_color() + /datum/blood_type/slime/get_damage_color(mob/living/carbon/victim) return victim.dna?.features?[FEATURE_MUTANT_COLOR] || get_color() diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 4864a5fb05eb..c4c9193d7de7 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -506,6 +506,7 @@ TRAIT_EXPERT_FISHER, // live off land, fish from river TRAIT_ROUGHRIDER, // ride beast, chase down prey, flee from danger TRAIT_BEAST_EMPATHY, // know the way of beast, calm with food + TRAIT_NECROPOLIS_WORSHIP, TRAIT_TACKLING_TAILED_DEFENDER, ) diff --git a/code/modules/mob/living/brain/emote.dm b/code/modules/mob/living/brain/emote.dm index c93e3acf4b24..443b578f3257 100644 --- a/code/modules/mob/living/brain/emote.dm +++ b/code/modules/mob/living/brain/emote.dm @@ -1,4 +1,5 @@ /datum/emote/brain + abstract_type = /datum/emote/brain mob_type_allowed_typecache = list(/mob/living/brain) mob_type_blacklist_typecache = list() emote_type = EMOTE_AUDIBLE diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm index 774a69ee5042..0bd641ff3635 100644 --- a/code/modules/mob/living/carbon/alien/emote.dm +++ b/code/modules/mob/living/carbon/alien/emote.dm @@ -1,4 +1,5 @@ /datum/emote/living/alien + abstract_type = /datum/emote/living/alien mob_type_allowed_typecache = list(/mob/living/carbon/alien) /datum/emote/living/alien/gnarl diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index e803ba657c91..501068d9defe 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -70,8 +70,8 @@ var/victim_stamathletics = victim_stamina + victim.st_get_stat(STAT_ATHLETICS) var/victim_keephigher = max(victim_stambrawl, victim_stamathletics) - var/attacker_roll = SSroll.storyteller_roll(dice = attacker_keephigher, difficulty = 6, numerical = TRUE) - var/victim_roll = SSroll.storyteller_roll(dice = victim_keephigher, difficulty = 6, mobs_to_show_output = list(victim), alert_atom = victim, numerical = TRUE) + var/attacker_roll = SSroll.storyteller_roll(dice = attacker_keephigher, difficulty = 6, roller = thrower, numerical = TRUE) + var/victim_roll = SSroll.storyteller_roll(dice = victim_keephigher, difficulty = 6, roller = victim, numerical = TRUE) if(victim_roll > attacker_roll) blocked = TRUE @@ -1360,3 +1360,18 @@ if(!CAN_HAVE_BLOOD(src)) return return dna?.blood_type + +/mob/living/carbon/update_nutrition() + . = ..() + // Force a weight update in case we're stasis'd and don't tick + if (HAS_TRAIT_FROM(src, TRAIT_FAT, OBESITY)) + if (overeatduration >= 200 SECONDS) + return + + to_chat(src, span_notice("You feel fit again!")) + remove_traits(list(TRAIT_FAT, TRAIT_OFF_BALANCE_TACKLER), OBESITY) + return + + if (overeatduration >= 200 SECONDS) + to_chat(src, span_danger("You suddenly feel blubbery!")) + add_traits(list(TRAIT_FAT, TRAIT_OFF_BALANCE_TACKLER), OBESITY) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index 5dac58d31233..5dc3cbd7d705 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -30,9 +30,7 @@ /mob/living/carbon/get_ear_protection(ignore_deafness = FALSE) var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) - if(!ears) - return INFINITY - return ..() + ears.bang_protect + return ..() + ears?.bang_protect /mob/living/carbon/is_mouth_covered(check_flags = ALL) if((check_flags & ITEM_SLOT_HEAD) && head && (head.flags_cover & HEADCOVERSMOUTH)) @@ -535,15 +533,6 @@ if(hit_clothes) hit_clothes.take_damage(damage_amount, damage_type, damage_flag, 0) -/mob/living/carbon/can_hear() - . = FALSE - var/obj/item/organ/ears/ears = get_organ_slot(ORGAN_SLOT_EARS) - if(ears && !HAS_TRAIT(src, TRAIT_DEAF)) - . = TRUE - if(health <= hardcrit_threshold && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)) - . = FALSE - - /mob/living/carbon/adjust_oxy_loss(amount, updating_health = TRUE, forced, required_biotype, required_respiration_type) if(!forced && HAS_TRAIT(src, TRAIT_NOBREATH)) amount = min(amount, 0) //Prevents oxy damage but not healing diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 19d5039de929..2a39bb6ab162 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -295,7 +295,6 @@ continue if(isnull(damage_overlay) && (iter_part.brutestate || iter_part.burnstate)) damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "blank", -DAMAGE_LAYER, appearance_flags = KEEP_TOGETHER) - damage_overlay.color = iter_part.damage_overlay_color if(iter_part.brutestate) var/mutable_appearance/blood_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[iter_part.dmg_overlay_type]_[iter_part.body_zone]_[iter_part.brutestate]0", appearance_flags = RESET_COLOR) //we're adding icon_states of the base image as overlays blood_damage_overlay.color = get_bloodtype()?.get_damage_color(src) @@ -548,6 +547,11 @@ . += "[husk_type]" . += "-husk" . += "-[body_zone]" + var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list() + if (LAZYLEN(blood_dna)) + . += "-[get_color_from_blood_list(blood_dna)]" + else + . += "-[BLOOD_COLOR_RED]" if(ishuman(owner)) var/mob/living/carbon/human/human_owner = owner . += "-[human_owner.mob_height]" diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm index b226427dfb55..b1f790814140 100644 --- a/code/modules/mob/living/carbon/emote.dm +++ b/code/modules/mob/living/carbon/emote.dm @@ -1,4 +1,5 @@ /datum/emote/living/carbon + abstract_type = /datum/emote/living/carbon mob_type_allowed_typecache = list(/mob/living/carbon) /datum/emote/living/carbon/airguitar diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm index 10c45dbdc39b..4d79a1b841e4 100644 --- a/code/modules/mob/living/carbon/examine.dm +++ b/code/modules/mob/living/carbon/examine.dm @@ -185,7 +185,7 @@ if(-INFINITY to BLOOD_VOLUME_BAD) . += span_deadsay("[t_He] resemble[p_s()] a crushed, empty juice pouch.") - . += display_darkpack_examine_text() // DARKPACK EDIT ADD + . += display_darkpack_examine_text(user) // DARKPACK EDIT ADD if(is_bleeding()) var/list/obj/item/bodypart/bleeding_limbs = list() diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm index 914d894b6446..4c2a0e8ab5ac 100644 --- a/code/modules/mob/living/carbon/human/_species.dm +++ b/code/modules/mob/living/carbon/human/_species.dm @@ -814,24 +814,20 @@ GLOBAL_LIST_EMPTY(features_by_species) //Someone in a grapple is much more vulnerable to being harmed by punches. var/grappled = (target.pulledby && target.pulledby.grab_state >= GRAB_AGGRESSIVE) - // Our lower and upper unarmed damage values. Damage is rolled between these two values. - var/lower_unarmed_damage = attacking_bodypart.unarmed_damage_low - var/upper_unarmed_damage = attacking_bodypart.unarmed_damage_high - - // The presence of TRAIT_STRENGTH increases our upper unarmed damage. This is a damage cap increase. - upper_unarmed_damage += HAS_TRAIT(user, TRAIT_STRENGTH) ? 2 : 0 - - // DARKPACK EDIT ADD - Storyteller Stats - var/damage_multiplier = 1 + ((user.st_get_stat(STAT_STRENGTH) - 2) / 5) - upper_unarmed_damage *= damage_multiplier - // DARKPACK EDIT ADD - Storyteller Stats - - // Out athletics skill is used to set our potential base damage roll. It won't increase our potential damage roll, but will make our unarmed attack more consistent. - // For a normal human arm, this would cap at 10, and for a normal human leg, this would go up to 14. - lower_unarmed_damage = min(lower_unarmed_damage + user.st_get_stat(STAT_BRAWL), upper_unarmed_damage) // DARKPACK EDIT CHANGE - STORYTELLER_STATS + // DARKPACK EDIT CHANGE START - STORYTELLER_STATS + // ROLL TO HIT // DARKPACK TODO + // var/successes = SSroll.storyteller_roll(user.st_get_stat(STAT_DEXTERITY) + user.st_get_stat(STAT_BRAWL), 6, list(user), user) + // ROLL TO DAMAGE + var/damage_output + if(HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER)) + damage_output = user.st_get_stat(STAT_STRENGTH) + else + var/datum/storyteller_roll/damage/damage_roll = new() + damage_output = damage_roll.st_roll(user, target) + // DARKPACK EDIT CHANGE END // The actual damage roll. May still be augmented by further factors. - var/damage = rand(lower_unarmed_damage, upper_unarmed_damage) + var/damage = damage_output TTRPG_DAMAGE // DARKPACK EDIT CHANGE - STORYTELLER_STATS // Limb accuracy is used to determine miss probabilities (higher the value, the less likely you are to miss), armor penetration (if entitled) and the possible result from a stagger combo hit. var/limb_accuracy = attacking_bodypart.unarmed_effectiveness // Limb sharpness determines the type of wounds this unarmed strike could possibly roll. By default, most limbs are blunt and have no sharpness. @@ -873,7 +869,7 @@ GLOBAL_LIST_EMPTY(features_by_species) var/obj/item/bodypart/affecting = target.get_bodypart(hit_zone) var/miss_chance = 100//calculate the odds that a punch misses entirely. considers stamina and brute damage of the puncher. punches miss by default to prevent weird cases - if(lower_unarmed_damage) + if(damage) // DARKPACK EDIT CHANGE - STORYTELLER_STATS if((target.body_position == LYING_DOWN) || HAS_TRAIT(user, TRAIT_PERFECT_ATTACKER) || staggered || user_drunkenness && HAS_TRAIT(user, TRAIT_DRUNKEN_BRAWLER)) //kicks and attacks against staggered targets never miss (provided your species deals more than 0 damage). Drunken brawlers while drunk also don't miss miss_chance = 0 else @@ -945,12 +941,12 @@ GLOBAL_LIST_EMPTY(features_by_species) SEND_SIGNAL(target, COMSIG_HUMAN_GOT_PUNCHED, user, damage, attack_type, affecting, final_armor_block, kicking, limb_sharpness) SEND_SIGNAL(user, COMSIG_HUMAN_PUNCHED, target, damage, attack_type, affecting, final_armor_block, kicking, limb_sharpness) - // DARKPACK EDIT ADD - Knockdown chance system from old harm proc + // DARKPACK EDIT ADD START - (Knockdown chance system from old harm proc) if((target.stat != DEAD) && (!target.IsKnockdown())) var/roll = SSroll.storyteller_roll( dice = user.st_get_stat(STAT_STRENGTH), difficulty = target.st_get_stat(STAT_DEXTERITY), - mobs_to_show_output = list(target, user)) + roller = user) if(roll == ROLL_SUCCESS) target.visible_message(span_danger("[user] knocks [target] down!"), \ @@ -959,7 +955,7 @@ GLOBAL_LIST_EMPTY(features_by_species) to_chat(user, span_danger("You knock [target] down!")) target.apply_effect(2 SECONDS, EFFECT_KNOCKDOWN, armor_block) log_combat(user, target, "got a stun punch with their previous punch") - // DARKPACK EDIT END + // DARKPACK EDIT ADD END // If our target is staggered and has sustained enough damage, we can apply a randomly determined status effect to inflict when we punch them. // The effects are based on the punching effectiveness of our attacker. Some effects are not reachable by the average human, and require augmentation to reach or being a species with a heavy punch effectiveness. diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index ba947e114e1c..7dc7906b2f9e 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -53,8 +53,10 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) return readout /mob/living/carbon/human/proc/makeSkeleton() - ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC) set_species(/datum/species/skeleton) + var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD) + if(head) + ADD_TRAIT(head, TRAIT_DISFIGURED, INNATE_TRAIT) return TRUE /mob/living/carbon/proc/Drain() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 61a9166a36bb..e8069416015a 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -55,6 +55,8 @@ /mob/living/carbon/human/proc/setup_organless_effects() // All start without eyes, and get them via set species become_blind(NO_EYES) + // And no ears, and get them via set species + ADD_TRAIT(src, TRAIT_DEAF, NO_EARS) // Mobs cannot taste anything without a tongue; the tongue organ removes this on Insert ADD_TRAIT(src, TRAIT_AGEUSIA, NO_TONGUE_TRAIT) @@ -64,7 +66,6 @@ /mob/living/carbon/human/Destroy() QDEL_NULL(physiology) GLOB.human_list -= src - GLOB.kindred_list -= src // DARKPACK EDIT ADD if (mob_mood) QDEL_NULL(mob_mood) @@ -801,10 +802,8 @@ if(!check_rights(R_SPAWN)) return var/list/options = list("Clear"="Clear") - for(var/type in subtypesof(/datum/quirk)) + for(var/type in valid_subtypesof(/datum/quirk)) var/datum/quirk/quirk_type = type - if(initial(quirk_type.abstract_parent_type) == type) - continue var/qname = initial(quirk_type.name) options[has_quirk(quirk_type) ? "[qname] (Remove)" : "[qname] (Add)"] = quirk_type var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in sort_list(options) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 53e72908af09..fedc4d6e58cf 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -508,7 +508,7 @@ emote("scream") set_facial_hairstyle("Shaved", update = FALSE) set_hairstyle("Bald") //This calls update_body_parts() - ADD_TRAIT(src, TRAIT_DISFIGURED, TRAIT_GENERIC) + ADD_TRAIT(affecting, TRAIT_DISFIGURED, TRAIT_GENERIC) apply_damage(acidity * damage_mod, BRUTE, affecting) apply_damage(acidity * damage_mod * 2, BURN, affecting) diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index f2721606a863..a5a3f700fac6 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -50,11 +50,11 @@ return signal_face // no need to null-check, because force_set will always set a signal_face var/face_name = isnull(signal_face) ? get_face_name() : signal_face // DARKPACK EDIT, ORIGINAL: var/face_name = isnull(signal_face) ? get_face_name("") : signal_face - var/id_name = isnull(signal_id) ? get_id_name("", honorifics = TRUE) : signal_id + var/id_name = isnull(signal_id) ? get_id_name("", honorifics = add_id_name) : signal_id // We need to account for real name if(force_real_name) - var/disguse_name = get_visible_name(add_id_name = TRUE, force_real_name = FALSE) + var/disguse_name = get_visible_name(add_id_name = add_id_name, force_real_name = FALSE) return "[real_name][disguse_name == real_name ? "" : " (as [disguse_name])"]" // We're just some unknown guy @@ -97,7 +97,7 @@ if(obscured_slots & HIDEFACE) return TRUE var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD) - if(isnull(head) || HAS_TRAIT(src, TRAIT_DISFIGURED) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible + if(isnull(head) || HAS_TRAIT(head, TRAIT_DISFIGURED) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible return TRUE return FALSE diff --git a/code/modules/mob/living/carbon/human/init_signals.dm b/code/modules/mob/living/carbon/human/init_signals.dm index de58a63183a7..923ec33f2128 100644 --- a/code/modules/mob/living/carbon/human/init_signals.dm +++ b/code/modules/mob/living/carbon/human/init_signals.dm @@ -14,7 +14,6 @@ RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_HUSK), SIGNAL_REMOVETRAIT(TRAIT_HUSK)), PROC_REF(refresh_obscured)) RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_INVISIBLE_MAN), SIGNAL_REMOVETRAIT(TRAIT_INVISIBLE_MAN)), PROC_REF(invisible_man_toggle)) - RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DISFIGURED), SIGNAL_REMOVETRAIT(TRAIT_DISFIGURED)), PROC_REF(update_visible_name)) //DARKPACK EDIT ADD START - POWERS - (Obfuscate Discipline) RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_OBFUSCATED), PROC_REF(make_invisible)) RegisterSignal(src, SIGNAL_REMOVETRAIT(TRAIT_OBFUSCATED), PROC_REF(make_visible)) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 4714ba39cb12..e32b3b53619e 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -47,6 +47,10 @@ handle_liver(seconds_per_tick) // For special species interactions dna.species.spec_life(src, seconds_per_tick) + // DARKPACK EDIT ADD START - SPLATS + for(var/datum/splat/splat in splats) + splat.splat_life(seconds_per_tick) + // DARKPACK EDIT ADD END return stat != DEAD /mob/living/carbon/human/calculate_affecting_pressure(pressure) diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index bd7fcd60bd18..2c9c1127edf2 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -267,7 +267,6 @@ if (!istype(detached_head)) return // It's so over detached_head.real_name = headless.real_name - detached_head.name = headless.real_name name = headless.real_name detached_head.voice = headless.voice detached_head.pitch = pitch diff --git a/code/modules/mob/living/carbon/human/species_types/humans.dm b/code/modules/mob/living/carbon/human/species_types/humans.dm index c34dba20112f..c87c52b52357 100644 --- a/code/modules/mob/living/carbon/human/species_types/humans.dm +++ b/code/modules/mob/living/carbon/human/species_types/humans.dm @@ -9,12 +9,14 @@ payday_modifier = 1.1 /datum/species/human/prepare_human_for_preview(mob/living/carbon/human/human) -// human.set_haircolor("#bb9966", update = FALSE) // brown // DARKPACK EDIT START + // DARKPACK EDIT CHANGE START + // human.set_haircolor("#bb9966", update = FALSE) // brown human.set_hairstyle("Bald", update = TRUE) human.set_facial_hairstyle("Beard (Goatee)", update = TRUE) human.set_facial_haircolor("#7E3F00") human.undershirt = "T-Shirt (Yellow)" - human.update_body() // DARKPACK EDIT END + human.update_body() + // DARKPACK EDIT CHANGE END /datum/species/human/get_scream_sound(mob/living/carbon/human/human) if(human.physique == MALE) diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index a46acd8c5201..da1cf59de617 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -141,3 +141,7 @@ SIGNAL_HANDLER cure_trauma_type(/datum/brain_trauma/severe/split_personality, TRAUMA_LIMIT_ABSOLUTE) + +/mob/living/carbon/on_hearing_loss(datum/source) + . = ..() + breathing_loop.stop() diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index e933de8ff40d..1e2c1942651c 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -26,16 +26,14 @@ if(.) //not dead handle_blood(seconds_per_tick) - if(stat != DEAD) + if(stat != DEAD) // still not dead (blood could have changed that) + for(var/key in mind?.addiction_points) + SSaddiction.all_addictions[key].process_addiction(src, seconds_per_tick) handle_brain_damage(seconds_per_tick) if(stat != DEAD) handle_bodyparts(seconds_per_tick) - if(. && mind) //. == not dead - for(var/key in mind.addiction_points) - var/datum/addiction/addiction = SSaddiction.all_addictions[key] - addiction.process_addiction(src, seconds_per_tick) if(stat != DEAD) return TRUE @@ -121,19 +119,13 @@ loc_as_obj.handle_internal_lifeform(src,0) if(check_breath(breath) && is_on_internals) - try_breathing_sound(breath) + // successful breath from internals, try to play the breathing sound + if(!HAS_TRAIT(src, TRAIT_DEAF) && client?.prefs?.read_preference(/datum/preference/toggle/sound_breathing)) + breathing_loop.start() if(breath) loc.assume_air(breath) -//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 && canon_client?.mob.can_hear()) - breathing_loop.start() - else if((!should_be_on && breathing_loop.timer_id) || !canon_client?.mob.can_hear()) - breathing_loop.stop() - /mob/living/carbon/proc/has_smoke_protection() if(HAS_TRAIT(src, TRAIT_NOBREATH)) return TRUE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 3b85f8554b2e..61850f26f10a 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -144,23 +144,23 @@ * Simply a wrapper for calling mob adjustXLoss() procs to heal a certain damage type, * when you don't know what damage type you're healing exactly. */ -/mob/living/proc/heal_damage_type(heal_amount = 0, damagetype = BRUTE) +/mob/living/proc/heal_damage_type(heal_amount = 0, damagetype = BRUTE, update_health = TRUE) heal_amount = abs(heal_amount) * -1 switch(damagetype) if(BRUTE) - return adjust_brute_loss(heal_amount) + return adjust_brute_loss(heal_amount, update_health) if(BURN) - return adjust_fire_loss(heal_amount) + return adjust_fire_loss(heal_amount, update_health) if(TOX) - return adjust_tox_loss(heal_amount) + return adjust_tox_loss(heal_amount, update_health) if(OXY) - return adjust_oxy_loss(heal_amount) + return adjust_oxy_loss(heal_amount, update_health) if(STAMINA) - return adjust_stamina_loss(heal_amount) + return adjust_stamina_loss(heal_amount, update_health) // DARKPACK EDIT ADD START - AGGRAVATED_DAMAGE if(AGGRAVATED) - return adjust_agg_loss(heal_amount) + return adjust_agg_loss(heal_amount, update_health) // DARPACK EDIT ADD END /// return the damage amount for the type given @@ -569,12 +569,14 @@ updatehealth() ///heal up to amount damage, in a given order -/mob/living/proc/heal_ordered_damage(amount, list/damage_types) +/mob/living/proc/heal_ordered_damage(amount, list/damage_types, update_health = TRUE) . = 0 //we'll return the amount of damage healed for(var/damagetype in damage_types) var/amount_to_heal = min(abs(amount), get_current_damage_of_type(damagetype)) //heal only up to the amount of damage we have if(amount_to_heal) - . += heal_damage_type(amount_to_heal, damagetype) + . += heal_damage_type(amount_to_heal, damagetype, FALSE) amount -= amount_to_heal //remove what we healed from our current amount if(!amount) break + if(. && update_health) + updatehealth() diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index e87b425acd7c..80189ae49024 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -180,9 +180,13 @@ return for(var/mob/living/nearby in viewers(src)) - if(nearby.stat >= UNCONSCIOUS || nearby.is_blind()) + if(nearby == src || nearby.stat >= UNCONSCIOUS || nearby.is_blind()) continue nearby.add_mood_event("saw_death", /datum/mood_event/conditional/see_death, src, dusted, gibbed) + nearby.mind?.witnessed_death(src) + + if(!gibbed && !dusted) + mind?.experienced_death() /mob/living/silicon/send_death_moodlets(dusted = FALSE, gibbed = FALSE) return // You are a machine (Future todo, roboticists feel sad though) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index a5b546f9cdc1..bef9780b390e 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -1,6 +1,7 @@ /* EMOTE DATUMS */ /datum/emote/living + abstract_type = /datum/emote/living mob_type_allowed_typecache = /mob/living mob_type_blacklist_typecache = list(/mob/living/brain) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a46f8ec02f71..cfb84489b996 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -754,7 +754,7 @@ add_traits(list(TRAIT_UI_BLOCKED, TRAIT_PULL_BLOCKED, TRAIT_UNDENSE), LYING_DOWN_TRAIT) if(HAS_TRAIT(src, TRAIT_FLOORED) && !(dir & (NORTH|SOUTH))) setDir(pick(NORTH, SOUTH)) // We are and look helpless. - if(rotate_on_lying) + if(rotate_on_lying && !HAS_TRAIT(src, TRAIT_NO_LYING_ANGLE)) // DARKPACK EDIT CHANGE - WEREWOLF add_offsets(LYING_DOWN_TRAIT, y_add = PIXEL_Y_OFFSET_LYING) /// Proc to append behavior related to lying down. @@ -1232,19 +1232,15 @@ //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) // Grabber is the "action taker" so he is the "owner" - var/success = ROLL_SUCCESS - if(pulledby && isliving(pulledby)) - var/mob/living/living_puller = pulledby - success = SSroll.opposed_roll( - player_a = living_puller, - player_b = src, - dice_a = living_puller.st_get_stat(STAT_STRENGTH)+living_puller.st_get_stat(STAT_BRAWL), - dice_b = st_get_stat(STAT_DEXTERITY)+st_get_stat(STAT_BRAWL), - show_player_a = TRUE, - show_player_b = TRUE, - alert_atom = src, - draw_goes_to_b = TRUE - ) + var/success = ROLL_FAILURE + if(isliving(pulledby)) + var/datum/storyteller_roll/grappling/pulled_roll = new() + var/puller_result = pulled_roll.st_roll(pulledby, src) + var/datum/storyteller_roll/grappled/our_roll = new() + var/our_result = our_roll.st_roll(src, pulledby) + + if(puller_result > our_result) + success = ROLL_SUCCESS if(!success) visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \ @@ -1259,7 +1255,7 @@ 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!")) if(moving_resist && client) //we resisted by trying to move - client.move_delay = world.time + 4 SECONDS + client.move_delay = world.time + 1 TURNS else pulledby.stop_pulling() return FALSE @@ -1372,6 +1368,10 @@ return /mob/living/can_hold_items(obj/item/I) + // DARKPACK EDIT ADD START + if(I && (I.w_class <= WEIGHT_CLASS_SMALL) && HAS_TRAIT(src, TRAIT_SMALL_HANDS)) + return FALSE + // DARKPACK EDIT ADD END return ..() && HAS_TRAIT(src, TRAIT_CAN_HOLD_ITEMS) && usable_hands /mob/living/can_perform_action(atom/target, action_bitflags) @@ -2452,7 +2452,9 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(HARD_CRIT) if(stat != UNCONSCIOUS) cure_blind(UNCONSCIOUS_TRAIT) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) if(DEAD) + REMOVE_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) remove_from_dead_mob_list() add_to_alive_mob_list() switch(stat) //Current stat. @@ -2480,17 +2482,14 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(. != UNCONSCIOUS) become_blind(UNCONSCIOUS_TRAIT) ADD_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) log_combat(src, src, "entered hard crit") if(DEAD) REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + ADD_TRAIT(src, TRAIT_DEAF, STAT_TRAIT) remove_from_alive_mob_list() add_to_dead_mob_list() log_combat(src, src, "died") - if(!can_hear()) - stop_sound_channel(CHANNEL_AMBIENCE) - refresh_looping_ambience() - - ///Reports the event of the change in value of the buckled variable. /mob/living/proc/set_buckled(new_buckled) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index bafaeafe7d49..68bcf1fee298 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -142,7 +142,7 @@ apply_projectile_effects(proj, def_zone, blocked) /mob/living/proc/apply_projectile_effects(obj/projectile/proj, def_zone, armor_check) - apply_damage( + var/damage_dealt = apply_damage( damage = proj.damage, damagetype = proj.damage_type, def_zone = def_zone, @@ -154,6 +154,10 @@ attacking_item = proj, ) + if(proj.damage_type == BRUTE && damage_dealt >= 10 && proj.speed >= 1 && prob(0.1)) + var/obj/item/organ/brain/a_brain = locate() in get_bodypart(def_zone) + a_brain?.cure_trauma_type(resilience = TRAUMA_RESILIENCE_LOBOTOMY) + apply_effects( stun = proj.stun, knockdown = proj.knockdown, @@ -175,6 +179,10 @@ if (proj.damage && armor_check < 100) create_projectile_hit_effects(proj, def_zone, armor_check) + if(proj.fired_from) + SEND_SIGNAL(proj.fired_from, COMSIG_PROJECTILE_POST_HIT_LIVING, src, def_zone, armor_check) + SEND_SIGNAL(proj, COMSIG_PROJECTILE_SELF_POST_HIT_LIVING, src, def_zone, armor_check) + /mob/living/proc/create_projectile_hit_effects(obj/projectile/proj, def_zone, blocked) if (proj.damage_type != BRUTE) return @@ -473,7 +481,7 @@ if(.) return TRUE - if(!combat_mode && HAS_TRAIT(src, TRAIT_READY_TO_OPERATE) && user.perform_surgery(src)) + if(!user.combat_mode && HAS_TRAIT(src, TRAIT_READY_TO_OPERATE) && user.perform_surgery(src)) return TRUE return FALSE diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index c5acae150852..885647b905d0 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -321,7 +321,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( // But we can still see them speak if(speaker_is_signing) deaf_message = "[span_name("[speaker]")] [speaker.get_default_say_verb()] something, but the motions are too subtle to make out from afar." - else if(can_hear()) // If we can't hear we want to continue to the default deaf message + else if(!HAS_TRAIT(src, TRAIT_DEAF)) // If we can't hear we want to continue to the default deaf message if(isliving(speaker)) var/mob/living/living_speaker = speaker var/mouth_hidden = living_speaker.is_mouth_covered() || HAS_TRAIT(living_speaker, TRAIT_FACE_COVERED) @@ -375,7 +375,7 @@ GLOBAL_LIST_INIT(message_modes_stat_limits, list( deaf_type = MSG_AUDIBLE // Since you should be able to hear yourself without looking // Create map text prior to modifying message for goonchat - if (use_runechat && can_hear()) + if (use_runechat && !HAS_TRAIT(src, TRAIT_DEAF)) if (message_mods[MODE_CUSTOM_SAY_ERASE_INPUT]) create_chat_message(speaker, null, message_mods[MODE_CUSTOM_SAY_EMOTE], spans, EMOTE_MESSAGE) else diff --git a/code/modules/mob/living/living_update_icons.dm b/code/modules/mob/living/living_update_icons.dm index 274a5a4f101d..e11457a49a23 100644 --- a/code/modules/mob/living/living_update_icons.dm +++ b/code/modules/mob/living/living_update_icons.dm @@ -8,7 +8,7 @@ var/final_dir = dir var/changed = FALSE - if(lying_angle != lying_prev && rotate_on_lying) + if(lying_angle != lying_prev && (rotate_on_lying && !HAS_TRAIT(src, TRAIT_NO_LYING_ANGLE))) // DARKPACK EDIT CHANGE - WEREWOLF changed = TRUE if(lying_angle && lying_prev == 0) if(current_translate) @@ -25,7 +25,7 @@ if(resize != RESIZE_DEFAULT_SIZE) changed = TRUE - var/is_vertical = !lying_angle || !rotate_on_lying + var/is_vertical = !lying_angle || !rotate_on_lying || HAS_TRAIT(src, TRAIT_NO_LYING_ANGLE) // DARKPACK EDIT CHANGE - WEREWOLF var/new_translation = get_transform_translation_size(resize * current_size) // scaling also affects translation, so we've to undo the old translate beforehand. if(is_vertical && current_translate) @@ -41,6 +41,14 @@ if(is_vertical && new_translation) ntransform.Translate(0, new_translation) + // DARKPACK EDIT ADD START - WEREWOLF + if(HAS_TRAIT(src, TRAIT_TRANSFORM_UPDATES_ICON)) + update_body() + update_damage_overlays() + // regenerate_icons, as much as it should be what is called, happens to call this, creating a infinite loop. + // regenerate_icons() + // DARKPACK EDIT ADD END + if(!changed) //Nothing has been changed, nothing has to be done. return FALSE diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 60cfb34eebb2..1075ee898ff9 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -57,8 +57,7 @@ INVOKE_ASYNC(src, PROC_REF(set_core_display_icon), null, client) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) + spark_system = new /datum/effect_system/basic/spark_spread(src, 5, FALSE) spark_system.attach(src) add_verb(src, /mob/living/silicon/ai/proc/show_laws_verb) diff --git a/code/modules/mob/living/silicon/ai/ai_defines.dm b/code/modules/mob/living/silicon/ai/ai_defines.dm index 1dbf35d33421..6ffcd8bc66c8 100644 --- a/code/modules/mob/living/silicon/ai/ai_defines.dm +++ b/code/modules/mob/living/silicon/ai/ai_defines.dm @@ -172,4 +172,4 @@ /// Used as a fake multitool in tcomms machinery VAR_FINAL/obj/item/multitool/aiMulti /// Helper effect that creates sparks when the AI is damaged - VAR_FINAL/datum/effect_system/spark_spread/spark_system + VAR_FINAL/datum/effect_system/basic/spark_spread/spark_system diff --git a/code/modules/mob/living/silicon/ai/ai_say.dm b/code/modules/mob/living/silicon/ai/ai_say.dm index 26f2cc2f64a5..88abc47ab378 100644 --- a/code/modules/mob/living/silicon/ai/ai_say.dm +++ b/code/modules/mob/living/silicon/ai/ai_say.dm @@ -171,7 +171,7 @@ // Play voice for all mobs in the z level for(var/mob/player_mob as anything in GLOB.player_list) var/pref_volume = safe_read_pref(player_mob.client, /datum/preference/numeric/volume/sound_ai_vox) - if(!player_mob.can_hear() || !pref_volume) + if(HAS_TRAIT(player_mob, TRAIT_DEAF) || !pref_volume) continue var/turf/player_turf = get_turf(player_mob) diff --git a/code/modules/mob/living/silicon/ai/emote.dm b/code/modules/mob/living/silicon/ai/emote.dm index c73a2b95c0d7..8779eb90c571 100644 --- a/code/modules/mob/living/silicon/ai/emote.dm +++ b/code/modules/mob/living/silicon/ai/emote.dm @@ -1,8 +1,9 @@ /datum/emote/ai + abstract_type = /datum/emote/ai mob_type_allowed_typecache = /mob/living/silicon/ai mob_type_blacklist_typecache = list() - +// This might be worth making an abstract type. /datum/emote/ai/emotion_display key = "blank" var/emotion = AI_EMOTION_BLANK diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index f304cbbc400d..27556eadc506 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -1,4 +1,5 @@ /datum/emote/silicon + abstract_type = /datum/emote/silicon trait_required = TRAIT_SILICON_EMOTES_ALLOWED emote_type = EMOTE_AUDIBLE diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 75cbaaa97aaf..176caf3f346f 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,6 +1,5 @@ /mob/living/silicon/robot/Initialize(mapload) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) + spark_system = new /datum/effect_system/basic/spark_spread(src, 5, FALSE) spark_system.attach(src) add_traits(list(TRAIT_CAN_STRIP, TRAIT_FORCED_STANDING, TRAIT_KNOW_ENGI_WIRES, TRAIT_IGNORE_SURGERY_MODIFIERS), INNATE_TRAIT) @@ -250,8 +249,7 @@ return if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) + ion_trail = new(src) ionpulse_on = !ionpulse_on to_chat(src, span_notice("You [ionpulse_on ? null :"de"]activate your ion thrusters.")) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index e4af75d9025a..e32fb1d3c7ba 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -536,7 +536,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real . = TRUE return ..() || . -/mob/living/silicon/robot/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item) +/mob/living/silicon/robot/apply_damage(damage, damagetype, def_zone, blocked, forced, spread_damage, wound_bonus, exposed_wound_bonus, sharpness, attack_direction, attacking_item, wound_clothing) var/mob/living/silicon/robot/borg = src var/obj/item/shield_module/shield = locate() in borg if(!shield) diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm index 8ae9d1cf0c6b..2cf30e5a5d92 100644 --- a/code/modules/mob/living/silicon/robot/robot_defines.dm +++ b/code/modules/mob/living/silicon/robot/robot_defines.dm @@ -116,7 +116,7 @@ ///Whether the robot has no charge left. var/low_power_mode = FALSE ///So they can initialize sparks whenever/N - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system ///Smoke particle type for brute damage var/smoke_particles ///Spark particle type for burn damage diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index b68e70589e26..86f50e6246ad 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -707,10 +707,7 @@ Pass a positive integer as an argument to override a bot's default speed. bot_reset() //Reset a bot before setting it to call mode. - //For giving the bot temporary all-access. This method is bad and makes me feel bad. Refactoring access to a component is for another PR. - //Easier then building the list ourselves. I'm sorry. - var/static/obj/item/card/id/all_access = new /obj/item/card/id/advanced/gold/captains_spare() - set_path(get_path_to(src, waypoint, max_distance=200, access = all_access.GetAccess())) + set_path(get_path_to(src, waypoint, max_distance=200, access = REGION_ACCESS_ALL_STATION)) calling_ai = summoner //Link the AI to the bot! ai_waypoint = waypoint diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index b6c0b63d65d6..bd6024c74ad4 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -59,10 +59,11 @@ var/security_mode_flags = SECBOT_DECLARE_ARRESTS | SECBOT_CHECK_RECORDS | SECBOT_HANDCUFF_TARGET // Selections: SECBOT_DECLARE_ARRESTS | SECBOT_CHECK_IDS | SECBOT_CHECK_WEAPONS | SECBOT_CHECK_RECORDS | SECBOT_HANDCUFF_TARGET - ///On arrest, charges the violator this much. If they don't have that much in their account, they will get beaten instead - var/fair_market_price_arrest = 25 - ///Charged each time the violator is stunned on detain - var/fair_market_price_detain = 5 + /// On arrest, charges the violator this much. + /// If they don't have that much in their account, they will get beaten instead + var/price_arrest = 0 + /// Charged each time the violator is stunned on detain + var/price_detain = 0 ///Force of the harmbaton used on them var/weapon_force = 20 ///The department the secbot will deposit collected money into @@ -319,10 +320,7 @@ return ..() var/mob/living/carbon/carbon_target = attack_target if(!carbon_target.IsParalyzed() || !(security_mode_flags & SECBOT_HANDCUFF_TARGET)) - if(!check_nap_violations()) - stun_attack(attack_target, TRUE) - else - stun_attack(attack_target) + stun_attack(attack_target, payment_check()) else if(carbon_target.canBeHandcuffed() && !carbon_target.handcuffed) start_handcuffing(attack_target) @@ -407,11 +405,7 @@ back_to_idle() return if(Adjacent(target) && isturf(target.loc)) // if right next to perp - if(!check_nap_violations()) - stun_attack(target, TRUE) - else - stun_attack(target) - + stun_attack(target, payment_check()) set_anchored(TRUE) return @@ -448,11 +442,11 @@ frustration = 0 return - if(target.handcuffed) //no target or target cuffed? back to idle. - if(!check_nap_violations()) + if(target.handcuffed) // target is cuffed, mission accomplished + if(payment_check()) // try to fine them - give them a love tap if they fail stun_attack(target, TRUE) - return - back_to_idle() + else // otherwise return to idle state + back_to_idle() return if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && !HAS_TRAIT(target, TRAIT_FLOORED))) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. @@ -569,36 +563,26 @@ return knockOver(C) -/// Returns false if the current target is unable to pay the fair_market_price for being arrested/detained -/mob/living/simple_animal/bot/secbot/proc/check_nap_violations() - if(!SSeconomy.full_ancap) - return TRUE - if(!target) - return TRUE +/// Returns true if the current target is unable to pay to be detained/arrested +/mob/living/simple_animal/bot/secbot/proc/payment_check() + var/fair_market_price = (security_mode_flags & SECBOT_HANDCUFF_TARGET) ? price_arrest : price_detain + if(fair_market_price <= 0) + return FALSE if(!ishuman(target)) - return TRUE + return FALSE var/mob/living/carbon/human/human_target = target var/obj/item/card/id/target_id = human_target.get_idcard() if(!target_id) - say("Suspect NAP Violation: No ID card found.") - nap_violation(target) - return FALSE + say("Unable to pay fine: No ID card found.") + return TRUE var/datum/bank_account/insurance = target_id.registered_account if(!insurance) - say("Suspect NAP Violation: No bank account found.") - nap_violation(target) - return FALSE - var/fair_market_price = (security_mode_flags & SECBOT_HANDCUFF_TARGET ? fair_market_price_detain : fair_market_price_arrest) - if(!insurance.adjust_money(-fair_market_price)) - say("Suspect NAP Violation: Unable to pay.") - nap_violation(target) - return FALSE - var/datum/bank_account/beepsky_department_account = SSeconomy.get_dep_account(payment_department) - say("Thank you for your compliance. Your account been charged [fair_market_price] [MONEY_NAME].") - if(beepsky_department_account) - beepsky_department_account.adjust_money(fair_market_price) + say("Unable to pay fine: No bank account found.") + return TRUE + if(!insurance.adjust_money(-fair_market_price, "Securitron fine")) + say("Unable to pay fine: Not enough funds in account.") return TRUE -/// Does nothing -/mob/living/simple_animal/bot/secbot/proc/nap_violation(mob/violator) - return + SSeconomy.get_dep_account(payment_department)?.adjust_money(fair_market_price) + say("Fine paid: Thank you for your compliance. Your account been charged [fair_market_price] [MONEY_NAME].") + return FALSE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/clockwork_knight.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/clockwork_knight.dm index b43b9742b89f..905c5d4ba74d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/clockwork_knight.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/clockwork_knight.dm @@ -23,7 +23,7 @@ I'd rather there be something than the clockwork ruin be entirely empty though s armour_penetration = 40 melee_damage_lower = 20 melee_damage_upper = 20 - mob_biotypes = MOB_ROBOTIC|MOB_SPECIAL|MOB_MINING|MOB_MINERAL + mob_biotypes = MOB_SPECIAL|MOB_MINING|MOB_MINERAL vision_range = 9 aggro_vision_range = 9 speed = 5 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index b029c442e4c3..8ddcdf02aa55 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -53,7 +53,7 @@ Difficulty: Hard armour_penetration = 50 melee_damage_lower = 15 melee_damage_upper = 15 - mob_biotypes = MOB_ROBOTIC|MOB_SPECIAL|MOB_MINING + mob_biotypes = MOB_SPECIAL|MOB_MINING speed = 10 move_to_delay = 10 ranged = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm index 1210cb357ad4..5b5178ac5a83 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/legionnaire.dm @@ -231,9 +231,7 @@ visible_message(span_boldwarning("[src] spews smoke from the tip of their spine!")) else visible_message(span_boldwarning("[src] spews smoke from its maw!")) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(2, holder = src, location = smoke_location) - smoke.start() + do_smoke(2, src, smoke_location) //The legionnaire's head. Basically the same as any legion head, but we have to tell our creator when we die so they can generate another head. /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm index 1a6009e3bc34..3945413cb373 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/pandora.dm @@ -36,7 +36,7 @@ speed = 3 move_to_delay = 10 mouse_opacity = MOUSE_OPACITY_ICON - mob_biotypes = MOB_ROBOTIC|MOB_MINING|MOB_MINERAL + mob_biotypes = MOB_MINING|MOB_MINERAL death_sound = 'sound/effects/magic/repulse.ogg' death_message = "'s lights flicker, before its top part falls down." loot_drop = /obj/item/clothing/accessory/pandora_hope diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index ddab116113e9..4fbd0bf66a2b 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -513,7 +513,6 @@ REMOVE_TRAIT(src, TRAIT_HUSK, source) if(HAS_TRAIT(src, TRAIT_HUSK)) return FALSE - REMOVE_TRAIT(src, TRAIT_DISFIGURED, "husk") update_body() UnregisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UNHUSKABLE)) return TRUE @@ -525,7 +524,6 @@ ADD_TRAIT(src, TRAIT_HUSK, source) if (was_husk) return - ADD_TRAIT(src, TRAIT_DISFIGURED, "husk") update_body() RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_UNHUSKABLE), PROC_REF(became_unhuskable)) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index d3217bb736fd..691a36a8b658 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -100,7 +100,7 @@ update_mouse_pointer() update_ambience_area(get_area(src)) - if(!can_hear()) + if(HAS_TRAIT(src, TRAIT_DEAF)) stop_sound_channel(CHANNEL_AMBIENCE) if(client) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index da960af8c704..b04cba60081e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -243,7 +243,7 @@ type = alt_type . = FALSE - if(type & MSG_AUDIBLE && !can_hear())//Hearing related + if(type & MSG_AUDIBLE && HAS_TRAIT(src, TRAIT_DEAF))//Hearing related if(!alt_msg) return FALSE else @@ -372,7 +372,7 @@ continue if(self_message && hearing_mob == src) continue - if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(hearing_mob, audible_message_flags) && hearing_mob.can_hear()) + if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(hearing_mob, audible_message_flags) && !HAS_TRAIT(hearing_mob, TRAIT_DEAF)) hearing_mob.create_chat_message(src, raw_message = raw_msg, runechat_flags = audible_message_flags) hearing_mob.show_message(message, MSG_AUDIBLE, deaf_message, MSG_VISUAL) @@ -948,7 +948,7 @@ var/result = perform_hand_swap(held_index) if (result) - SEND_SIGNAL(src, COMSIG_MOB_SWAP_HANDS) + SEND_SIGNAL(src, COMSIG_MOB_SWAP_HANDS, get_active_held_item(), held_item) return result diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index d48bbd9ac232..02e382c7117e 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -403,10 +403,6 @@ var/mob/living/T = pick(nearby_mobs) ClickOn(T) -///Can the mob hear -/mob/proc/can_hear() - return !HAS_TRAIT(src, TRAIT_DEAF) - /** * Get the list of keywords for policy config * diff --git a/code/modules/mob_spawn/ghost_roles/mining_roles.dm b/code/modules/mob_spawn/ghost_roles/mining_roles.dm index 401366afbb6f..3fb0e2f8f7a0 100644 --- a/code/modules/mob_spawn/ghost_roles/mining_roles.dm +++ b/code/modules/mob_spawn/ghost_roles/mining_roles.dm @@ -231,6 +231,8 @@ return ..() /obj/effect/mob_spawn/ghost_role/human/ash_walker/allow_spawn(mob/user, silent = FALSE) + if(isnull(team)) + return FALSE if(!(user.ckey in team.players_spawned))//one per person unless you get a bonus spawn return TRUE if(!silent) diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm index 7f07997a5277..b4e4a78826a3 100644 --- a/code/modules/mod/mod_types.dm +++ b/code/modules/mod/mod_types.dm @@ -36,8 +36,7 @@ /obj/item/mod/control/pre_equipped/uninstall(obj/item/mod/module/old_module, deleting) . = ..() - if(default_pins[old_module.type]) - default_pins -= old_module + default_pins -= old_module.type /obj/item/mod/control/pre_equipped/standard applied_modules = list( diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index e45e59c5f2ab..092bdd654b94 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -1026,6 +1026,4 @@ /obj/item/mod/module/shock_absorber/proc/mob_batoned(datum/source) SIGNAL_HANDLER drain_power(use_energy_cost) - var/datum/effect_system/lightning_spread/sparks = new /datum/effect_system/lightning_spread - sparks.set_up(number = 5, cardinals_only = TRUE, location = mod.wearer.loc) - sparks.start() + do_sparks(5, TRUE, mod.wearer.loc) diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm index d4723b04ca68..ae72a93df3ac 100644 --- a/code/modules/mod/modules/modules_ninja.dm +++ b/code/modules/mod/modules/modules_ninja.dm @@ -265,30 +265,31 @@ var/maxcapacity = FALSE //Safety check for batteries var/drain = 0 //Drain amount from batteries var/drain_total = 0 - if(cell?.charge) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, loc) - while(cell.charge> 0 && !maxcapacity) - drain = rand(NINJA_MIN_DRAIN, NINJA_MAX_DRAIN) - if(cell.charge < drain) - drain = cell.charge - if(hacking_module.mod.get_charge() + drain > hacking_module.mod.get_max_charge()) - drain = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() - maxcapacity = TRUE//Reached maximum battery capacity. - if (do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) - spark_system.start() - playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - cell.use(drain) - hacking_module.mod.add_charge(drain) - drain_total += drain - else - break - if(!(obj_flags & EMAGGED)) - flick("apc-spark", hacking_module) + if(!cell?.charge) + hacking_module.charge_message(src, drain_total) + return + var/datum/effect_system/basic/spark_spread/spark_system = new(loc, 5, FALSE) + while(cell.charge> 0 && !maxcapacity) + drain = rand(NINJA_MIN_DRAIN, NINJA_MAX_DRAIN) + if(cell.charge < drain) + drain = cell.charge + if(hacking_module.mod.get_charge() + drain > hacking_module.mod.get_max_charge()) + drain = hacking_module.mod.get_max_charge() - hacking_module.mod.get_charge() + maxcapacity = TRUE//Reached maximum battery capacity. + if (do_after(ninja, 1 SECONDS, target = src, hidden = TRUE)) + spark_system.start() playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - obj_flags |= EMAGGED - locked = FALSE - update_appearance() + cell.use(drain) + hacking_module.mod.add_charge(drain) + drain_total += drain + else + break + if(!(obj_flags & EMAGGED)) + flick("apc-spark", hacking_module) + playsound(loc, SFX_SPARKS, 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + obj_flags |= EMAGGED + locked = FALSE + update_appearance() hacking_module.charge_message(src, drain_total) //SMES, Drains power to supply your modsuit @@ -302,8 +303,7 @@ var/maxcapacity = FALSE //Safety check for batteries var/drain = 0 //Drain amount from batteries var/drain_total = 0 - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, loc) + var/datum/effect_system/basic/spark_spread/spark_system = new(loc, 5, FALSE) while(charge > 0 && !maxcapacity) drain = rand(NINJA_MIN_DRAIN, NINJA_MAX_DRAIN) if(charge < drain) @@ -525,9 +525,7 @@ //20 uses for a standard cell. 200 for high capacity cells. if(hacking_module.mod.subtract_charge(DEFAULT_CHARGE_DRAIN*10)) //Got that electric touch - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, loc) - spark_system.start() + do_sparks(5, FALSE, loc) visible_message(span_danger("[ninja] electrocutes [src] with [ninja.p_their()] touch!"), span_userdanger("[ninja] electrocutes you with [ninja.p_their()] touch!")) addtimer(CALLBACK(src, PROC_REF(ninja_knockdown)), 0.3 SECONDS) return NONE diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index a519651ce816..9f5f1a257b96 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -91,12 +91,7 @@ /obj/item/mod/module/pepper_shoulders/on_use(mob/activator) playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6) - var/datum/reagents/capsaicin_holder = new(10) - capsaicin_holder.add_reagent(/datum/reagent/consumable/condensedcapsaicin, 10) - var/datum/effect_system/fluid_spread/smoke/chem/quick/smoke = new - smoke.set_up(1, holder = src, location = get_turf(src), carry = capsaicin_holder) - smoke.start(log = TRUE) - QDEL_NULL(capsaicin_holder) // Reagents have a ref to their holder which has a ref to them. No leaks please. + do_chem_smoke(1, src, get_turf(src), /datum/reagent/consumable/condensedcapsaicin, 10, log = TRUE, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/quick) /obj/item/mod/module/pepper_shoulders/proc/on_check_block() SIGNAL_HANDLER diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm index c40e3552bce4..1ef5a55b5c5f 100644 --- a/code/modules/mod/modules/modules_service.dm +++ b/code/modules/mod/modules/modules_service.dm @@ -49,17 +49,13 @@ balloon_alert(mod.wearer, "not in storage!") return var/obj/item/microwave_target = target - var/datum/effect_system/spark_spread/spark_effect = new() - spark_effect.set_up(2, 1, mod.wearer) - spark_effect.start() + do_sparks(2, TRUE, mod.wearer) mod.wearer.Beam(target,icon_state="lightning[rand(1,12)]", time = 5) if(microwave_target.microwave_act(microwaver = mod.wearer) & COMPONENT_MICROWAVE_SUCCESS) playsound(src, 'sound/machines/microwave/microwave-end.ogg', 50, FALSE) else balloon_alert(mod.wearer, "can't be microwaved!") - var/datum/effect_system/spark_spread/spark_effect_two = new() - spark_effect_two.set_up(2, 1, microwave_target) - spark_effect_two.start() + do_sparks(2, TRUE, microwave_target) drain_power(use_energy_cost) //Waddle - Makes you waddle and squeak. diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 5163557eb04e..d1c035d14522 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -34,8 +34,7 @@ if(computer.internal_cell && prob(25)) QDEL_NULL(computer.internal_cell) computer.visible_message(span_notice("\The [computer]'s battery explodes in rain of sparks.")) - var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread - spark_system.start() + do_sparks(3, FALSE, src) /datum/computer_file/program/revelation/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) . = ..() diff --git a/code/modules/modular_computers/file_system/programs/dept_order.dm b/code/modules/modular_computers/file_system/programs/dept_order.dm index 09ef7f9753af..538fe27aae68 100644 --- a/code/modules/modular_computers/file_system/programs/dept_order.dm +++ b/code/modules/modular_computers/file_system/programs/dept_order.dm @@ -48,6 +48,8 @@ GLOBAL_VAR(department_cd_override) /datum/computer_file/program/department_order/proc/set_linked_department(datum/job_department/department) linked_department = department var/datum/job_department/linked_department_real = SSjob.get_department_type(linked_department) + if (isnull(linked_department_real)) + return // Heads of staff can download LAZYOR(download_access, linked_department_real.head_of_staff_access) // Heads of staff + anyone in the dept can run it diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index bd81b2e719b6..961ee5341284 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -179,7 +179,6 @@ var/atom/movable/node = popleft(queue) if(visited[node]) continue - visited[node] = TRUE //visit all neighbours of this pipe as well pipe = node @@ -195,15 +194,22 @@ for(var/atom/movable/subnode in pipe.neighbours) queue += subnode + visited[node] = TRUE continue //assign machines to new network for(var/datum/component/plumbing/plumbing as anything in node.GetComponents(/datum/component/plumbing)) + //disconnect old net for(var/dirtext in plumbing.ducts) if(plumbing.ducts[dirtext] == net) net.remove_plumber(plumbing) - if(newnet) - newnet.add_plumber(plumbing, text2num(dirtext)) + //assign new net + if(newnet) + for(pipe as anything in newnet.ducts) + var/dir = pipe.neighbours[node] + if(dir) + newnet.add_plumber(plumbing, REVERSE_DIR(dir)) + disconnect() return ..() @@ -300,18 +306,16 @@ if(!user.is_holding(src)) return if(new_layer) - duct_layer = new_layer + duct_layer = GLOB.plumbing_layers[new_layer] var/new_color = tgui_input_list(user, "Select a color", "Color", GLOB.pipe_paint_colors, GLOB.pipe_color_name[duct_color]) if(!user.is_holding(src)) return if(new_color) - duct_color = new_color - add_atom_colour(GLOB.pipe_paint_colors[new_color], FIXED_COLOUR_PRIORITY) + duct_color = GLOB.pipe_paint_colors[new_color] + add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY) /obj/item/stack/ducts/wrench_act(mob/living/user, obj/item/tool) - . = check_attach_turf(loc) - if(!.) - . = ITEM_INTERACT_FAILURE + return check_attach_turf(loc) /obj/item/stack/ducts/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) // Turn into a duct stack and then merge to the in-hand stack. diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm index 16fd62d4aec2..9efcd05fcee8 100644 --- a/code/modules/plumbing/plumbers/_plumb_machinery.dm +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -8,6 +8,7 @@ icon = 'icons/obj/pipes_n_cables/hydrochem/plumbers.dmi' icon_state = "pump" density = TRUE + subsystem_type = /datum/controller/subsystem/processing/plumbing processing_flags = START_PROCESSING_MANUALLY active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 2.75 resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF @@ -22,6 +23,8 @@ /obj/machinery/plumbing/Initialize(mapload) . = ..() set_anchored(mapload) + if(mapload) + begin_processing() create_reagents(buffer, reagent_flags) AddElement(/datum/element/simple_rotation) register_context() diff --git a/code/modules/plumbing/plumbers/destroyer.dm b/code/modules/plumbing/plumbers/destroyer.dm index db3100f10f87..b86d206f22fb 100644 --- a/code/modules/plumbing/plumbers/destroyer.dm +++ b/code/modules/plumbing/plumbers/destroyer.dm @@ -1,25 +1,94 @@ +//Maximum disposal rate +#define MAX_DISPOSAL_RATE 25 + /obj/machinery/plumbing/disposer name = "chemical disposer" desc = "Breaks down chemicals and annihilates them." icon_state = "disposal" + base_icon_state = "disposal" pass_flags_self = PASSMACHINE | LETPASSTHROW // Small + active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 4 - ///we remove 5 reagents per second + ///Reagents to remove per second var/disposal_rate = 5 + ///Is this machine switched on + var/on = FALSE /obj/machinery/plumbing/disposer/Initialize(mapload, layer) . = ..() - AddComponent(/datum/component/plumbing/simple_demand, layer) + AddComponent(/datum/component/plumbing/simple_demand/disposer, layer) + RegisterSignal(reagents, COMSIG_REAGENTS_HOLDER_UPDATED, PROC_REF(update)) + +/obj/machinery/plumbing/disposer/examine(mob/user) + . = ..() + . += span_notice("It is disposing [disposal_rate]u reagents per second.") + . += span_notice("Use hand to change disposal rate.") + +/obj/machinery/plumbing/disposer/add_context(atom/source, list/context, obj/item/held_item, mob/user) + if(isnull(held_item)) + context[SCREENTIP_CONTEXT_LMB] = "Set transfer rate" + return CONTEXTUAL_SCREENTIP_SET + + return ..() + +/obj/machinery/plumbing/disposer/proc/update() + SIGNAL_HANDLER + + update_appearance(UPDATE_ICON_STATE) + +/obj/machinery/plumbing/disposer/update_icon_state() + icon_state = "[base_icon_state][is_operational && anchored && on && reagents.total_volume ? "_working" : ""]" + return ..() + +/obj/machinery/plumbing/disposer/on_set_is_operational(old_value) + . = ..() + update_appearance(UPDATE_ICON_STATE) + +/obj/machinery/plumbing/disposer/wrench_act(mob/living/user, obj/item/tool) + . = ..() + if(. == ITEM_INTERACT_SUCCESS) + update_appearance(UPDATE_ICON_STATE) + +/obj/machinery/plumbing/disposer/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ChemDisposer", name) + ui.open() + +/obj/machinery/plumbing/disposer/ui_static_data(mob/user) + return list( + max_volume = MAX_DISPOSAL_RATE + ) + +/obj/machinery/plumbing/disposer/ui_data(mob/user) + return list( + enabled = on, + disposal_rate = disposal_rate + ) + +/obj/machinery/plumbing/disposer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + switch(action) + if("toggle_power") + on = !on + update_appearance(UPDATE_ICON_STATE) + return TRUE + + if("change_volume") + var/num = text2num(params["volume"]) + if(!isnum(num)) + return FALSE + + disposal_rate = round(clamp(num, 0.1, MAX_DISPOSAL_RATE), CHEMICAL_VOLUME_ROUNDING) + return TRUE /obj/machinery/plumbing/disposer/process(seconds_per_tick) - if(!is_operational) + if(!is_operational || !reagents.total_volume || !on) return - if(reagents.total_volume) - if(icon_state != initial(icon_state) + "_working") //threw it here instead of update icon since it only has two states - icon_state = initial(icon_state) + "_working" - reagents.remove_all(disposal_rate * seconds_per_tick) - use_energy(active_power_usage * seconds_per_tick) - else - if(icon_state != initial(icon_state)) - icon_state = initial(icon_state) + reagents.remove_all(disposal_rate * seconds_per_tick) + use_energy((disposal_rate / MAX_DISPOSAL_RATE) * active_power_usage * seconds_per_tick) +#undef MAX_DISPOSAL_RATE diff --git a/code/modules/plumbing/plumbers/iv_drip.dm b/code/modules/plumbing/plumbers/iv_drip.dm index 1982d8a09ed5..1077844ae63a 100644 --- a/code/modules/plumbing/plumbers/iv_drip.dm +++ b/code/modules/plumbing/plumbers/iv_drip.dm @@ -6,10 +6,13 @@ base_icon_state = "plumb" density = TRUE use_internal_storage = TRUE + subsystem_type = /datum/controller/subsystem/processing/plumbing processing_flags = START_PROCESSING_MANUALLY /obj/machinery/iv_drip/plumbing/Initialize(mapload, layer) . = ..() + if(mapload) + begin_processing() AddComponent(/datum/component/plumbing/automated_iv, layer) AddElement(/datum/element/simple_rotation) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 5d8024216e8a..f917abf0ba2f 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -132,7 +132,9 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list( ///Clear the linked indicator bitflags /obj/structure/cable/proc/disconnect_cable() - for(var/check_dir in linked_dirs) + for(var/check_dir in GLOB.cardinals) + if(!(linked_dirs & check_dir)) + continue var/inverse = REVERSE_DIR(check_dir) var/turf/check_turf = get_step(loc, check_dir) for(var/obj/structure/cable/other_cable in check_turf) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index e6c6e61b01ec..6a0654d98733 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -242,7 +242,7 @@ color_set = color if(reagents || !is_full_charge()) START_PROCESSING(SSmachines, src) - if (reagents.spark_act(active_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE) + if (reagents?.spark_act(active_power_usage, SPARK_ACT_ENCLOSED) & SPARK_ACT_DESTRUCTIVE) message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") qdel(src) return @@ -281,7 +281,7 @@ else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE * SSMACHINES_DT) && !turned_off()) use_power = IDLE_POWER_USE low_power_mode = TRUE - if (reagents?.spark_act(idle_power_usage, TRUE) & SPARK_ACT_DESTRUCTIVE) + if (reagents?.spark_act(idle_power_usage, SPARK_ACT_ENCLOSED) & SPARK_ACT_DESTRUCTIVE) message_admins("A rigged lightbulb at [AREACOORD(src)] has exploded.") qdel(src) return @@ -759,7 +759,7 @@ /obj/machinery/light/floor/is_mountable_turf(turf/target) return !isgroundlessturf(target) -/obj/machinery/light/floor/get_moutable_objects() +/obj/machinery/light/floor/get_mountable_objects() var/static/list/attachables = list( /obj/structure/thermoplastic, /obj/structure/lattice/catwalk, diff --git a/code/modules/power/lighting/light_construct.dm b/code/modules/power/lighting/light_construct.dm index c83c4171914b..8dbadb5d06cd 100644 --- a/code/modules/power/lighting/light_construct.dm +++ b/code/modules/power/lighting/light_construct.dm @@ -184,3 +184,9 @@ icon_state = "floor-construct-stage1" fixture_type = "floor" sheets_refunded = 1 + +/obj/structure/light_construct/floor/get_turfs_to_mount_on() + return list(get_turf(src)) + +/obj/structure/light_construct/floor/is_mountable_turf(turf/target) + return !isgroundlessturf(target) diff --git a/code/modules/power/lighting/light_mapping_helpers.dm b/code/modules/power/lighting/light_mapping_helpers.dm index 460e096f1b12..89a99bd91677 100644 --- a/code/modules/power/lighting/light_mapping_helpers.dm +++ b/code/modules/power/lighting/light_mapping_helpers.dm @@ -126,8 +126,7 @@ if(ishuman(arrived)) var/mob/living/L = arrived if(L.client) - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, get_turf(src)) + var/datum/effect_system/basic/spark_spread/s = new(get_turf(src), 5, 1) s.start() playsound(loc, 'modular_darkpack/modules/electricity/sounds/generator_break.ogg', 100, TRUE) qdel(src) diff --git a/code/modules/power/power_store.dm b/code/modules/power/power_store.dm index 22539a5ed28b..63b7a9ec8e1a 100644 --- a/code/modules/power/power_store.dm +++ b/code/modules/power/power_store.dm @@ -206,7 +206,7 @@ return FALSE if (!corrupted) - if (!(reagents?.spark_act(check_charge, TRUE) & SPARK_ACT_DESTRUCTIVE)) + if (!(reagents?.spark_act(check_charge, SPARK_ACT_ENCLOSED) & SPARK_ACT_DESTRUCTIVE)) return FALSE message_admins("[ADMIN_LOOKUPFLW(usr)] has triggered a rigged power cell explosion at [AREACOORD(loc)].") usr?.log_message("triggered a rigged power cell explosion", LOG_GAME) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index b2064b0acdfe..7bde7c5e829b 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -46,7 +46,7 @@ ///What's the projectile sound? var/projectile_sound = 'sound/items/weapons/emitter.ogg' ///Sparks emitted with every shot - var/datum/effect_system/spark_spread/sparks + var/datum/effect_system/basic/spark_spread/sparks ///Stores the type of gun we are using inside the emitter var/obj/item/gun/energy/gun ///List of all the properties of the inserted gun @@ -74,9 +74,8 @@ set_anchored(TRUE) connect_to_network() - sparks = new + sparks = new(src, 5, TRUE) sparks.attach(src) - sparks.set_up(5, TRUE, src) AddElement(/datum/element/simple_rotation) AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES) diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index acdecca4b00a..da0e31fc9301 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -100,11 +100,8 @@ /obj/item/ammo_casing/shotgun/buckshot/old/fire_casing(atom/target, mob/living/user, params, distro, quiet, zone_override, spread, atom/fired_from) . = ..() - if(!fired_from) - return - - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = fired_from, location = fired_from) + if(fired_from) + do_smoke(0, fired_from, fired_from) /obj/item/ammo_casing/shotgun/buckshot/milspec name = "milspec buckshot shell" diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 011deb00f701..72c7ae3bc37a 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -146,8 +146,10 @@ var/obj/item/suppressor/suppressor = null /// Sound played when the burst mode is changed var/burst_select_sound = SFX_FIRE_MODE_SWITCH - COOLDOWN_DECLARE(recoil_skill_check) // DARKPACK EDIT ADD - + // DARKPACK EDIT ADD START - STORYTELLER_DICE + COOLDOWN_DECLARE(recoil_skill_check) + var/datum/storyteller_roll/shooting/recoil_roll + // DARKPACK EDIT ADD END // DARKPACK EDIT ADD START - FORENSICS /// Base serial number prefix, whatever's here will come before the numbers. Blank means no number/obliterated number. var/serial_type = "" @@ -238,11 +240,10 @@ // DARKPACK EDIT ADD END if(selector_switch_icon) - switch(burst_fire_selection) - if(FALSE) - . += "[initial(icon_state)]_semi" - if(TRUE) - . += "[initial(icon_state)]_burst" + if(burst_fire_selection) + . += "[initial(icon_state)]_burst" + else + . += "[initial(icon_state)]_semi" if(show_bolt_icon) if (bolt_type == BOLT_TYPE_LOCKING) @@ -607,11 +608,15 @@ if(sawn_off) bonus_spread += SAWN_OFF_ACC_PENALTY - // DARKPACK EDIT ADD - recoil + // DARKPACK EDIT ADD START - STORYTELLER_DICE + if(!recoil_roll) + recoil_roll = new() + if(COOLDOWN_FINISHED(src, recoil_skill_check)) - var/recoil_reduction = SSroll.storyteller_roll(user.st_get_stat(STAT_FIREARMS), initial(recoil), user, numerical = TRUE) + var/recoil_reduction = recoil_roll.st_roll(user, src) recoil = max(initial(recoil) - recoil_reduction, 0) COOLDOWN_START(src, recoil_skill_check, 1 SCENES) + // DARKPACK EDIT ADD END // DARKPACK EDIT ADD START - FORENSICS if(serial_type && serial_shown) diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index b1dea5902681..cceeb6b547b4 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -13,6 +13,8 @@ clumsy_check = FALSE trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead pin = /obj/item/firing_pin/magic + /// If true, our fire sound gets lower as our charges decrease + var/pitch_with_charges = TRUE /// What kind of magic is this var/school = SCHOOL_EVOCATION /// What kind of antimagic resists this @@ -49,11 +51,18 @@ return ..() /obj/item/gun/magic/fire_sounds() - var/frequency_to_use = sin((90/max_charges) * charges) + var/pitch_to_use = 1 + + if (pitch_with_charges && max_charges > 1) + pitch_to_use = LERP(1, 0.4, (1 - (charges/max_charges)) ** 2) + + var/sound/playing_sound = sound(suppressed ? suppressed_sound : fire_sound) + playing_sound.pitch = pitch_to_use + if(suppressed) - playsound(src, suppressed_sound, suppressed_volume, vary_fire_sound, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0, frequency = frequency_to_use) + playsound(src, playing_sound, suppressed_volume, vary_fire_sound, ignore_walls = FALSE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0) else - playsound(src, fire_sound, fire_sound_volume, vary_fire_sound, frequency = frequency_to_use) + playsound(src, playing_sound, fire_sound_volume, vary_fire_sound) /** * Signal proc for [COMSIG_ITEM_MAGICALLY_CHARGED] diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index f7ff0ddf2aa4..53667d5e1229 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -195,17 +195,13 @@ /obj/item/gun/magic/wand/teleport/zap_self(mob/living/user, suicide = FALSE) if(do_teleport(user, user, 10, channel = TELEPORT_CHANNEL_MAGIC)) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(3, holder = src, location = user.loc) - smoke.start() + do_smoke(3, src, user.loc) charges-- return ..() /obj/item/gun/magic/wand/teleport/do_suicide(mob/living/user) playsound(loc, fire_sound, 50, TRUE, -1) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(3, holder = src, location = user.loc) - smoke.start() + do_smoke(3, src, user.loc) if (!iscarbon(user)) return SHAME @@ -247,11 +243,11 @@ var/turf/origin = get_turf(user) var/turf/destination = find_safe_turf(extended_safety_checks = TRUE) - if(do_teleport(user, destination, channel=TELEPORT_CHANNEL_MAGIC)) - for(var/t in list(origin, destination)) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = t) - smoke.start() + if(!do_teleport(user, destination, channel = TELEPORT_CHANNEL_MAGIC)) + return ..() + + for(var/turf/smoke_turf as anything in list(origin, destination)) + do_smoke(0, src, smoke_turf) return ..() /obj/item/gun/magic/wand/safety/do_suicide(mob/living/user) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index e067bcbd13e9..e07268cbeac0 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -234,8 +234,6 @@ var/log_override = FALSE /// If true, the projectile won't cause any logging whatsoever. Used for hallucinations and shit. var/do_not_log = FALSE - /// We ignore mobs with these factions. - var/list/ignored_factions /// Turf that we have registered connect_loc signal - this is done for performance, as we're moving ~a dozen turfs per tick /// and registering and unregistering signal for every single one of them is stupid. Unregistering the signal from the correct turf in case we get moved by smth else is important var/turf/last_tick_turf @@ -644,9 +642,9 @@ var/mob/firer_mob = firer if (firer_mob.buckled == target) return FALSE - if(ignored_factions?.len && ismob(target) && !direct_target) + if(LAZYLEN(faction) && ismob(target) && !direct_target) var/mob/target_mob = target - if(target_mob.has_faction(ignored_factions)) + if(FAST_FACTION_CHECK(faction, target_mob.get_faction(), allies, target_mob.allies, FALSE)) return FALSE if(target.density || cross_failed) //This thing blocks projectiles, hit it regardless of layer/mob stuns/etc. return TRUE diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 33d8e98b373a..b5890679eae3 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -95,13 +95,13 @@ if(!isturf(target)) teleloc = target.loc for(var/atom/movable/stuff in teleloc) - if(!stuff.anchored && stuff.loc && !isobserver(stuff)) - if(do_teleport(stuff, stuff, 10, channel = TELEPORT_CHANNEL_MAGIC)) - teleammount++ - var/smoke_range = max(round(4 - teleammount), 0) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(smoke_range, holder = src, location = stuff.loc) //Smoke drops off if a lot of stuff is moved for the sake of sanity - smoke.start() + if(stuff.anchored || isobserver(stuff)) + continue + if(!do_teleport(stuff, stuff, 10, channel = TELEPORT_CHANNEL_MAGIC)) + continue + teleammount++ + var/smoke_range = max(round(4 - teleammount), 0) + do_smoke(smoke_range, src, stuff.loc) /// Teleports you somewhere on the station where the local conditions won't kill you /obj/projectile/magic/safety @@ -116,11 +116,11 @@ var/turf/origin_turf = get_turf(target) var/turf/destination_turf = find_safe_turf(extended_safety_checks = TRUE) - if(do_teleport(target, destination_turf, channel=TELEPORT_CHANNEL_MAGIC)) - for(var/t in list(origin_turf, destination_turf)) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = t) - smoke.start() + if(!do_teleport(target, destination_turf, channel = TELEPORT_CHANNEL_MAGIC)) + return + + for(var/turf/smoke_turf as anything in list(origin_turf, destination_turf)) + do_smoke(0, src, smoke_turf) /// Turns walls into doors, or opens doors /obj/projectile/magic/door diff --git a/code/modules/reagents/chem_splash.dm b/code/modules/reagents/chem_splash.dm index 2efa09635561..b13010cca424 100644 --- a/code/modules/reagents/chem_splash.dm +++ b/code/modules/reagents/chem_splash.dm @@ -79,11 +79,8 @@ */ /proc/spread_reagents(datum/reagents/source, atom/epicenter, spread_range) spread_range = min(spread_range, 20) // Fuck off with trying to do more then this - var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() - steam.set_up(10, 0, epicenter) - steam.attach(epicenter) - steam.start() - + var/datum/effect_system/basic/steam_spread/steam = new /datum/effect_system/basic/steam_spread(epicenter, 10, FALSE) + steam.attach(epicenter).start() // This is a basic floodfill algorithm of atmos connected tiles // Turfs will be stored in the form turf -> TRUE var/chem_temp = source.chem_temp diff --git a/code/modules/reagents/chemistry/holder/holder.dm b/code/modules/reagents/chemistry/holder/holder.dm index a03c360e3287..78e9c1bce798 100644 --- a/code/modules/reagents/chemistry/holder/holder.dm +++ b/code/modules/reagents/chemistry/holder/holder.dm @@ -792,10 +792,10 @@ * Call in case of electrical current exposure, rapid heating or blunt force, things that would set off explosives and alike * Arguments: * * power_charge - If we were triggered from electric current, how much power was dumped into us? - * * enclosed - Is the reaction happening in an enclosed container or not? Doesn't use reagent holder's flags as it might be called on reagents "exiting" the container + * * spark_flags - Set of flags describing the interaction * * banned_reagents - List of reagent types which we may want to have custom handling for and should avoid checking in here */ -/datum/reagents/proc/spark_act(power_charge, enclosed, list/banned_reagents) +/datum/reagents/proc/spark_act(power_charge, spark_flags, list/banned_reagents) if (!islist(banned_reagents)) banned_reagents = list(banned_reagents) var/result = NONE @@ -803,7 +803,7 @@ for (var/datum/reagent/reagent as anything in reagent_list) if (is_type_in_list(reagent, banned_reagents)) continue - var/reagent_result = reagent.on_spark_act(power_charge, enclosed) + var/reagent_result = reagent.on_spark_act(power_charge, spark_flags) if (!reagent_result) continue result |= (reagent_result & SPARK_ACT_RETURNS) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 8a1bc7479b6e..a2dbaf3860e5 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -9,6 +9,10 @@ resistance_flags = FIRE_PROOF | ACID_PROOF circuit = /obj/item/circuitboard/machine/chem_dispenser processing_flags = NONE + // DARKPACK EDIT ADD START - STORYTELER_STATS + skill_required_for_use = STAT_SCIENCE + skill_dots_minimum = 1 + // DARKPACK EDIT ADD END /// The cell used to dispense reagents var/obj/item/stock_parts/power_store/cell @@ -375,7 +379,7 @@ if("save_recording") if(!is_operational) return - var/name = tgui_input_text(ui.user, "What do you want to name this recipe?", "Recipe Name", max_length = MAX_NAME_LEN) + var/name = tgui_input_text(ui.user, "What do you want to name this recipe?", "Recipe Name", max_length = MAX_NAME_LEN, encode = FALSE) if(!ui.user.can_perform_action(src, ALLOW_SILICON_REACH)) return if(saved_recipes[name] && tgui_alert(ui.user, "\"[name]\" already exists, do you want to overwrite it?",, list("Yes", "No")) == "No") @@ -633,6 +637,8 @@ ) base_reagent_purity = 0.5 + skill_required_for_use = null // DARKPACK EDIT ADD - STORYTELER_STATS + /obj/machinery/chem_dispenser/drinks/Initialize(mapload) if(dispensable_reagents != null && !dispensable_reagents.len) dispensable_reagents = drinks_dispensable_reagents diff --git a/code/modules/reagents/chemistry/machinery/smoke_machine.dm b/code/modules/reagents/chemistry/machinery/smoke_machine.dm index e41296a589aa..d1c33fc4a294 100644 --- a/code/modules/reagents/chemistry/machinery/smoke_machine.dm +++ b/code/modules/reagents/chemistry/machinery/smoke_machine.dm @@ -30,14 +30,6 @@ opacity = FALSE alpha = 100 -/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location, datum/reagents/carry, efficiency = 10, silent = FALSE) - src.holder = holder - src.location = get_turf(location) - src.amount = amount - if(carry) - carry.trans_to(chemholder, 20, copy_only = TRUE) - carry.remove_all(amount / efficiency) - /obj/machinery/smoke_machine/Initialize(mapload) create_reagents(REAGENTS_BASE_VOLUME, INJECTABLE) @@ -163,12 +155,14 @@ return PROCESS_KILL var/turf/location = get_turf(src) - if(!(locate(/obj/effect/particle_effect/fluid/smoke) in location)) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/smoke = new() - smoke.set_up(setting * 3, holder = src, location = location, carry = reagents, efficiency = efficiency) - smoke.start() - use_energy(active_power_usage * (setting / max_range)) - update_appearance(UPDATE_ICON_STATE) + if(locate(/obj/effect/particle_effect/fluid/smoke) in location) + return + + var/smoke_amount = DIAMOND_AREA(setting * 3) + do_chem_smoke(amount = smoke_amount, holder = src, location = location, carry = reagents, carry_limit = 20, smoke_type = /datum/effect_system/fluid_spread/smoke/chem/smoke_machine) + reagents.remove_all(smoke_amount / efficiency) + use_energy(active_power_usage * (setting / max_range)) + update_appearance(UPDATE_ICON_STATE) /obj/machinery/smoke_machine/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 273268f1c30b..8a2dcaa0e538 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -242,9 +242,9 @@ * Probably shouldn't be called from within a mob's bloodstream, unless you're ready for some very explosive results * Arguments: * * power_charge - If we were triggered from electric current, how much power was dumped into us? - * * enclosed - Is the reaction happening in an enclosed container or not? Doesn't use reagent holder's flags as it might be called on reagents "exiting" the container + * * spark_flags - Flags specific to the interaction, is it in an enclosed space, should we nerf common reagents, etc. */ -/datum/reagent/proc/on_spark_act(power_charge = 0, enclosed = TRUE) +/datum/reagent/proc/on_spark_act(power_charge = 0, spark_flags = NONE) return NONE /** diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index 2304ee0fff5b..6a7ad214695e 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -408,7 +408,7 @@ return if(trans_volume >= 0.4) //prevents cheesing with ultralow doses. - exposed_mob.adjust_tox_loss((-3 * min(2, trans_volume) * REM) * normalise_creation_purity(), required_biotype = affected_biotype) //This is to promote iv pole use for that chemotherapy feel. + exposed_mob.adjust_tox_loss((-1.5 * min(2, trans_volume)) * normalise_creation_purity(), required_biotype = affected_biotype) //This is to promote iv pole use for that chemotherapy feel. var/obj/item/organ/liver/L = exposed_mob.organs_slot[ORGAN_SLOT_LIVER] if(!L || L.organ_flags & ORGAN_FAILING) return diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 31f69d54e11f..977e3db847a8 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -103,6 +103,7 @@ taste_description = "sourness" ph = 2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + default_container = /obj/item/reagent_containers/cup/glass/bottle/juice/lemonjuice /datum/reagent/consumable/banana name = "Banana Juice" diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index 80810adeedec..421f980b2a82 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -115,21 +115,26 @@ if(SPT_PROB(2.5, seconds_per_tick)) to_chat(affected_mob, span_notice("[high_message]")) affected_mob.add_mood_event("smacked out", /datum/mood_event/narcotic_heavy) - if(current_cycle == 36 && creation_purity <= 0.6) - if(!istype(affected_mob.dna.species, /datum/species/human/krokodil_addict)) - to_chat(affected_mob, span_userdanger("Your skin falls off easily!")) - var/mob/living/carbon/human/affected_human = affected_mob - affected_human.set_facial_hairstyle("Shaved", update = FALSE) - affected_human.set_hairstyle("Bald", update = FALSE) - affected_mob.set_species(/datum/species/human/krokodil_addict) - if(affected_mob.adjust_brute_loss(50 * REM, updating_health = FALSE, required_bodytype = affected_bodytype)) // holy shit your skin just FELL THE FUCK OFF - return UPDATE_MOB_HEALTH + + if(current_cycle != 36 || creation_purity > 0.6) + return + + if(istype(affected_mob.dna.species, /datum/species/human/krokodil_addict)) + return + + to_chat(affected_mob, span_userdanger("Your skin falls off easily!")) + var/mob/living/carbon/human/affected_human = affected_mob + affected_human.set_facial_hairstyle("Shaved", update = FALSE) + affected_human.set_hairstyle("Bald", update = FALSE) + affected_mob.set_species(/datum/species/human/krokodil_addict) + + if(affected_mob.adjust_brute_loss(25, updating_health = FALSE, required_bodytype = affected_bodytype)) // holy shit your skin just FELL THE FUCK OFF + return UPDATE_MOB_HEALTH /datum/reagent/drug/krokodil/overdose_process(mob/living/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - var/need_mob_update - need_mob_update = affected_mob.adjust_organ_loss(ORGAN_SLOT_BRAIN, 0.25 * metabolization_ratio * seconds_per_tick, required_organ_flag = affected_organ_flags) - need_mob_update = affected_mob.adjust_tox_loss(0.25 * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype) + var/need_mob_update = affected_mob.adjust_organ_loss(ORGAN_SLOT_BRAIN, 0.25 * metabolization_ratio * seconds_per_tick, required_organ_flag = affected_organ_flags) + need_mob_update |= affected_mob.adjust_tox_loss(0.25 * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype) if(need_mob_update) return UPDATE_MOB_HEALTH diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 7633a00f0502..db0d3e190a6b 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -351,6 +351,15 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/item/reagent_containers/condiment/ketchup +/datum/reagent/consumable/mustard + name = "Mustard" + description = "Spicy, tangy sauce, made from the mustard plant." + nutriment_factor = 5 + color = "#ffd129" + taste_description = "mustard" + chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + default_container = /obj/item/reagent_containers/condiment/mustard + /datum/reagent/consumable/capsaicin name = "Capsaicin Oil" description = "This is what makes chilis hot." 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 2b89e0dbc3bf..13f14d3af2c9 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 @@ -767,18 +767,18 @@ Basically, we fill the time between now and 2s from now with hands based off the . = ..() random_span = pick("clown", "small", "big", "hypnophrase", "alien", "cult", "alert", "danger", "emote", "yell", "brass", "sans", "papyrus", "robot", "his_grace", "phobia") RegisterSignal(affected_mob, COMSIG_MOVABLE_HEAR, PROC_REF(owner_hear)) - to_chat(affected_mob, span_warning("Your hearing seems to be a bit off[affected_mob.can_hear() ? "!" : " - wait, that's normal."]")) + to_chat(affected_mob, span_warning("Your hearing seems to be a bit off[!HAS_TRAIT(affected_mob, TRAIT_DEAF) ? "!" : " - wait, that's normal."]")) /datum/reagent/impurity/inacusiate/on_mob_end_metabolize(mob/living/affected_mob) . = ..() UnregisterSignal(affected_mob, COMSIG_MOVABLE_HEAR) - to_chat(affected_mob, span_notice("You start hearing things normally again[affected_mob.can_hear() ? "" : " - no, wait, no you don't"].")) + to_chat(affected_mob, span_notice("You start hearing things normally again[!HAS_TRAIT(affected_mob, TRAIT_DEAF) ? "" : " - no, wait, no you don't"].")) /datum/reagent/impurity/inacusiate/proc/owner_hear(mob/living/owner, list/hearing_args) SIGNAL_HANDLER // don't skip messages that the owner says or can't understand (since they still make sounds) - if(!owner.can_hear()) + if(HAS_TRAIT(owner, TRAIT_DEAF)) return // not technically hearing var/atom/movable/speaker = hearing_args[HEARING_SPEAKER] @@ -1220,7 +1220,7 @@ Basically, we fill the time between now and 2s from now with hands based off the /datum/reagent/inverse/colorful_reagent/overdose_start(mob/living/affected_mob) . = ..() - metabolization_rate = 0.04 * REM + metabolization_rate = 0.1 * REAGENTS_METABOLISM /datum/reagent/inverse/colorful_reagent/on_mob_metabolize(mob/living/carbon/affected_mob) . = ..() diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index c5357772f049..57e21242b34f 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -159,7 +159,9 @@ for(var/i in affected_mob.all_wounds) var/datum/wound/iter_wound = i iter_wound.on_xadone(0.5 * power * metabolization_ratio * seconds_per_tick) - REMOVE_TRAIT(affected_mob, TRAIT_DISFIGURED, TRAIT_GENERIC) //fixes common causes for disfiguration + var/obj/item/bodypart/head = affected_mob.get_bodypart(BODY_ZONE_HEAD) + if (head) + REMOVE_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC) //fixes common causes for disfiguration if(need_mob_update) return UPDATE_MOB_HEALTH @@ -178,29 +180,32 @@ /datum/reagent/medicine/pyroxadone/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(affected_mob.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT) - var/power = 0 - switch(affected_mob.bodytemperature) - if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400) - power = 2 - if(400 to 460) - power = 3 - else - power = 5 - if(affected_mob.on_fire) - power *= 2 + if(affected_mob.bodytemperature <= BODYTEMP_HEAT_DAMAGE_LIMIT) + return + var/power = 0 + switch(affected_mob.bodytemperature) + if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400) + power = 2 + if(400 to 460) + power = 3 + else + power = 5 + if(affected_mob.on_fire) + power *= 2 - var/need_mob_update - need_mob_update = affected_mob.adjust_oxy_loss(-1 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) - need_mob_update += affected_mob.adjust_brute_loss(-0.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype) - need_mob_update += affected_mob.adjust_fire_loss(-0.75 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype) - need_mob_update += affected_mob.adjust_tox_loss(-0.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, forced = TRUE, required_biotype = affected_biotype) - if(need_mob_update) - . = UPDATE_MOB_HEALTH - for(var/i in affected_mob.all_wounds) - var/datum/wound/iter_wound = i - iter_wound.on_xadone(0.5 * power * metabolization_ratio * seconds_per_tick) - REMOVE_TRAIT(affected_mob, TRAIT_DISFIGURED, TRAIT_GENERIC) + var/need_mob_update + need_mob_update = affected_mob.adjust_oxy_loss(-1 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) + need_mob_update += affected_mob.adjust_brute_loss(-0.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype) + need_mob_update += affected_mob.adjust_fire_loss(-0.75 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype) + need_mob_update += affected_mob.adjust_tox_loss(-0.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, forced = TRUE, required_biotype = affected_biotype) + if(need_mob_update) + . = UPDATE_MOB_HEALTH + for(var/i in affected_mob.all_wounds) + var/datum/wound/iter_wound = i + iter_wound.on_xadone(0.5 * power * metabolization_ratio * seconds_per_tick) + var/obj/item/bodypart/head = affected_mob.get_bodypart(BODY_ZONE_HEAD) + if (head) + REMOVE_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC) /datum/reagent/medicine/rezadone name = "Rezadone" @@ -223,7 +228,9 @@ required_bodytype = affected_biotype )) . = UPDATE_MOB_HEALTH - REMOVE_TRAIT(affected_mob, TRAIT_DISFIGURED, TRAIT_GENERIC) + var/obj/item/bodypart/head = affected_mob.get_bodypart(BODY_ZONE_HEAD) + if (head) + REMOVE_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC) /datum/reagent/medicine/rezadone/overdose_process(mob/living/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() @@ -922,7 +929,7 @@ . = ..() if(creation_purity >= 1) ADD_TRAIT(affected_mob, TRAIT_GOOD_HEARING, type) - if(affected_mob.can_hear()) + if(!HAS_TRAIT(affected_mob, TRAIT_DEAF)) to_chat(affected_mob, span_nicegreen("You can feel your hearing drastically improve!")) /datum/reagent/medicine/inacusiate/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) @@ -938,7 +945,7 @@ /datum/reagent/medicine/inacusiate/on_mob_delete(mob/living/affected_mob) . = ..() REMOVE_TRAIT(affected_mob, TRAIT_GOOD_HEARING, type) - if(affected_mob.can_hear()) + if(!HAS_TRAIT(affected_mob, TRAIT_DEAF)) to_chat(affected_mob, span_notice("Your hearing returns to its normal acuity.")) /datum/reagent/medicine/atropine @@ -951,7 +958,7 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED inverse_chem_val = 0.35 inverse_chem = /datum/reagent/inverse/atropine - added_traits = list(TRAIT_PREVENT_IMPLANT_AUTO_EXPLOSION) + added_traits = list(TRAIT_NOCRITDAMAGE, TRAIT_PREVENT_IMPLANT_AUTO_EXPLOSION) /datum/reagent/medicine/atropine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() @@ -1021,6 +1028,7 @@ /datum/reagent/medicine/epinephrine/metabolize_reagent(mob/living/carbon/affected_mob, seconds_per_tick, metabolized_volume) if(holder.has_reagent(/datum/reagent/toxin/lexorin)) + // REM is intentional here holder.remove_reagent(/datum/reagent/toxin/lexorin, 1 * REM * metabolized_volume * seconds_per_tick) holder.remove_reagent(/datum/reagent/medicine/epinephrine, 0.5 * REM * metabolized_volume * seconds_per_tick) return ..() diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 5624bb737259..e52275d312ac 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -351,8 +351,8 @@ /datum/reagent/water/holywater/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - - data["deciseconds_metabolized"] += (seconds_per_tick * 1 SECONDS * REM) + // Microdosing holy water is less effective than just gulping it down + data["deciseconds_metabolized"] += seconds_per_tick * 1 SECONDS * metabolization_ratio affected_mob.adjust_jitter_up_to(2 SECONDS * metabolization_ratio * seconds_per_tick, 20 SECONDS) var/need_mob_update = FALSE @@ -1028,7 +1028,7 @@ /datum/reagent/chlorine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(affected_mob.take_bodypart_damage(0.5*REM*seconds_per_tick, 0)) + if(affected_mob.take_bodypart_damage(0.25 * metabolization_ratio * seconds_per_tick, 0)) return UPDATE_MOB_HEALTH /datum/reagent/fluorine @@ -1048,7 +1048,7 @@ /datum/reagent/fluorine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(affected_mob.adjust_tox_loss(0.5*REM*seconds_per_tick, updating_health = FALSE)) + if(affected_mob.adjust_tox_loss(0.25 * metabolization_ratio * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH /datum/reagent/sodium @@ -1149,7 +1149,7 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/effect/decal/cleanable/greenglow /// How much tox damage to deal per tick - var/tox_damage = 0.5 + var/tox_damage = 0.25 /// How radioactive is this reagent var/rad_power = 1 @@ -1159,7 +1159,8 @@ var/chance = min(volume / (20 - rad_power * 5), rad_power) if(SPT_PROB(chance, seconds_per_tick)) // ignore rad protection calculations bc it's inside of us affected_mob.AddComponent(/datum/component/irradiated) - if(affected_mob.adjust_tox_loss(tox_damage * seconds_per_tick * REM, updating_health = FALSE)) + + if(affected_mob.adjust_tox_loss(tox_damage * seconds_per_tick * metabolization_rate, updating_health = FALSE)) return UPDATE_MOB_HEALTH /datum/reagent/uranium/expose_obj(obj/exposed_obj, reac_volume, methods=TOUCH, show_message=TRUE) @@ -1226,7 +1227,7 @@ description = "Radium is an alkaline earth metal. It is extremely radioactive." color = "#00CC00" // ditto taste_description = "the colour blue and regret" - tox_damage = 1 + tox_damage = 0.5 material = null ph = 10 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED @@ -1320,14 +1321,18 @@ if(pool) pool.burn_amount = max(min(round(reac_volume / 5), 10), 1) -/datum/reagent/fuel/on_spark_act(power_charge, enclosed) +/datum/reagent/fuel/on_spark_act(power_charge, spark_flags) // Doesn't go boom in open air unless we pass a REALLY high current or if we're hot enough - if (!enclosed && power_charge < STANDARD_BATTERY_VALUE && holder.chem_temp < 474) + if (!(spark_flags & SPARK_ACT_ENCLOSED) && power_charge < STANDARD_BATTERY_VALUE && holder.chem_temp < 474) var/turf/our_turf = get_turf(holder.my_atom) our_turf?.hotspot_expose(holder.chem_temp * 10, volume) return NONE - reagent_explode(holder, volume, strengthdiv = 10, clear_holder_reagents = FALSE) + var/strengthdiv = 10 + if (spark_flags & SPARK_ACT_WEAKEN_COMMON) + strengthdiv *= 3 // Noticeably weaker than waterpot, at least put some effort in, cmon + + reagent_explode(holder, volume, strengthdiv = strengthdiv, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/space_cleaner diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index d6c65db9324a..a768c47fd1a5 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -29,8 +29,8 @@ if(affected_mob.adjust_organ_loss(ORGAN_SLOT_HEART, -0.5 * metabolization_ratio * seconds_per_tick * normalise_creation_purity(), required_organ_flag = affected_organ_flags)) return UPDATE_MOB_HEALTH -/datum/reagent/nitroglycerin/on_spark_act(power_charge, enclosed) - reagent_explode(holder, volume, strengthdiv = 2, clear_holder_reagents = FALSE) +/datum/reagent/nitroglycerin/on_spark_act(power_charge, spark_flags) + reagent_explode(holder, volume, strengthdiv = 2, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/stabilizing_agent @@ -89,7 +89,7 @@ taste_description = "air and bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/sorium/on_spark_act(power_charge, enclosed) +/datum/reagent/sorium/on_spark_act(power_charge, spark_flags) var/range = clamp(sqrt(volume), 1, 6) goonchem_vortex(get_turf(holder.my_atom), 1, range) return SPARK_ACT_NON_DESTRUCTIVE @@ -101,7 +101,7 @@ taste_description = "compressed bitterness" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/liquid_dark_matter/on_spark_act(power_charge, enclosed) +/datum/reagent/liquid_dark_matter/on_spark_act(power_charge, spark_flags) var/range = clamp(sqrt(volume / 2), 1, 6) goonchem_vortex(get_turf(holder.my_atom), 0, range) return SPARK_ACT_NON_DESTRUCTIVE @@ -129,12 +129,11 @@ if(source.flags_1 & PREVENT_CONTENTS_EXPLOSION_1) return var/location = get_turf(holder.my_atom) - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(1 + round(volume / 6, 1), location, message = FALSE) - e.start(holder.my_atom) + var/datum/effect_system/reagents_explosion/expl = new(location, 1 + round(volume / 6, 1), message = FALSE) + expl.start(holder.my_atom) holder.clear_reagents() -/datum/reagent/gunpowder/on_spark_act(power_charge, enclosed) +/datum/reagent/gunpowder/on_spark_act(power_charge, spark_flags) // Gunpowder doesn't blow in presence of stabilizing agent but instead consumes it every time it'd get triggered var/agent_volume = holder.get_reagent_amount(/datum/reagent/stabilizing_agent) if (agent_volume) @@ -144,11 +143,11 @@ return if (power_charge >= STANDARD_CELL_CHARGE || holder.chem_temp >= 474) - reagent_explode(holder, volume, modifier = 5, strengthdiv = 10, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, modifier = 5, strengthdiv = 10, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL holder.my_atom.visible_message(span_boldnotice("Sparks start flying around the gunpowder!")) - if (!enclosed) + if (!(spark_flags & SPARK_ACT_ENCLOSED)) do_sparks(2, TRUE, get_turf(holder.my_atom)) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reagent_explode), holder, volume, 5, 10), rand(5 SECONDS, 10 SECONDS)) return SPARK_ACT_NON_DESTRUCTIVE // just wait a bit... @@ -160,15 +159,15 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/rdx/on_spark_act(power_charge, enclosed) +/datum/reagent/rdx/on_spark_act(power_charge, spark_flags) if (power_charge) // Okay but what if we made a REALLY big boom? var/power_coeff = log(2, power_charge / STANDARD_CELL_CHARGE) - reagent_explode(holder, volume, modifier = min(2 * power_coeff, 8), strengthdiv = 7 / min(power_coeff, 4), clear_holder_reagents = FALSE) + reagent_explode(holder, volume, modifier = min(2 * power_coeff, 8), strengthdiv = 7 / min(power_coeff, 4), clear_holder_reagents = FALSE, flame_factor = 1) else if (holder.chem_temp >= 474) - reagent_explode(holder, volume, modifier = 2, strengthdiv = 7, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, modifier = 2, strengthdiv = 7, clear_holder_reagents = FALSE, flame_factor = 1) else - reagent_explode(holder, volume, strengthdiv = 8, clear_holder_reagents = FALSE) + reagent_explode(holder, volume, strengthdiv = 8, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/tatp @@ -178,8 +177,8 @@ taste_description = "death" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/tatp/on_spark_act(power_charge, enclosed) - reagent_explode(holder, volume, strengthdiv = 1.5 + rand() * 1.5, clear_holder_reagents = FALSE) +/datum/reagent/tatp/on_spark_act(power_charge, spark_flags) + reagent_explode(holder, volume, strengthdiv = 1.5 + rand() * 1.5, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/flash_powder @@ -189,11 +188,14 @@ taste_description = "salt" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/flash_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/flash_powder/on_spark_act(power_charge, spark_flags) // Even weaker version of the normal flash effect var/turf/location = get_turf(holder.my_atom) - if (!enclosed) + if (!(spark_flags & SPARK_ACT_ENCLOSED)) do_sparks(2, TRUE, location) + else if (!(holder.flags & TRANSPARENT) || (!isturf(holder.my_atom.loc) && !ismob(holder.my_atom.loc))) // nope + return SPARK_ACT_NON_DESTRUCTIVE + var/range = round(volume / 15, 1) holder.my_atom.flash_lighting_fx(range = (range + 2)) for(var/mob/living/victim in get_hearers_in_view(range, location)) @@ -216,21 +218,19 @@ taste_description = "smoke" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/smoke_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/smoke_powder/on_spark_act(power_charge, spark_flags) // Can't really make a cloud of smoke if we're inside of an enclosed container // ...unless we're a mob, in which case this is pretty cursed - if (enclosed && !ismob(holder.my_atom)) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) return var/location = get_turf(holder.my_atom) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_system = new() playsound(location, 'sound/effects/smoke.ogg', 50, TRUE, -3) if (iscarbon(holder.my_atom)) var/mob/living/carbon/victim = holder.my_atom if (victim.stat != DEAD) victim.visible_message(span_warning("[victim] starts violently coughing up smoke!")) victim.adjust_organ_loss(ORGAN_SLOT_LUNGS, volume / 15) - smoke_system.set_up(amount = volume / 1.5, holder = holder.my_atom, location = location, carry = holder, silent = FALSE) - smoke_system.start(log = TRUE) + do_chem_smoke(amount = volume / 1.5, holder = holder.my_atom, location = location, carry = holder, silent = FALSE, log = TRUE) return SPARK_ACT_NON_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL /datum/reagent/sonic_powder @@ -240,7 +240,7 @@ taste_description = "loud noises" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/sonic_powder/on_spark_act(power_charge, enclosed) +/datum/reagent/sonic_powder/on_spark_act(power_charge, spark_flags) // Even weaker version of the normal flash effect var/turf/location = get_turf(holder.my_atom) var/range = round(volume / 15, 1) @@ -270,8 +270,8 @@ if(metabolizer.adjust_fire_loss(0.15 * max(metabolizer.fire_stacks, 0.15) * metabolization_ratio * seconds_per_tick, updating_health = FALSE)) return UPDATE_MOB_HEALTH -/datum/reagent/phlogiston/on_spark_act(power_charge, enclosed) - if (enclosed && !ismob(holder.my_atom)) +/datum/reagent/phlogiston/on_spark_act(power_charge, spark_flags) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) if (!holder.my_atom.uses_integrity) return // Spicy! @@ -308,8 +308,8 @@ if(istype(exposed_mob) && (methods & (TOUCH|VAPOR|PATCH))) exposed_mob.adjust_fire_stacks(min(reac_volume / 4, 20)) -/datum/reagent/napalm/on_spark_act(power_charge, enclosed) - if (enclosed && !ismob(holder.my_atom)) +/datum/reagent/napalm/on_spark_act(power_charge, spark_flags) + if ((spark_flags & SPARK_ACT_ENCLOSED) && !ismob(holder.my_atom)) if (!holder.my_atom.uses_integrity) return // Spicy! @@ -361,7 +361,7 @@ /datum/reagent/cryostylane/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - metabolization_rate = 0.25 * REM//faster consumption when alive + metabolization_rate = 0.625 * REAGENTS_METABOLISM //faster consumption when alive if(affected_mob.reagents.has_reagent(/datum/reagent/oxygen)) affected_mob.reagents.remove_reagent(/datum/reagent/oxygen, 1 * metabolization_ratio * seconds_per_tick) affected_mob.adjust_bodytemperature(-30 * metabolization_ratio * seconds_per_tick) @@ -445,7 +445,7 @@ var/mob/living/carbon/human/affected_human = affected_mob affected_human.physiology.siemens_coeff *= 0.5 -/datum/reagent/teslium/on_spark_act(power_charge, enclosed) +/datum/reagent/teslium/on_spark_act(power_charge, spark_flags) tesla_zap(source = holder.my_atom, zap_range = round(volume / 5, 1), power = volume * 20 + power_charge, cutoff = 1 KILO JOULES, zap_flags = ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN | ZAP_LOW_POWER_GEN) playsound(holder.my_atom, 'sound/machines/defib/defib_zap.ogg', 50, TRUE) return SPARK_ACT_NON_DESTRUCTIVE diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index 944fcac731fe..70bbb61993ac 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -67,7 +67,7 @@ /datum/reagent/toxin/mutagen/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() - if(affected_mob.adjust_tox_loss(0.5 * seconds_per_tick * REM, required_biotype = affected_biotype)) + if(affected_mob.adjust_tox_loss(0.25 * seconds_per_tick * metabolization_ratio, required_biotype = affected_biotype)) return UPDATE_MOB_HEALTH /datum/reagent/toxin/mutagen/on_hydroponics_apply(obj/machinery/hydroponics/mytray, mob/user) @@ -82,6 +82,7 @@ #define LIQUID_PLASMA_BP (50+T0C) #define LIQUID_PLASMA_IG (325+T0C) #define LIQUID_PLASMA_CHARGE_COEFF 2.7 +#define LIQUID_PLASMA_VOLUME_POWER_CAP 7 /datum/reagent/toxin/plasma name = "Plasma" @@ -150,7 +151,16 @@ exposed_mob.adjust_fire_stacks(reac_volume / 5) return -/datum/reagent/toxin/plasma/on_spark_act(power_charge, enclosed) +/datum/reagent/toxin/plasma/on_spark_act(power_charge, spark_flags) + // Tape up your plasma IEDs + if ((spark_flags & SPARK_ACT_WEAKEN_COMMON) && !(spark_flags & SPARK_ACT_ENCLOSED)) + if(holder.chem_temp < LIQUID_PLASMA_BP) + return NONE + var/turf/holder_turf = get_turf(holder) + if (holder_turf) + holder_turf.atmos_spawn_air("[GAS_PLASMA]=[volume];[TURF_TEMPERATURE(holder.chem_temp)]") + return SPARK_ACT_NON_DESTRUCTIVE + // If we have any stabilizing agent in the mix, we need 0.2% of a standard cell value per mol of agent to be spent at once to blow // This should allow for some more creative traps to be made with plasma var/agent_volume = holder.get_reagent_amount(/datum/reagent/stabilizing_agent) @@ -158,13 +168,21 @@ return NONE // Plasma explosions become stronger with higher current, and don't care about if they're enclosed or not - var/power_modifier = max(0, round(power_charge / STANDARD_CELL_CHARGE * LIQUID_PLASMA_CHARGE_COEFF, 1) - 1) - reagent_explode(holder, volume, modifier = power_modifier, strengthdiv = 5, clear_holder_reagents = FALSE) + var/power_modifier = max(0, round(sqrt(power_charge / STANDARD_CELL_CHARGE) * LIQUID_PLASMA_CHARGE_COEFF, 1) - 1) + var/strengthdiv = 5 + if (spark_flags & SPARK_ACT_WEAKEN_COMMON) + strengthdiv *= 3 // Stronger than waterpot, weaker than methbombs + var/current_limit = round(volume / LIQUID_PLASMA_VOLUME_POWER_CAP, 1) + // High current can only get you so far before you get a sharp dropoff + if (power_modifier > current_limit) + power_modifier = round(current_limit + log(power_modifier - current_limit + 1), 1) + reagent_explode(holder, volume, modifier = power_modifier, strengthdiv = strengthdiv, clear_holder_reagents = FALSE, flame_factor = 1) return SPARK_ACT_DESTRUCTIVE | SPARK_ACT_CLEAR_ALL #undef LIQUID_PLASMA_BP #undef LIQUID_PLASMA_IG #undef LIQUID_PLASMA_CHARGE_COEFF +#undef LIQUID_PLASMA_VOLUME_POWER_CAP /datum/reagent/toxin/hot_ice name = "Hot Ice Slush" @@ -296,7 +314,7 @@ /datum/reagent/toxin/zombiepowder/proc/zombify(mob/living/holder_mob) PRIVATE_PROC(TRUE) - holder_mob.adjust_oxy_loss(0.5*REM, FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) + holder_mob.adjust_oxy_loss(0.25, FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) if((data?["method"] & (INGEST|INHALE)) && holder_mob.stat != DEAD) holder_mob.apply_status_effect(/datum/status_effect/reagent_effect/fakedeath, type) @@ -855,8 +873,7 @@ /datum/reagent/toxin/itching_powder/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) var/scratched = FALSE - var/scratch_damage = 0.2 * REM - + var/scratch_damage = 0.25 * metabolization_ratio var/obj/item/bodypart/head = affected_mob.get_bodypart(BODY_ZONE_HEAD) if(!isnull(head) && SPT_PROB(8, seconds_per_tick)) scratched = affected_mob.itch(damage = scratch_damage, target_part = head) diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index e07c14d04846..ff4438037a06 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -305,8 +305,7 @@ if (!istype(holder.my_atom, /obj/machinery/plumbing)) //excludes standard plumbing equipment from spamming admins with this shit message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(our_turf)][inside_msg]. Last Fingerprint: [touch_msg].") log_game("Reagent explosion reaction occurred at [AREACOORD(our_turf)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." ) - var/datum/effect_system/reagents_explosion/explosion_system = new() - explosion_system.set_up(power, our_turf, flash_fact = flash_factor, flame_fact = flame_factor) + var/datum/effect_system/reagents_explosion/explosion_system = new(our_turf, power, flash_fact = flash_factor, flame_fact = flame_factor) explosion_system.start(holder.my_atom) if (istype(holder.my_atom, /obj/item/organ/stomach)) @@ -370,7 +369,6 @@ //Spews out the inverse of the chems in the beaker of the products/reactants only /datum/chemical_reaction/proc/explode_invert_smoke(datum/reagents/holder, datum/equilibrium/equilibrium, force_range = 0, clear_products = TRUE, clear_reactants = TRUE, accept_impure = TRUE) var/datum/reagents/invert_reagents = new (2100, NO_REACT)//I think the biggest size we can get is 2100? - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new() var/sum_volume = 0 invert_reagents.my_atom = holder.my_atom //Give the gas a fingerprint for(var/datum/reagent/reagent as anything in holder.reagent_list) //make gas for reagents, has to be done this way, otherwise it never stops Exploding @@ -386,8 +384,7 @@ if(!force_range) force_range = (sum_volume/6) + 3 if(invert_reagents.reagent_list) - smoke.set_up(force_range, holder = holder.my_atom, location = holder.my_atom, carry = invert_reagents) - smoke.start(log = TRUE) + do_chem_smoke(force_range, holder.my_atom, holder.my_atom, carry = invert_reagents, log = TRUE) holder.my_atom.audible_message("The [holder.my_atom] suddenly explodes, launching the aerosolized reagents into the air!") if(clear_reactants) clear_reactants(holder) @@ -397,7 +394,6 @@ //Spews out the corrisponding reactions reagents (products/required) of the beaker in a smokecloud. Doesn't spew catalysts /datum/chemical_reaction/proc/explode_smoke(datum/reagents/holder, datum/equilibrium/equilibrium, force_range = 0, clear_products = TRUE, clear_reactants = TRUE) var/datum/reagents/reagents = new/datum/reagents(2100, NO_REACT)//Lets be safe first - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new() reagents.my_atom = holder.my_atom //fingerprint var/sum_volume = 0 for (var/datum/reagent/reagent as anything in holder.reagent_list) @@ -407,8 +403,7 @@ if(!force_range) force_range = (sum_volume/6) + 3 if(reagents.reagent_list) - smoke.set_up(force_range, holder = holder.my_atom, location = holder.my_atom, carry = reagents) - smoke.start(log = TRUE) + do_chem_smoke(force_range, holder = holder.my_atom, location = holder.my_atom, carry = reagents, log = TRUE) holder.my_atom.audible_message("The [holder.my_atom] suddenly explodes, launching the aerosolized reagents into the air!") if(clear_reactants) clear_reactants(holder) diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index f4f89f6af366..8b563bb9b1a4 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -72,9 +72,8 @@ if(!istype(holder.my_atom, /obj/machinery/plumbing)) //excludes standard plumbing equipment from spamming admins with this shit message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].") log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." ) - var/datum/effect_system/reagents_explosion/e = new() - e.set_up(power, T) - e.start(holder.my_atom) + var/datum/effect_system/reagents_explosion/expl = new(T, power) + expl.start(holder.my_atom) holder.clear_reagents() ///Amount of meth required to make a crystal diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index 876bfc6d78ed..d627f119cd97 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -393,12 +393,8 @@ return holder.remove_reagent(/datum/reagent/smoke_powder, created_volume * 3) var/location = get_turf(holder.my_atom) - var/datum/effect_system/fluid_spread/smoke/chem/S = new - S.attach(location) + do_chem_smoke(amount = created_volume * 3, holder = holder.my_atom, location = location, carry = holder, silent = FALSE, log = TRUE) playsound(location, 'sound/effects/smoke.ogg', 50, TRUE, -3) - if(S) - S.set_up(amount = created_volume * 3, holder = holder.my_atom, location = location, carry = holder, silent = FALSE) - S.start(log = TRUE) if(holder?.my_atom) holder.clear_reagents() if (!iscarbon(holder?.my_atom)) @@ -417,12 +413,8 @@ /datum/chemical_reaction/smoke_powder_smoke/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume) var/location = get_turf(holder.my_atom) - var/datum/effect_system/fluid_spread/smoke/chem/S = new - S.attach(location) + do_chem_smoke(amount = created_volume, holder = holder.my_atom, location = location, carry = holder, log = TRUE, silent = FALSE) playsound(location, 'sound/effects/smoke.ogg', 50, TRUE, -3) - if(S) - S.set_up(amount = created_volume, holder = holder.my_atom, location = location, carry = holder, silent = FALSE) - S.start(log = TRUE) if(holder?.my_atom) holder.clear_reagents() if(holder?.my_atom) diff --git a/code/modules/reagents/reagent_containers/condiment.dm b/code/modules/reagents/reagent_containers/condiment.dm index cd2d8e6821ff..56c1c26654d4 100644 --- a/code/modules/reagents/reagent_containers/condiment.dm +++ b/code/modules/reagents/reagent_containers/condiment.dm @@ -329,6 +329,13 @@ list_reagents = list(/datum/reagent/consumable/ketchup = 50) fill_icon_thresholds = null +/obj/item/reagent_containers/condiment/mustard + name = "mustard" + desc = "A spicy and tangy sauce made out of the mustard plant. Great on hotdogs!" + icon_state = "mustard" + list_reagents = list(/datum/reagent/consumable/mustard = 50) + fill_icon_thresholds = null + /obj/item/reagent_containers/condiment/worcestershire name = "worcestershire sauce" desc = "A fermented sauce of legend from old England. Makes almost anything better." diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index cc65cb3d9e97..e76832500a2f 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -11,31 +11,53 @@ righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi' reagent_container_liquid_sound = SFX_DEFAULT_LIQUID_SLOSH - ///Like Edible's food type, what kind of drink is this? + /// Like Edible's food type, what kind of drink is this? var/drink_type = NONE - ///The last time we have checked for taste. + /// The last time we have checked for taste. var/last_check_time - ///How much we drink at once, shot glasses drink more. + /// How much we drink at once, shot glasses drink more. var/gulp_size = 5 - ///Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it. + /// Whether the 'bottle' is made of glass or not so that milk cartons dont shatter when someone gets hit by it. var/isGlass = FALSE - ///What kind of chem transfer method does this cup use. Defaults to INGEST + /// What kind of chem transfer method does this cup use. Defaults to INGEST var/reagent_consumption_method = INGEST - ///What sound does our consumption play on consuming from the container? + /// What sound does our consumption play on consuming from the container? var/consumption_sound = 'sound/items/drink.ogg' - ///Whether to allow heating up the contents with a source of flame. + /// Whether to allow heating up the contents with a source of flame. var/heatable = TRUE + /// Can we put a lid on this container? + var/can_lid = FALSE + /// Does this container have a lid on right now? + var/has_lid = FALSE + /// Assembly attached to our lid + var/obj/item/assembly_holder/lid_assembly = null + /// Power cell duct-taped to the side of the beaker + var/obj/item/stock_parts/power_store/cell/attached_cell = null + /// Have we added wiring to the cell? + var/cell_wired = FALSE + /// Visual y-offset for the assembly on our lid + var/assembly_pixel_y = 0 /obj/item/reagent_containers/cup/Initialize(mapload, vol) . = ..() if(heatable) AddElement(/datum/element/reagents_item_heatable) +/obj/item/reagent_containers/cup/Destroy(force) + QDEL_NULL(lid_assembly) + QDEL_NULL(attached_cell) + return ..() + /obj/item/reagent_containers/cup/examine(mob/user) . = ..() if(drink_type) var/list/types = bitfield_to_list(drink_type, FOOD_FLAGS) . += span_notice("The label says it contains [LOWER_TEXT(english_list(types))] ingredients.") + if(can_lid) + if(has_lid) + . += span_notice("Its sealed with a bright orange rubber lid[!isnull(lid_assembly) ? "with an assembly attached ontop of it" : ""].") + else + . += span_notice("It can be sealed with a lid using [EXAMINE_HINT("Alt-Click")].") /** * Checks if the mob actually liked drinking this cup. @@ -141,8 +163,58 @@ return NONE /obj/item/reagent_containers/cup/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(has_lid && istype(tool, /obj/item/assembly_holder)) + if (lid_assembly) + to_chat(user, span_warning("[src]'s lid already has an assembly attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (attach_assembly(tool, user)) + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + + if (lid_assembly && istype(tool, /obj/item/stock_parts/power_store/cell)) + if (attached_cell) + to_chat(user, span_warning("[src] already has \a [attached_cell] attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (isnull(locate(/obj/item/assembly/igniter) in lid_assembly)) + to_chat(user, span_warning("[lid_assembly] doesn't have an igniter to connect [src] to!")) + return ITEM_INTERACT_BLOCKING + + if (!user.transferItemToLoc(tool, src)) + to_chat(user, span_warning("[tool] is stuck to your hand!")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You attach [tool] underneath [src]'s lid.")) + add_fingerprint(user) + log_bomber(user, "attached [tool.name] to", src) + attached_cell = tool + attached_cell.pixel_y = 0 + attached_cell.pixel_z = -4 + update_appearance() + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + return ITEM_INTERACT_SUCCESS + + if (attached_cell && istype(tool, /obj/item/stack/cable_coil)) + if (cell_wired) + to_chat(user, span_warning("[attached_cell] is already wired to [lid_assembly]!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/cable_coil/cable = tool + if (!cable.use(5)) + to_chat(user, span_warning("You need at least 5 cable pieces to wire [attached_cell]!")) + return ITEM_INTERACT_BLOCKING + + to_chat(user, span_notice("You wire [attached_cell] to [lid_assembly].")) + add_fingerprint(user) + cell_wired = TRUE + update_appearance() + return ITEM_INTERACT_SUCCESS + if(!is_open_container()) return NONE + if(istype(tool, /obj/item/food/egg)) //breaking eggs if(reagents.holder_full()) to_chat(user, span_notice("[src] is full.")) @@ -172,6 +244,125 @@ /obj/item/reagent_containers/cup/proc/on_cup_reset() drink_type = NONE +/obj/item/reagent_containers/cup/update_overlays() + . = ..() + if (has_lid) + . += mutable_appearance(icon, "[icon_state]_lid") + if (lid_assembly) + . += lid_assembly + if (attached_cell) + . += attached_cell + if (cell_wired) + . += mutable_appearance('icons/obj/machines/cell_charger.dmi', "ccharger-[attached_cell.connector_type]-on") + +// For player convinience, assume that the lids are rubber and can be pierced with a syringe +/obj/item/reagent_containers/cup/is_refillable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/is_drainable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/is_dunkable() + return ..() && !has_lid + +/obj/item/reagent_containers/cup/attack_self(mob/user) + if (!lid_assembly) + return ..() + lid_assembly.attack_self(user) + return TRUE + +/obj/item/reagent_containers/cup/click_alt(mob/user) + if (!can_lid) + return NONE + + if (cell_wired) + balloon_alert(user, "cut the wiring first!") + return CLICK_ACTION_BLOCKING + + if (attached_cell) + var/obj/item/our_cell = attached_cell + // Exited() automatically clears it + our_cell.forceMove(drop_location()) + user.put_in_hands(our_cell) + balloon_alert(user, "cell detached") + update_appearance() + return CLICK_ACTION_SUCCESS + + if (lid_assembly) + var/obj/item/our_assembly = lid_assembly + our_assembly.forceMove(drop_location()) + user.put_in_hands(our_assembly) + balloon_alert(user, "assembly detached") + update_appearance() + return CLICK_ACTION_SUCCESS + + has_lid = !has_lid + update_appearance() + balloon_alert(user, "lid [has_lid ? "sealed" : "unsealed"]") + if (has_lid) + add_container_flags(SEALED_CONTAINER) + else + reset_container_flags() + return CLICK_ACTION_SUCCESS + +/obj/item/reagent_containers/cup/wirecutter_act(mob/living/user, obj/item/tool) + if (user.combat_mode || !cell_wired) + return NONE + + new /obj/item/stack/cable_coil(drop_location(), 5) + cell_wired = FALSE + update_appearance() + balloon_alert(user, "wiring cut") + tool.play_tool_sound(src, 50) + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/proc/attach_assembly(obj/item/assembly_holder/assembly, mob/living/user) + if (!user.transferItemToLoc(assembly, src)) + to_chat(user, span_warning("[assembly] is stuck to your hand!")) + return FALSE + + to_chat(user, span_notice("You attach [assembly] to [src]'s lid.")) + add_fingerprint(user) + lid_assembly = assembly + lid_assembly.master = src + lid_assembly.pixel_y = 0 + lid_assembly.pixel_z = assembly_pixel_y + lid_assembly.on_attach() + RegisterSignal(src, COMSIG_IGNITER_ACTIVATE, PROC_REF(on_igniter_activate)) + log_bomber(user, "attached [lid_assembly.name] to", src) + update_appearance() + return TRUE + +/obj/item/reagent_containers/cup/Exited(atom/movable/gone, direction) + . = ..() + if (gone == lid_assembly) + lid_assembly = null + UnregisterSignal(src, COMSIG_IGNITER_ACTIVATE) + update_appearance() + else if (gone == attached_cell) + attached_cell = null + cell_wired = FALSE + update_appearance() + +/obj/item/reagent_containers/cup/proc/on_igniter_activate(datum/source, obj/item/assembly/igniter/igniter) + SIGNAL_HANDLER + // We've got an attached cell wired up, so we'll try to spend all of its current first + if (attached_cell && cell_wired) + var/power_spent = attached_cell.use(attached_cell.charge()) + // Power cell was rigged + if (QDELETED(src)) + return + + // We'll be nerfing plasma and welding fuel as they're very easy to get and make for boring bombs + if (power_spent > 0 && (reagents.spark_act(power_spent, SPARK_ACT_ENCLOSED | SPARK_ACT_WEAKEN_COMMON) & SPARK_ACT_DESTRUCTIVE)) + qdel(src) + return + + // Igniters heat, condensers chill + var/igniter_temp = igniter.get_temperature() + if (igniter_temp > 0) + reagents.expose_temperature(igniter_temp) + /obj/item/reagent_containers/cup/beaker name = "beaker" desc = "A beaker. It can hold up to 50 units." @@ -186,6 +377,8 @@ pickup_sound = 'sound/items/handling/beaker_pickup.ogg' drop_sound = 'sound/items/handling/beaker_place.ogg' sound_vary = TRUE + can_lid = TRUE + assembly_pixel_y = 4 /obj/item/reagent_containers/cup/beaker/Initialize(mapload) . = ..() @@ -199,6 +392,7 @@ desc = "A jar for honey. It can hold up to 50 units of sweet delight." icon = 'icons/obj/medical/chemical.dmi' icon_state = "vapour" + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/large name = "large beaker" @@ -209,6 +403,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100) fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + assembly_pixel_y = 8 /obj/item/reagent_containers/cup/beaker/plastic name = "x-large beaker" @@ -220,6 +415,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,60,120) fill_icon_thresholds = list(0, 1, 10, 20, 40, 60, 80, 100) + assembly_pixel_y = 8 /obj/item/reagent_containers/cup/beaker/meta name = "metamaterial beaker" @@ -231,6 +427,7 @@ amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,60,120,180) fill_icon_thresholds = list(0, 1, 10, 25, 35, 50, 60, 80, 100) + assembly_pixel_y = 10 /obj/item/reagent_containers/cup/beaker/noreact name = "cryostasis beaker" @@ -242,6 +439,7 @@ initial_reagent_flags = OPENCONTAINER | NO_REACT volume = 50 amount_per_transfer_from_this = 10 + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/bluespace name = "bluespace beaker" @@ -254,6 +452,7 @@ volume = 300 amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,20,25,30,50,100,300) + can_lid = FALSE /obj/item/reagent_containers/cup/beaker/meta/omnizine list_reagents = list(/datum/reagent/medicine/omnizine = 180) @@ -529,3 +728,8 @@ possible_transfer_amounts = list(5, 10, 15, 30) volume = 30 fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + can_lid = TRUE + +/obj/item/reagent_containers/cup/tube/attach_assembly(obj/item/assembly_holder/assembly, mob/living/user) + to_chat(user, span_warning("[src]'s lid is too small to fit [assembly]!")) + return FALSE diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 9557ef3a9f75..5a8e8bc5a7d5 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -11,6 +11,8 @@ possible_transfer_amounts = list(5, 10, 15, 25, 50) volume = 50 fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) + can_lid = TRUE + assembly_pixel_y = 4 /obj/item/reagent_containers/cup/bottle/Initialize(mapload) . = ..() @@ -505,6 +507,7 @@ fill_icon_thresholds = list(0, 20, 40, 60, 80, 100) possible_transfer_amounts = list(5, 10) amount_per_transfer_from_this = 5 + can_lid = FALSE /obj/item/reagent_containers/cup/bottle/syrup_bottle/Initialize(mapload) . = ..() diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm index 872a365c2473..ef2943ae4fce 100644 --- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm +++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm @@ -1043,6 +1043,14 @@ list_reagents = list(/datum/reagent/consumable/orangejuice = 100) drink_type = FRUIT | BREAKFAST +/obj/item/reagent_containers/cup/glass/bottle/juice/lemonjuice + name = "lemon juice" + desc = "Some like to pour a few drops of this over their fish." + icon = 'icons/obj/drinks/boxes.dmi' + icon_state = "lemonjuice" + list_reagents = list(/datum/reagent/consumable/lemonjuice = 100) + drink_type = FRUIT + /obj/item/reagent_containers/cup/glass/bottle/juice/cream name = "milk cream" desc = "It's cream. Made from milk. What else did you think you'd find in there?" diff --git a/code/modules/reagents/reagent_containers/cups/organ_jar.dm b/code/modules/reagents/reagent_containers/cups/organ_jar.dm index 52754f5836a7..b71b048de283 100644 --- a/code/modules/reagents/reagent_containers/cups/organ_jar.dm +++ b/code/modules/reagents/reagent_containers/cups/organ_jar.dm @@ -12,6 +12,7 @@ possible_transfer_amounts = list(20, 40, 60, 120) fill_icon_thresholds = list(0, 1, 20, 40, 60, 80, 100) w_class = WEIGHT_CLASS_SMALL // Organs are small by default, so the jar should be at least small as well + can_lid = FALSE /// The organ that is currently inside the jar var/obj/item/organ/held_organ = null /// Whether the jar is filled to capacity with formaldehyde, preserving any organ inside diff --git a/code/modules/reagents/reagent_containers/cups/soda.dm b/code/modules/reagents/reagent_containers/cups/soda.dm index a7fd8865cabd..33c984a61a85 100644 --- a/code/modules/reagents/reagent_containers/cups/soda.dm +++ b/code/modules/reagents/reagent_containers/cups/soda.dm @@ -22,6 +22,12 @@ throwforce = 12 // set to 0 upon being opened. Have you ever been domed by a soda can? Those things fucking hurt /// If the can hasn't been opened yet, this is the measure of how fizzed up it is from being shaken or thrown around. When opened, this is rolled as a percentage chance to burst var/fizziness = 0 + /// Have we been sealed with tape? And if so, what color is it? + var/tape_color = null + /// Color of our fuse, if any + var/fuse_color = null + /// Timer for our explosion + var/fuse_timer = null /obj/item/reagent_containers/cup/soda_cans/Initialize(mapload, vol) . = ..() @@ -39,7 +45,7 @@ H.visible_message(span_warning("[H] is trying to take a big sip from [src]... The can is empty!")) return SHAME if(!is_drainable()) - open_soda() + open_soda(H) sleep(1 SECONDS) H.visible_message(span_suicide("[H] takes a big sip from [src]! It looks like [H.p_theyre()] trying to commit suicide!")) playsound(H,'sound/items/drink.ogg', 80, TRUE) @@ -67,24 +73,24 @@ return TOXLOSS /obj/item/reagent_containers/cup/soda_cans/interact_with_atom(atom/target, mob/living/user, list/modifiers) - if(iscarbon(target) && !reagents.total_volume && user.combat_mode && user.zone_selected == BODY_ZONE_HEAD) - if(target == user) - user.visible_message( - span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"), - span_notice("You crush the can of [src] on your forehead."), - ) - else - user.visible_message( - span_warning("[user] crushes the can of [src] on [target]'s forehead!"), - span_notice("You crush the can of [src] on [target]'s forehead."), - ) - playsound(src, 'sound/items/weapons/pierce.ogg', rand(10, 50), TRUE) - var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(target.drop_location()) - crushed_can.icon_state = icon_state - qdel(src) - return ITEM_INTERACT_SUCCESS - - return ..() + if(!iscarbon(target) || reagents.total_volume || !user.combat_mode || user.zone_selected != BODY_ZONE_HEAD) + return ..() + + if(target == user) + user.visible_message( + span_warning("[user] crushes the can of [src] on [user.p_their()] forehead!"), + span_notice("You crush the can of [src] on your forehead."), + ) + else + user.visible_message( + span_warning("[user] crushes the can of [src] on [target]'s forehead!"), + span_notice("You crush the can of [src] on [target]'s forehead."), + ) + playsound(src, 'sound/items/weapons/pierce.ogg', rand(10, 50), TRUE) + var/obj/item/trash/can/crushed_can = new /obj/item/trash/can(target.drop_location()) + crushed_can.icon_state = icon_state + qdel(src) + return ITEM_INTERACT_SUCCESS /obj/item/reagent_containers/cup/soda_cans/bullet_act(obj/projectile/proj) . = ..() @@ -99,6 +105,14 @@ qdel(src) /obj/item/reagent_containers/cup/soda_cans/proc/open_soda(mob/user) + if(tape_color) + to_chat(user, "You rip off the tape covering [src]'s hole.") + playsound(user, 'sound/items/duct_tape/duct_tape_rip.ogg', 50, TRUE) + tape_color = null + add_container_flags(OPENCONTAINER) + update_appearance() + return + if(prob(fizziness)) user.visible_message(span_danger("[user] opens [src], and is suddenly sprayed by the fizzing contents!"), span_danger("You pull back the tab of [src], and are suddenly sprayed with a torrent of liquid! Ahhh!!")) burst_soda(user) @@ -135,9 +149,120 @@ reagents.clear_reagents() throwforce = 0 +/obj/item/reagent_containers/cup/soda_cans/wirecutter_act(mob/living/user, obj/item/tool) + if (!fuse_color) + return NONE + to_chat(user, span_notice("You snip [src]'s fuse off.")) + tool.play_tool_sound(src, 50) + add_fingerprint(user) + fuse_color = null + if (!isnull(fuse_timer)) + deltimer(fuse_timer) + fuse_timer = null + log_bomber(user, "has disarmed", src) + if (heatable) + AddElement(/datum/element/reagents_item_heatable) + update_appearance() + +/obj/item/reagent_containers/cup/soda_cans/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if (istype(tool, /obj/item/stack/cable_coil)) + if (fuse_color) + to_chat(user, span_warning("[src] already has a fuse attached to it!")) + return ITEM_INTERACT_BLOCKING + + if (tape_color) + to_chat(user, span_warning("[src]'s hole is covered up with tape!")) + return ITEM_INTERACT_BLOCKING + + if (!is_drainable()) + to_chat(user, span_warning("[src] hasn't been opened yet!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/cable_coil/coil = tool + var/coil_color = GLOB.cable_colors[coil.cable_color] + add_fingerprint(user) + if (!coil.use(1)) + return ITEM_INTERACT_BLOCKING + + fuse_color = coil_color + // Heating replaced with lighting the fuse + RemoveElement(/datum/element/reagents_item_heatable) + to_chat(user, span_notice("You attach a fuse to [src].")) + log_bomber(user, "attached a fuse to", src) + update_appearance() + return ITEM_INTERACT_SUCCESS + + if (istype(tool, /obj/item/stack/sticky_tape)) + if (tape_color) + to_chat(user, span_warning("[src]'s hole is already covered up with tape!")) + return ITEM_INTERACT_BLOCKING + + if (!is_drainable()) + to_chat(user, span_warning("[src] hasn't been opened yet!")) + return ITEM_INTERACT_BLOCKING + + var/obj/item/stack/sticky_tape/tape = tool + var/list/tape_colors = SSgreyscale.ParseColorString(tape.greyscale_colors) + add_fingerprint(user) + if (!tape.use(1)) + return ITEM_INTERACT_BLOCKING + + tape_color = tape_colors[1] + to_chat(user, span_notice("You wrap [src] up in [tape].")) + reset_container_flags() + update_appearance() + return ITEM_INTERACT_SUCCESS + + if (!fuse_color || tool.get_temperature() < FIRE_MINIMUM_TEMPERATURE_TO_EXIST) + return ..() + + if (fuse_timer) + to_chat(user, span_warning("[src] is already lit!")) + return ITEM_INTERACT_BLOCKING + + add_fingerprint(user) + log_bomber(user, "has primed a rigged", src) + to_chat(user, span_warning("You light [src]'s fuse!")) + fuse_timer = addtimer(CALLBACK(src, PROC_REF(try_detonate)), rand(2 SECONDS, 4 SECONDS)) + update_appearance() + return ITEM_INTERACT_SUCCESS + +/obj/item/reagent_containers/cup/soda_cans/proc/try_detonate() + var/spark_flags = SPARK_ACT_WEAKEN_COMMON + if (tape_color) + spark_flags |= SPARK_ACT_ENCLOSED + + playsound(src, 'sound/effects/sparks/sparks1.ogg', 50, TRUE) + if (reagents.spark_act(0, spark_flags) & SPARK_ACT_DESTRUCTIVE) + qdel(src) + return + + // Was a dud + fuse_color = null + tape_color = null + add_container_flags(OPENCONTAINER) + if (heatable) + AddElement(/datum/element/reagents_item_heatable) + update_appearance() + +/obj/item/reagent_containers/cup/soda_cans/update_overlays() + . = ..() + if (fuse_color) + var/mutable_appearance/fuse_overlay = mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_fuse") + fuse_overlay.color = fuse_color + . += fuse_overlay + + if (tape_color) + var/mutable_appearance/tape_overlay = mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_tape") + tape_overlay.color = tape_color + . += tape_overlay + + if (fuse_timer) + . += mutable_appearance('icons/obj/weapons/grenade.dmi', "improvised_grenade_active") + /obj/item/reagent_containers/cup/soda_cans/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) . = ..() - if(. || is_open_container() || !reagents.total_volume) // if it was caught, already opened, or has nothing in it + if(. || is_open_container() || !reagents.total_volume || tape_color) // if it was caught, already opened, or has nothing in it return fizziness += SODA_FIZZINESS_THROWN @@ -152,9 +277,14 @@ QDEL_IN(src, 1 SECONDS) // give it a second so it can still be logged for the throw impact /obj/item/reagent_containers/cup/soda_cans/attack_self(mob/user) + if(fuse_timer) + balloon_alert(user, "the fuse is on fire!") + return + if(!is_drainable()) open_soda(user) return + return ..() /obj/item/reagent_containers/cup/soda_cans/attack_self_secondary(mob/user) diff --git a/code/modules/reagents/reagent_containers/inhaler.dm b/code/modules/reagents/reagent_containers/inhaler.dm index 181a7e1d6e1c..8a8faa72bae3 100644 --- a/code/modules/reagents/reagent_containers/inhaler.dm +++ b/code/modules/reagents/reagent_containers/inhaler.dm @@ -260,21 +260,11 @@ /obj/item/reagent_containers/inhaler_canister/handle_deconstruct(disassembled) if (!reagents?.total_volume) - return ..() - - var/datum/reagents/smoke_reagents = new/datum/reagents() // Lets be safe first, our own reagents may be qdelled if we get deleted - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/smoke = new() - smoke_reagents.my_atom = src - for (var/datum/reagent/reagent as anything in reagents.reagent_list) - smoke_reagents.add_reagent(reagent.type, reagent.volume, added_purity = reagent.purity) - reagents.remove_reagent(reagent.type, reagent.volume) - if (smoke_reagents.reagent_list) - smoke.set_up(1, holder = src, location = get_turf(src), carry = smoke_reagents) - smoke.start(log = TRUE) - visible_message(span_warning("[src] breaks open and sprays its aerosilized contents everywhere!")) - else visible_message(span_warning("[src] breaks open - but is empty!")) + return ..() + do_chem_smoke(1, src, get_turf(src), carry = reagents, log = TRUE) + visible_message(span_warning("[src] breaks open and sprays its aerosilized contents everywhere!")) return ..() /obj/item/inhaler/medical diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 60af82540d22..b052473d55f8 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -211,11 +211,8 @@ return FALSE /obj/structure/reagent_dispensers/proc/knock_down() - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new () var/range = reagents.total_volume / REAGENT_SPILL_DIVISOR - smoke.attach(drop_location()) - smoke.set_up(round(range), holder = drop_location(), location = drop_location(), carry = reagents, silent = FALSE) - smoke.start(log = TRUE) + do_chem_smoke(round(range), drop_location(), drop_location(), carry = reagents, silent = FALSE, log = TRUE) reagents.clear_reagents() qdel(src) diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm index 8930450f2267..5d1cc61f92c8 100644 --- a/code/modules/religion/burdened/psyker.dm +++ b/code/modules/religion/burdened/psyker.dm @@ -40,9 +40,13 @@ limb_id = BODYPART_ID_PSYKER is_dimorphic = FALSE should_draw_greyscale = FALSE - bodypart_traits = list(TRAIT_DISFIGURED, TRAIT_BALD, TRAIT_SHAVED) + bodypart_traits = list(TRAIT_BALD, TRAIT_SHAVED) head_flags = HEAD_DEBRAIN | HEAD_NO_DISFIGURE // ignore disfigurement by damage, as we're always disfigured +/obj/item/bodypart/head/psyker/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_DISFIGURED, INNATE_TRAIT) + /// flavorful variant of psykerizing that deals damage and sends messages before calling psykerize() /mob/living/carbon/human/proc/slow_psykerize(blind_them = FALSE) if(stat == DEAD || !get_bodypart(BODY_ZONE_HEAD) || istype(get_bodypart(BODY_ZONE_HEAD), /obj/item/bodypart/head/psyker)) @@ -177,28 +181,10 @@ /obj/item/gun/ballistic/revolver/chaplain/Initialize(mapload) . = ..() - AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) - AddComponent(/datum/component/effect_remover, \ - success_feedback = "You disrupt the magic of %THEEFFECT with %THEWEAPON.", \ - success_forcesay = "BEGONE FOUL MAGIKS!!", \ - tip_text = "Clear rune", \ - on_clear_callback = CALLBACK(src, PROC_REF(on_cult_rune_removed)), \ - effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \ - ) - AddElement(/datum/element/bane, mob_biotypes = MOB_SPIRIT, damage_multiplier = 0, added_damage = 25) + AddElement(/datum/element/nullrod_core, FALSE) name = pick(possible_names) desc = possible_names[name] -/obj/item/gun/ballistic/revolver/chaplain/proc/on_cult_rune_removed(obj/effect/target, mob/living/user) - SIGNAL_HANDLER - if(!istype(target, /obj/effect/rune)) - return - - var/obj/effect/rune/target_rune = target - if(target_rune.log_when_erased) - user.log_message("erased [target_rune.cultist_name] rune using [src]", LOG_GAME) - SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_NARNAR] = TRUE - /obj/item/gun/ballistic/revolver/chaplain/suicide_act(mob/living/user) . = ..() name = "Habemus Papam" diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm index 1d7b54c080ab..441a1108e8dc 100644 --- a/code/modules/research/anomaly/anomaly_core.dm +++ b/code/modules/research/anomaly/anomaly_core.dm @@ -100,3 +100,6 @@ desc = "The neutralized core of a weather anomaly. The sound of thunder can be heard in the distance. It'd probably be valuable for research." icon_state = "weather_core" anomaly_type = /obj/effect/anomaly/weather + + /// Used in weather towers to track uses before depleting + var/charges = 8 diff --git a/code/modules/research/designs/autolathe/multi-department_designs.dm b/code/modules/research/designs/autolathe/multi-department_designs.dm index bb7761c86fa1..bb6a1d478407 100644 --- a/code/modules/research/designs/autolathe/multi-department_designs.dm +++ b/code/modules/research/designs/autolathe/multi-department_designs.dm @@ -40,7 +40,10 @@ name = "Multitool" id = "multitool" build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT*0.5, /datum/material/glass =SMALL_MATERIAL_AMOUNT*0.2) + materials = list( + /datum/material/iron =SMALL_MATERIAL_AMOUNT * 0.5, + /datum/material/glass =SMALL_MATERIAL_AMOUNT * 0.2 + ) build_path = /obj/item/multitool category = list( RND_CATEGORY_INITIAL, diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 6f7e8fc0d4e4..457b4cca52a4 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -233,9 +233,7 @@ use_energy(750 JOULES) /obj/machinery/rnd/experimentor/proc/throwSmoke(turf/where) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = where) - smoke.start() + do_smoke(0, src, where) /obj/machinery/rnd/experimentor/proc/experiment(exp,obj/item/exp_on) recentlyExperimented = 1 @@ -307,27 +305,15 @@ else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01)) visible_message(span_danger("[src] destroys [exp_on], leaking dangerous gas!")) chosenchem = pick(/datum/reagent/carbon,/datum/reagent/uranium/radium,/datum/reagent/toxin,/datum/reagent/consumable/condensedcapsaicin,/datum/reagent/drug/mushroomhallucinogen,/datum/reagent/drug/space_drugs,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/ethanol/beepsky_smash) - var/datum/reagents/tmp_holder = new/datum/reagents(50) - tmp_holder.my_atom = src - tmp_holder.add_reagent(chosenchem , 50) + do_chem_smoke(0, src, loc, chosenchem, 50) investigate_log("Experimentor has released [chosenchem] smoke.", INVESTIGATE_EXPERIMENTOR) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - smoke.set_up(0, holder = src, location = src, carry = tmp_holder, silent = TRUE) playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3) - smoke.start() - qdel(tmp_holder) ejectItem(TRUE) else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01)) visible_message(span_danger("[src]'s chemical chamber has sprung a leak!")) chosenchem = pick(/datum/reagent/mutationtoxin/classic,/datum/reagent/cyborg_mutation_nanomachines,/datum/reagent/toxin/acid) - var/datum/reagents/tmp_holder = new/datum/reagents(50) - tmp_holder.my_atom = src - tmp_holder.add_reagent(chosenchem , 50) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - smoke.set_up(0, holder = src, location = src, carry = tmp_holder, silent = TRUE) + do_chem_smoke(0, src, loc, chosenchem, 50) playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3) - smoke.start() - qdel(tmp_holder) ejectItem(TRUE) warn_admins(usr, "[chosenchem] smoke") investigate_log("Experimentor has released [chosenchem] smoke!", INVESTIGATE_EXPERIMENTOR) @@ -398,15 +384,9 @@ investigate_log("Experimentor has made a cup of [chosenchem] coffee.", INVESTIGATE_EXPERIMENTOR) else if(prob(EFFECT_PROB_VERYLOW * (100 - malfunction_probability_coeff) * 0.01)) visible_message(span_danger("[src] malfunctions, shattering [exp_on] and releasing a dangerous cloud of coolant!")) - var/datum/reagents/tmp_holder = new/datum/reagents(50) - tmp_holder.my_atom = src - tmp_holder.add_reagent(/datum/reagent/consumable/frostoil, 50) + do_chem_smoke(0, src, loc, /datum/reagent/consumable/frostoil, 50) investigate_log("Experimentor has released frostoil gas.", INVESTIGATE_EXPERIMENTOR) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - smoke.set_up(0, holder = src, location = src, carry = tmp_holder, silent = TRUE) playsound(src, 'sound/effects/smoke.ogg', 50, TRUE, -3) - smoke.start() - qdel(tmp_holder) ejectItem(TRUE) else if(prob(EFFECT_PROB_LOW * (100 - malfunction_probability_coeff) * 0.01)) visible_message(span_warning("[src] malfunctions, shattering [exp_on] and leaking cold air!")) @@ -419,9 +399,7 @@ ejectItem(TRUE) else if(prob(EFFECT_PROB_MEDIUM * (100 - malfunction_probability_coeff) * 0.01)) visible_message(span_warning("[src] malfunctions, releasing a flurry of chilly air as [exp_on] pops out!")) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = loc) - smoke.start() + do_smoke(0, src, loc) ejectItem() //////////////////////////////////////////////////////////////////////////////////////////////// if(exp == SCANTYPE_OBLITERATE) @@ -571,19 +549,11 @@ COOLDOWN_DECLARE(cooldown) //What visual theme this artefact has. Current possible choices: "prototype", "necrotech" var/artifact_theme = "prototype" - var/datum/effect_system/spark_spread/sparks /obj/item/relic/Initialize(mapload) . = ..() - sparks = new() - sparks.set_up(5, 1, src) - sparks.attach(src) random_themed_appearance() -/obj/item/relic/Destroy(force) - QDEL_NULL(sparks) - . = ..() - /obj/item/relic/proc/random_themed_appearance() var/themed_name_prefix var/themed_name_suffix @@ -642,9 +612,7 @@ call(src, hidden_power)(user) /obj/item/relic/proc/throw_smoke(turf/where) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = get_turf(where)) - smoke.start() + do_smoke(0, src, get_turf(where)) // Artefact Powers \\ @@ -740,7 +708,7 @@ /obj/item/relic/proc/drink_dispenser(mob/user) var/obj/item/reagent_containers/cup/glass/drinkingglass/freebie = new(get_step_rand(user)) playsound(freebie, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - sparks.start() + do_sparks(5, TRUE, src, src) addtimer(CALLBACK(src, PROC_REF(dispense_drink), freebie), 0.5 SECONDS) /obj/item/relic/proc/dispense_drink(obj/item/reagent_containers/cup/glass/glasser) diff --git a/code/modules/research/xenobiology/crossbreeding/burning.dm b/code/modules/research/xenobiology/crossbreeding/burning.dm index 8ccfc9c97bbc..a8a686d7a180 100644 --- a/code/modules/research/xenobiology/crossbreeding/burning.dm +++ b/code/modules/research/xenobiology/crossbreeding/burning.dm @@ -45,12 +45,7 @@ Burning extracts: /obj/item/slimecross/burning/orange/do_effect(mob/user) user.visible_message(span_danger("[src] boils over with a caustic gas!")) - var/datum/reagents/tmp_holder = new/datum/reagents(100) - tmp_holder.add_reagent(/datum/reagent/consumable/condensedcapsaicin, 100) - - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - smoke.set_up(7, holder = src, location = get_turf(user), carry = tmp_holder) - smoke.start(log = TRUE) + do_chem_smoke(7, user, get_turf(user), /datum/reagent/consumable/condensedcapsaicin, 100, log = TRUE) ..() /obj/item/slimecross/burning/purple @@ -120,12 +115,8 @@ Burning extracts: /obj/item/slimecross/burning/darkblue/do_effect(mob/user) user.visible_message(span_danger("[src] releases a burst of chilling smoke!")) - var/datum/reagents/tmp_holder = new/datum/reagents(100) - tmp_holder.add_reagent(/datum/reagent/consumable/frostoil, 40) user.reagents.add_reagent(/datum/reagent/medicine/regen_jelly, 10) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new - smoke.set_up(7, holder = src, location = get_turf(user), carry = tmp_holder) - smoke.start(log = TRUE) + do_chem_smoke(7, user, get_turf(user), /datum/reagent/consumable/frostoil, 40, log = TRUE) ..() /obj/item/slimecross/burning/silver diff --git a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm index 4a564b58b130..0b73e2351406 100644 --- a/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm +++ b/code/modules/research/xenobiology/vatgrowing/samples/_micro_organism.dm @@ -97,9 +97,7 @@ new /obj/effect/gibspawner/generic(get_turf(vat)) //Spawn some gibs. /datum/micro_organism/cell_line/proc/succeed_growing(obj/machinery/vatgrower/vat) - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = vat, location = vat.loc) - smoke.start() + do_smoke(0, vat, vat.loc) for(var/x in 1 to resulting_atom_count) var/atom/thing = new resulting_atom(get_turf(vat)) ADD_TRAIT(thing, TRAIT_VATGROWN, "vatgrowing") diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 5e38d9a18a01..65fcb3ffc388 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -254,7 +254,7 @@ ///Places one monkey, if possible /obj/machinery/computer/camera_advanced/xenobio/proc/feed_slime(mob/living/user, turf/open/target_turf) if(stored_monkeys < 1) - to_chat(user, span_warning("[src] needs to have at least 1 monkey stored. Currently has [stored_monkeys] stored_monkeys stored.")) + to_chat(user, span_warning("[src] needs to have at least 1 monkey stored. Currently has [stored_monkeys] monkeys stored.")) target_turf.balloon_alert(user, "not enough monkeys") return diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 90e30ea03f8a..1f5413a09edc 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -363,9 +363,7 @@ if(sparks_amt) do_sparks(sparks_amt, FALSE, get_turf(owner)) if(ispath(smoke_type, /datum/effect_system/fluid_spread/smoke)) - var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_type() - smoke.set_up(smoke_amt, holder = owner, location = get_turf(owner)) - smoke.start() + do_smoke(smoke_amt, owner, get_turf(owner)) // Send signals last in case they delete the spell SEND_SIGNAL(owner, COMSIG_MOB_AFTER_SPELL_CAST, src, cast_on) diff --git a/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm b/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm index 58fcbb6ff503..96cf2a46cdf6 100644 --- a/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm +++ b/code/modules/spells/spell_types/jaunt/ethereal_jaunt.dm @@ -165,8 +165,7 @@ /// Does some steam effects from the jaunt at passed loc. /datum/action/cooldown/spell/jaunt/ethereal_jaunt/proc/do_steam_effects(turf/loc) - var/datum/effect_system/steam_spread/steam = new() - steam.set_up(10, FALSE, loc) + var/datum/effect_system/basic/steam_spread/steam = new(loc, 10, FALSE) steam.start() diff --git a/code/modules/spells/spell_types/pointed/swap.dm b/code/modules/spells/spell_types/pointed/swap.dm index 57b74fba0562..d8690754a62b 100644 --- a/code/modules/spells/spell_types/pointed/swap.dm +++ b/code/modules/spells/spell_types/pointed/swap.dm @@ -38,7 +38,7 @@ /datum/action/cooldown/spell/pointed/swap/InterceptClickOn(mob/living/clicker, params, atom/target) if(!LAZYACCESS(params2list(params), RIGHT_CLICK)) return ..() - + if(!IsAvailable(feedback = TRUE)) return FALSE if(!target) @@ -69,16 +69,13 @@ to_chat(cast_on, span_userdanger("You feel space bending.")) if(ispath(smoke_type, /datum/effect_system/fluid_spread/smoke)) - var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_type() - smoke.set_up(smoke_amt, holder = owner, location = get_turf(owner)) - smoke.start() + do_smoke(smoke_amt, owner, get_turf(owner), smoke_type = smoke_type) + var/turf/target_location = get_turf(cast_on) if(!isnull(second_target) && get_dist(owner, second_target) <= cast_range && !(cast_on == second_target)) to_chat(second_target, span_userdanger("You feel space bending.")) if(ispath(smoke_type, /datum/effect_system/fluid_spread/smoke)) - var/datum/effect_system/fluid_spread/smoke/smoke = new smoke_type() - smoke.set_up(smoke_amt, holder = owner, location = get_turf(second_target)) - smoke.start() + do_smoke(smoke_amt, owner, get_turf(second_target)) var/turf/second_location = get_turf(second_target) do_teleport(second_target, owner.loc, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) do_teleport(cast_on, second_location, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC) diff --git a/code/modules/spells/spell_types/projectile/juggernaut.dm b/code/modules/spells/spell_types/projectile/juggernaut.dm index 151b5cedc854..5958fa2ed94a 100644 --- a/code/modules/spells/spell_types/projectile/juggernaut.dm +++ b/code/modules/spells/spell_types/projectile/juggernaut.dm @@ -15,5 +15,5 @@ /datum/action/cooldown/spell/basic_projectile/juggernaut/fire_projectile(atom/target, mob/caster) var/obj/projectile/magic/aoe/juggernaut/to_fire = ..() - to_fire.ignored_factions = caster.get_faction() + SET_FACTION_AND_ALLIES_FROM(to_fire, caster) return to_fire diff --git a/code/modules/spells/spell_types/shapeshift/_shape_status.dm b/code/modules/spells/spell_types/shapeshift/_shape_status.dm index 5943a704c9bb..5aa380602071 100644 --- a/code/modules/spells/spell_types/shapeshift/_shape_status.dm +++ b/code/modules/spells/spell_types/shapeshift/_shape_status.dm @@ -178,6 +178,8 @@ // Only transfer blood if both mobs are supposed to have a blood volume if (CAN_HAVE_BLOOD(owner) && CAN_HAVE_BLOOD(caster_mob)) owner.set_blood_volume(caster_mob.get_blood_volume()) + owner.maxbloodpool = caster_mob.maxbloodpool // DARKPACK EDIT ADD + owner.set_blood_pool(caster_mob.bloodpool) // DARKPACK EDIT ADD for(var/datum/action/bodybound_action as anything in caster_mob.actions) if(bodybound_action.target != caster_mob) @@ -218,6 +220,7 @@ // Only transfer blood if both mobs are supposed to have a blood volume if (CAN_HAVE_BLOOD(owner) && CAN_HAVE_BLOOD(caster_mob)) caster_mob.set_blood_volume(owner.get_blood_volume()) + caster_mob.set_blood_pool(owner.bloodpool) // DARKPACK EDIT ADD /datum/status_effect/shapechange_mob/from_spell/on_shape_death(datum/source, gibbed) var/datum/action/cooldown/spell/shapeshift/source_spell = source_weakref.resolve() diff --git a/code/modules/spells/spell_types/summon/servant.dm b/code/modules/spells/spell_types/summon/servant.dm index 63f30e913a2a..12ad4de5775c 100644 --- a/code/modules/spells/spell_types/summon/servant.dm +++ b/code/modules/spells/spell_types/summon/servant.dm @@ -79,7 +79,7 @@ spawn_location.visible_message(span_userdanger("A Magical [servant_title] appears in a cloud of smoke!")) var/mob/living/carbon/human/human_servant = new(spawn_location) human_servant.equipOutfit(/datum/outfit/butler) - do_smoke(0, holder = src, location = spawn_location) + do_smoke(0, src, spawn_location) human_servant.PossessByPlayer(chosen_one.key) summon_weakref = WEAKREF(human_servant) diff --git a/code/modules/sprite_editing/flood_fill.dm b/code/modules/sprite_editing/flood_fill.dm new file mode 100644 index 000000000000..5e1b295dd326 --- /dev/null +++ b/code/modules/sprite_editing/flood_fill.dm @@ -0,0 +1,53 @@ +//a macro for the stringized key for coordinates to check later +#define CANVAS_COORD(x, y) "[x]:[y]" +#define IS_IN_BOUNDS(x, y) (x > 0 && x <= width && y > 0 && y <= height) +#define COLORS_ARE_EQUAL(a, b) ((a == b) || (endswith(a, "00") && endswith(b, "00"))) + +#define SHOULD_ADD_POINT(x, y) (!coord_cache[CANVAS_COORD(x, y)] && IS_IN_BOUNDS(x, y) && COLORS_ARE_EQUAL(grid[y][x], target_color)) + +#define ADD_POINT(x, y) \ + points += list(list((x) - 1, (y) - 1, target_color));\ + coord_cache[CANVAS_COORD(x, y)] = TRUE + +/proc/flood_fill(list/grid, x, y, width, height) + var/target_color = grid[y][x] + var/list/coord_cache = list() + var/list/points = list() + var/list/coord_queue = list(x, x, y, 1, x, x, y-1, -1) + var/span_start + var/column + var/span_end + var/row + var/row_shift + while(length(coord_queue)) + span_start = coord_queue[1] + column = span_start + span_end = coord_queue[2] + row = coord_queue[3] + row_shift = coord_queue[4] + coord_queue.Cut(1, 5) + if(SHOULD_ADD_POINT(column, row)) + while(SHOULD_ADD_POINT(column - 1, row)) + ADD_POINT(column - 1, row) + column-- + if(column < span_start) + coord_queue += list(column, span_start - 1, row - row_shift, -row_shift) + while(span_start <= span_end) + while(SHOULD_ADD_POINT(span_start, row)) + ADD_POINT(span_start, row) + span_start++ + if(span_start > column) + coord_queue += list(column, span_start - 1, row + row_shift, row_shift) + if(span_start - 1 > span_end) + coord_queue += list(span_end + 1, span_start - 1, row - row_shift, -row_shift) + span_start++ + while(span_start < span_end && !SHOULD_ADD_POINT(span_start, row)) + span_start++ + column = span_start + return points + +#undef ADD_POINT +#undef SHOULD_ADD_POINT +#undef COLORS_ARE_EQUAL +#undef IS_IN_BOUNDS +#undef CANVAS_COORD diff --git a/code/modules/sprite_editing/workspace.dm b/code/modules/sprite_editing/workspace.dm new file mode 100644 index 000000000000..9871c342fd1c --- /dev/null +++ b/code/modules/sprite_editing/workspace.dm @@ -0,0 +1,302 @@ +/datum/sprite_editor_workspace + var/width + var/height + var/dirs + var/backdrop + + var/color_mode = SPRITE_EDITOR_COLOR_MODE_RGBA + /// A bitfield specifying whether certain functions of the sprite editor should be performed if the corresponding ui actions are received - used to prevent href exploitation + var/config_flags = ALL + /// A bitfield specifying what tools we are allowed to use in the sprite editor + var/tool_flags = ALL + + var/list/layers + var/list/undo_stack = list() + var/list/undo_names = list() + var/list/redo_stack = list() + var/list/redo_names = list() + +/datum/sprite_editor_workspace/New( + width = 32, + height = 32, + dirs = 1, + backdrop = null, + color_mode = SPRITE_EDITOR_COLOR_MODE_RGBA, + config_flags = ALL, + tool_flags = ALL, + initial_layer_color = null) + . = ..() + src.width = width + src.height = height + src.dirs = dirs + src.color_mode = color_mode + src.config_flags = config_flags + src.tool_flags = tool_flags + src.backdrop = backdrop + layers = list(list("name" = "Background", visible = TRUE, "data" = create_layer_data(initial_layer_color))) + +/datum/sprite_editor_workspace/proc/copy(preserve_history = FALSE) + var/datum/sprite_editor_workspace/new_workspace = new(width, height, dirs, color_mode, config_flags, tool_flags) + new_workspace.layers = deep_copy_list_alt(layers) + if(preserve_history) + new_workspace.undo_names = undo_names.Copy() + new_workspace.undo_stack = deep_copy_list_alt(undo_stack) + new_workspace.redo_names = redo_names.Copy() + new_workspace.redo_stack = deep_copy_list_alt(redo_stack) + return new_workspace + +/datum/sprite_editor_workspace/proc/create_layer_data(color = "#00000000") + var/list/out = list() + for(var/i in 1 to dirs) + var/list/layer = list() + for(var/y in 1 to height) + var/list/row = list() + for(var/x in 1 to width) + row += color + layer += list(row) + out["[GLOB.alldirs_dmi_order[i]]"] = layer + return out + +/** + * Take a new transaction, perform it, and optionally add it to the undo history. + * Returns TRUE if the transaction was valid. + */ +/datum/sprite_editor_workspace/proc/new_transaction(transaction) + if(!can_transact(transaction)) + return + preprocess_new_transaction(transaction) + transact(transaction) + if(!(config_flags & SPRITE_EDITOR_ALLOW_UNDO)) + return TRUE + redo_stack.Cut() + redo_names.Cut() + undo_stack += list(transaction) + undo_names += transaction["name"] + return TRUE + +/datum/sprite_editor_workspace/proc/undo() + if(!(config_flags & SPRITE_EDITOR_ALLOW_UNDO)) + return + if(length(undo_stack)) + pop(undo_names) + var/transaction = pop(undo_stack) + reverse_transact(transaction) + redo_stack += list(transaction) + redo_names += transaction["name"] + +/datum/sprite_editor_workspace/proc/redo() + if(!(config_flags & SPRITE_EDITOR_ALLOW_UNDO)) + return + if(length(redo_stack)) + pop(redo_names) + var/transaction = pop(redo_stack) + transact(transaction) + undo_stack += list(transaction) + undo_names += transaction["name"] + +/datum/sprite_editor_workspace/proc/toggle_layer_visible(layer) + if(!(config_flags & SPRITE_EDITOR_ALLOW_LAYERS)) + return + if(!isnum(layer)) + return + if(layer < 1 || layer > length(layers)) + return + layers[layer]["visible"] = !layers[layer]["visible"] + +/datum/sprite_editor_workspace/proc/is_valid_color(color) + if(SEND_SIGNAL(src, COMSIG_SPRITE_EDITOR_VALIDATE_COLOR, color)) + return FALSE + var/list/rgb_color = split_color(color) + switch(color_mode) + if(SPRITE_EDITOR_COLOR_MODE_RGBA) + return TRUE + if(SPRITE_EDITOR_COLOR_MODE_RGB) + return rgb_color[4] == 255 + if(SPRITE_EDITOR_COLOR_MODE_GREYSCALE) + return rgb_color[1] == rgb_color[2] && rgb_color[2] == rgb_color[3] + else + return TRUE + +/datum/sprite_editor_workspace/proc/can_transact(list/transaction) + switch(transaction["type"]) + if("pencil") + return tool_flags & SPRITE_EDITOR_TOOL_PENCIL && is_valid_color(transaction["color"]) + if("eraser") + return tool_flags & SPRITE_EDITOR_TOOL_ERASER + if("bucket") + return tool_flags & SPRITE_EDITOR_TOOL_BUCKET && is_valid_color(transaction["color"]) + if("renameLayer", "moveLayerUp", "moveLayerDown", "flattenLayer", "addLayer", "deleteLayer") + return config_flags & SPRITE_EDITOR_ALLOW_LAYERS + else // Invalid transaction type, probably from href exploitation + return FALSE + +/datum/sprite_editor_workspace/proc/preprocess_new_transaction(list/transaction) + switch(transaction["type"]) + if("pencil", "eraser") + var/layer = transaction["layer"] + var/dir = transaction["dir"] + var/list/points = transaction["points"] + var/list/affected_frame = layers[layer]["data"][dir] + for(var/point in points) + var/x = point[1]+1 + var/y = point[2]+1 + point += affected_frame[y][x] + if("bucket") + var/layer = transaction["layer"] + var/dir = transaction["dir"] + var/list/affected_frame = layers[layer]["data"][dir] + var/list/point = transaction["point"] + var/x = point[1]+1 + var/y = point[2]+1 + transaction["points"] = flood_fill(affected_frame, x, y, width, height) + transaction -= "point" + if("flattenLayer") + var/layer = transaction["layer"] + var/list/top_layer = layers[layer] + var/list/bottom_layer = layers[layer-1] + transaction["oldTop"] = top_layer + transaction["oldBottom"] = deep_copy_list(bottom_layer) + if("deleteLayer") + var/layer = transaction["layer"] + var/list/old_layer = layers[layer] + transaction["oldLayer"] = old_layer + +/datum/sprite_editor_workspace/proc/transact(list/transaction) + switch(transaction["type"]) + if("pencil", "bucket") + var/layer = transaction["layer"] + var/dir = transaction["dir"] + var/color = transaction["color"] + var/list/points = transaction["points"] + var/list/affected_frame = layers[layer]["data"][dir] + for(var/list/point in points) + var/x = point[1]+1 + var/y = point[2]+1 + affected_frame[y][x] = blend_color(affected_frame[y][x], color) + if("eraser") + var/layer = transaction["layer"] + var/dir = transaction["dir"] + var/list/points = transaction["points"] + var/list/affected_frame = layers[layer]["data"][dir] + for(var/list/point in points) + var/x = point[1]+1 + var/y = point[2]+1 + affected_frame[y][x] = "#00000000" + if("renameLayer") + var/layer = transaction["layer"] + var/new_name = transaction["newName"] + layers[layer]["name"] = new_name + if("moveLayerUp") + var/layer = transaction["layer"] + layers.Swap(layer, layer+1) + if("moveLayerDown") + var/layer = transaction["layer"] + layers.Swap(layer, layer-1) + if("flattenLayer") + var/layer = transaction["layer"] + var/list/top_layer = layers[layer] + var/list/bottom_layer = layers[layer-1] + for(var/dir in 1 to dirs) + for(var/y in 1 to height) + for(var/x in 1 to width) + bottom_layer["[dir]"][y][x] = blend_color(bottom_layer["[dir]"][y][x], top_layer["[dir]"][y][x]) + layers.Cut(layer, layer+1) + if("addLayer") + layers += list(list("name" = "New Layer", "visible" = TRUE, "data" = create_layer_data())) + if("deleteLayer") + var/layer = transaction["layer"] + layers.Cut(layer, layer+1) + +/datum/sprite_editor_workspace/proc/reverse_transact(list/transaction) + switch(transaction["type"]) + if("pencil", "eraser", "bucket") + var/layer = transaction["layer"] + var/dir = transaction["dir"] + var/list/points = transaction["points"] + var/list/affected_frame = layers[layer]["data"][dir] + for(var/list/point in points) + var/x = point[1]+1 + var/y = point[2]+1 + affected_frame[y][x] = point[3] + if("renameLayer") + var/layer = transaction["layer"] + var/old_name = transaction["oldName"] + layers[layer]["name"] = old_name + if("moveLayerUp") + var/layer = transaction["layer"] + layers.Swap(layer, layer+1) + if("moveLayerDown") + var/layer = transaction["layer"] + layers.Swap(layer, layer-1) + if("flattenLayer") + var/layer = transaction["layer"] + var/top_layer = transaction["oldTop"] + var/bottom_layer = transaction["oldBottom"] + var/bottom_layer_index = layer-1 + var/old_visibility = layers[bottom_layer_index]["visible"] + layers[bottom_layer_index] = bottom_layer + layers[bottom_layer_index]["visible"] = old_visibility + layers.Insert(layer, top_layer) + if("addLayer") + pop(layers) + if("deleteLayer") + var/layer = transaction["layer"] + var/old_layer = transaction["oldLayer"] + layers.Insert(layer, old_layer) + +/datum/sprite_editor_workspace/proc/sprite_editor_ui_data() + return list( + "colorMode" = color_mode, + "toolFlags" = tool_flags, + "undoStack" = undo_names, + "redoStack" = redo_names, + "sprite" = list( + "width" = width, + "height" = height, + "dirs" = dirs, + "backdrop" = backdrop, + "layers" = layers, + ) + ) + +/// Get a reference to the pixel data for the first layer of the given dir +/datum/sprite_editor_workspace/proc/get_first_layer_pixel_data(dir = SOUTH) + return layers[1]["data"]["[dir]"] + +/datum/sprite_editor_workspace/proc/to_icon() + var/metadata = json_encode(list( + "width" = width, + "height" = height, + "states" = list(list( + "name" = "", + "dirs" = dirs, + )), + )) + var/list/ideal_dims = calculate_optimal_icon_grid_dimensions(width, height, dirs) + var/grid_width = ideal_dims[1] + var/grid_height = ideal_dims[2] + var/file_width = width * grid_width + var/file_height = height * grid_height + var/layer_count = length(layers) + var/temp_file_prefix = copytext(REF(src), 2, -1) + for(var/i in 1 to layer_count) + var/list/layer_frames = list() + for(var/dir_index in 1 to dirs) + layer_frames += list(layers[i]["data"]["[GLOB.alldirs_dmi_order[dir_index]]"]) + var/pixels = reorder_pixels(width, height, grid_width, grid_height, layer_frames) + var/temp_path = "tmp/[temp_file_prefix]_layer[i].dmi" + var/result = rustg_dmi_create_png(temp_path, "[file_width]", "[file_height]", pixels) + if(result) + stack_trace(result) + return TRUE + result = rustg_dmi_inject_metadata(temp_path, metadata) + if(result) + stack_trace(result) + return TRUE + var/datum/universal_icon/out_icon = uni_icon("tmp/[temp_file_prefix]_layer1.dmi", "") + for(var/i in 2 to layer_count) + out_icon.blend_icon(uni_icon("tmp/[temp_file_prefix]_layer[i].dmi", ""), ICON_OVERLAY) + var/icon/final_icon = out_icon.to_icon() + for(var/i in 1 to layer_count) + fdel("tmp/[temp_file_prefix]_layer[i].dmi") + return final_icon diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index d97a5364be40..68dd73532ae3 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -370,9 +370,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) if(notice) return null //Totally nanite construction system not an immersion breaking spawning - var/datum/effect_system/fluid_spread/smoke/fourth_wall_guard = new - fourth_wall_guard.set_up(4, holder = src, location = get_turf(centerpiece)) - fourth_wall_guard.start() + do_smoke(4, get_turf(centerpiece), get_turf(centerpiece)) var/obj/machinery/bsa/full/cannon = new(get_turf(centerpiece),centerpiece.get_cannon_direction()) QDEL_NULL(centerpiece.front_ref) QDEL_NULL(centerpiece.back_ref) diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index ea9722e0fc6b..404b9573d28e 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -116,8 +116,6 @@ ///the type of damage overlay (if any) to use when this bodypart is bruised/burned. var/dmg_overlay_type = "human" - ///a color (optionally matrix) for the damage overlays to give the limb - var/damage_overlay_color /// If we're bleeding, which icon are we displaying on this part var/bleed_overlay_icon @@ -1434,6 +1432,11 @@ husk_blood.blend_mode = BLEND_INSET_OVERLAY husk_blood.dir = thing_to_husk.dir husk_blood.layer = thing_to_husk.layer + var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list() + if (LAZYLEN(blood_dna)) + husk_blood.color = get_color_from_blood_list(blood_dna) + else + husk_blood.color = BLOOD_COLOR_RED return husk_blood ///Add a bodypart overlay and call the appropriate update procs diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm index 02dff71c3a9d..73a526361f0d 100644 --- a/code/modules/surgery/bodyparts/head.dm +++ b/code/modules/surgery/bodyparts/head.dm @@ -98,6 +98,11 @@ /// Can this head be dismembered normally? can_dismember = FALSE +/obj/item/bodypart/head/Initialize(mapload) + . = ..() + AddElement(/datum/element/toy_talk) + RegisterSignals(src, list(SIGNAL_ADDTRAIT(TRAIT_DISFIGURED), SIGNAL_REMOVETRAIT(TRAIT_DISFIGURED)), PROC_REF(update_owner_name)) + /obj/item/bodypart/head/Destroy() QDEL_NULL(worn_ears_offset) QDEL_NULL(worn_glasses_offset) @@ -121,32 +126,39 @@ /obj/item/bodypart/head/examine(mob/user) . = ..() - if(show_organs_on_examine && IS_ORGANIC_LIMB(src)) - var/obj/item/organ/brain/brain = locate(/obj/item/organ/brain) in src - if(!brain) - . += span_info("The brain has been removed from [src].") - else if(brain.suicided || (brain.brainmob && HAS_TRAIT(brain.brainmob, TRAIT_SUICIDED))) - . += span_info("There's a miserable expression on [real_name]'s face; they must have really hated life. There's no hope of recovery.") - else if(brain.brainmob) - if(brain.brainmob?.health <= HEALTH_THRESHOLD_DEAD) - . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.") - if(brain.brainmob.key || brain.brainmob.get_ghost(FALSE, TRUE)) - . += span_info("Its muscles are twitching slightly... It seems to have some life still in it.") - else - . += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.") - else if(brain?.decoy_override) - . += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.") + if(!show_organs_on_examine || !IS_ORGANIC_LIMB(src)) + return + var/shown_name = get_face_name() + var/obj/item/organ/brain/brain = locate(/obj/item/organ/brain) in src + if(!brain) + . += span_info("The brain has been removed from [src].") + else if(brain.suicided || (brain.brainmob && HAS_TRAIT(brain.brainmob, TRAIT_SUICIDED))) + . += span_info("There's a miserable expression on [shown_name]'s face; they must have really hated life. There's no hope of recovery.") + else if(brain.brainmob) + if(brain.brainmob?.health <= HEALTH_THRESHOLD_DEAD) + . += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.") + if(brain.brainmob.key || brain.brainmob.get_ghost(FALSE, TRUE)) + . += span_info("Its muscles are twitching slightly... It seems to have some life still in it.") else - . += span_info("It's completely lifeless.") + . += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.") + else if(brain?.decoy_override) + . += span_info("It's completely lifeless. Perhaps there'll be a chance for them later.") + else + . += span_info("It's completely lifeless.") - if(!(locate(/obj/item/organ/eyes) in src)) - . += span_info("[real_name]'s eyes have been removed.") + if(!(locate(/obj/item/organ/eyes) in src)) + . += span_info("[shown_name]'s eyes have been removed.") - if(!(locate(/obj/item/organ/ears) in src)) - . += span_info("[real_name]'s ears have been removed.") + if(!(locate(/obj/item/organ/ears) in src)) + . += span_info("[shown_name]'s ears have been removed.") - if(!(locate(/obj/item/organ/tongue) in src)) - . += span_info("[real_name]'s tongue has been removed.") + if(!(locate(/obj/item/organ/tongue) in src)) + . += span_info("[shown_name]'s tongue has been removed.") + +/obj/item/bodypart/head/proc/get_face_name() + if (HAS_TRAIT(src, TRAIT_DISFIGURED)) + return "Unknown" + return real_name /obj/item/bodypart/head/can_dismember(obj/item/item) if (!can_dismember) @@ -171,10 +183,11 @@ /obj/item/bodypart/head/update_limb(dropping_limb, is_creating) . = ..() if(!isnull(owner)) - if(HAS_TRAIT(owner, TRAIT_HUSK)) - real_name = "Unknown" + real_name = owner.real_name + if(is_husked) + ADD_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) else - real_name = owner.real_name + REMOVE_TRAIT(src, TRAIT_DISFIGURED, HUSK_TRAIT) update_hair_and_lips(dropping_limb, is_creating) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -222,12 +235,8 @@ . += eye_left . += eye_right -/obj/item/bodypart/head/Initialize(mapload) - . = ..() - AddElement(/datum/element/toy_talk) - /obj/item/bodypart/head/get_voice(add_id_name) - return "The head of [real_name]" + return "The head of [get_face_name()]" /obj/item/bodypart/head/update_bodypart_damage_state() if (head_flags & HEAD_NO_DISFIGURE) @@ -237,9 +246,16 @@ . = ..() var/new_states = brutestate + burnstate if(new_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES) - add_bodypart_trait(TRAIT_DISFIGURED) + ADD_TRAIT(src, TRAIT_DISFIGURED, BODYPART_TRAIT) else if(old_states >= HUMAN_DISFIGURATION_HEAD_DAMAGE_STATES) - remove_bodypart_trait(TRAIT_DISFIGURED) + REMOVE_TRAIT(src, TRAIT_DISFIGURED, BODYPART_TRAIT) + +/obj/item/bodypart/head/proc/update_owner_name(datum/source) + SIGNAL_HANDLER + + if (ishuman(owner)) + var/mob/living/carbon/human/as_human = owner + as_human?.update_visible_name() /obj/item/bodypart/head/monkey icon = 'icons/mob/human/species/monkey/bodyparts.dmi' diff --git a/code/modules/surgery/bodyparts/worn_feature_offset.dm b/code/modules/surgery/bodyparts/worn_feature_offset.dm index 24f4cedbff81..3fee7097d2d4 100644 --- a/code/modules/surgery/bodyparts/worn_feature_offset.dm +++ b/code/modules/surgery/bodyparts/worn_feature_offset.dm @@ -19,7 +19,6 @@ list/offset_y = list("south" = 0), ) attached_part.feature_offsets[feature_key] = src - owner = attached_part.owner src.attached_part = attached_part src.feature_key = feature_key src.offset_x = offset_x @@ -28,13 +27,20 @@ if (length(offset_x) <= 1 && length(offset_y) <= 1) return // We don't need to do any extra signal handling - if (!isnull(owner)) - changed_owner(owner) + changed_owner(owner, attached_part.owner) RegisterSignal(attached_part, COMSIG_BODYPART_CHANGED_OWNER, PROC_REF(changed_owner)) +/datum/worn_feature_offset/Destroy(force) + attached_part.feature_offsets -= feature_key + attached_part = null + changed_owner(null, null) + return ..() + /// Returns the current offset which should be used for this feature /datum/worn_feature_offset/proc/get_offset() var/current_dir = owner ? owner.dir : SOUTH + if(ISDIAGONALDIR(current_dir)) + current_dir = current_dir & (EAST|WEST) current_dir = dir2text(current_dir) var/x = length(offset_x) ? ((current_dir in offset_x) ? offset_x[current_dir] : offset_x["south"]) : 0 var/y = length(offset_y) ? ((current_dir in offset_y) ? offset_y[current_dir] : offset_y["south"]) : 0 @@ -49,9 +55,11 @@ /// When the owner of the bodypart changes, update our signal registrations /datum/worn_feature_offset/proc/changed_owner(obj/item/bodypart/part, mob/living/new_owner, mob/living/old_owner) SIGNAL_HANDLER + if(isnull(old_owner)) + old_owner = owner owner = new_owner if (!isnull(old_owner)) - UnregisterSignal(old_owner, COMSIG_ATOM_POST_DIR_CHANGE) + UnregisterSignal(old_owner, list(COMSIG_ATOM_POST_DIR_CHANGE, COMSIG_QDELETING)) if (!isnull(new_owner)) RegisterSignal(new_owner, COMSIG_ATOM_POST_DIR_CHANGE, PROC_REF(on_dir_change)) RegisterSignal(new_owner, COMSIG_QDELETING, PROC_REF(on_owner_deleted)) diff --git a/code/modules/surgery/operations/operation_add_limb.dm b/code/modules/surgery/operations/operation_add_limb.dm index 018e32d10670..ecdd579312e4 100644 --- a/code/modules/surgery/operations/operation_add_limb.dm +++ b/code/modules/surgery/operations/operation_add_limb.dm @@ -173,7 +173,7 @@ /obj/item/stack/sticky_tape = 2, ) time = 4.8 SECONDS - operation_flags = OPERATION_SELF_OPERABLE | OPERATION_STANDING_ALLOWED + operation_flags = OPERATION_SELF_OPERABLE | OPERATION_STANDING_ALLOWED | OPERATION_IGNORE_CLOTHES all_surgery_states_required = SURGERY_PROSTHETIC_UNSECURED /datum/surgery_operation/limb/secure_arbitrary_prosthetic/get_default_radial_image() diff --git a/code/modules/surgery/operations/operation_healing.dm b/code/modules/surgery/operations/operation_healing.dm index 2592ab7b7fd2..525d9746f053 100644 --- a/code/modules/surgery/operations/operation_healing.dm +++ b/code/modules/surgery/operations/operation_healing.dm @@ -138,7 +138,7 @@ var/progress_text - if(surgeon.is_holding_item_of_type(/obj/item/healthanalyzer)) + if(show_stats(surgeon, patient)) if(brute_healed > 0 && patient.get_brute_loss() > 0) progress_text += ". Remaining brute: [patient.get_brute_loss()]" if(burn_healed > 0 && patient.get_fire_loss() > 0) @@ -163,6 +163,21 @@ return progress_text +/// Checks whether we show precise damage stats during the surgery +/datum/surgery_operation/basic/tend_wounds/proc/show_stats(mob/living/surgeon, mob/living/patient) + if(surgeon.is_holding_item_of_type(/obj/item/healthanalyzer)) + return TRUE + for(var/obj/machinery/vitals_reader/vitals in view(4, patient)) + if(vitals.patient == patient) + return TRUE + for(var/obj/machinery/computer/operating/op_pc in range(1, patient)) + if(op_pc.table?.patient == patient) + return TRUE + for(var/obj/item/mod/control/modsuit in surgeon.get_equipped_items()) + if(modsuit.active && istype(modsuit.selected_module, /obj/item/mod/module/health_analyzer)) + return TRUE + return FALSE + #undef CONDITIONAL_DAMAGE_MESSAGE /datum/surgery_operation/basic/tend_wounds/on_success(mob/living/patient, mob/living/surgeon, tool, list/operation_args) diff --git a/code/modules/surgery/operations/operation_lipo.dm b/code/modules/surgery/operations/operation_lipo.dm index b04e856057fe..f876c744412b 100644 --- a/code/modules/surgery/operations/operation_lipo.dm +++ b/code/modules/surgery/operations/operation_lipo.dm @@ -41,7 +41,9 @@ /datum/surgery_operation/limb/lipoplasty/state_check(obj/item/bodypart/limb) if(limb.body_zone != BODY_ZONE_CHEST) return FALSE - if(!HAS_TRAIT_FROM(limb.owner, TRAIT_FAT, OBESITY) && limb.owner.nutrition < NUTRITION_LEVEL_WELL_FED) + if(HAS_TRAIT(limb.owner, TRAIT_NOHUNGER)) + return FALSE + if(!HAS_TRAIT_FROM(limb.owner, TRAIT_FAT, OBESITY) || limb.owner.nutrition < NUTRITION_LEVEL_WELL_FED) return FALSE return TRUE @@ -63,27 +65,29 @@ span_notice("[surgeon] successfully removes excess fat from [limb.owner]'s body!"), span_notice("[surgeon] finishes cutting away excess fat from [limb.owner]'s [limb.plaintext_zone]."), ) - limb.owner.overeatduration = 0 //patient is unfatted var/removednutriment = limb.owner.nutrition + limb.owner.overeatduration = 0 //patient is unfatted limb.owner.set_nutrition(NUTRITION_LEVEL_WELL_FED) removednutriment -= NUTRITION_LEVEL_WELL_FED //whatever was removed goes into the meat - var/typeofmeat = /obj/item/food/meat/slab/human if(limb.owner.flags_1 & HOLOGRAM_1) - typeofmeat = null - else if(limb.owner.dna?.species) - typeofmeat = limb.owner.dna.species.meat + return + + var/typeofmeat = null + for(var/meat_path in limb.butcher_drops) + if(ispath(meat_path, /obj/item/food/meat)) + typeofmeat = meat_path + break if(!typeofmeat) return - var/obj/item/food/meat/slab/newmeat = new typeofmeat() + var/obj/item/food/meat/slab/newmeat = new typeofmeat(limb.owner.drop_location()) newmeat.name = "fatty meat" newmeat.desc = "Extremely fatty tissue taken from a patient." newmeat.subjectname = limb.owner.real_name newmeat.subjectjob = limb.owner.job - newmeat.reagents.add_reagent(/datum/reagent/consumable/nutriment, (removednutriment / 15)) //To balance with nutriment_factor of nutriment - newmeat.forceMove(limb.owner.drop_location()) + newmeat.reagents.add_reagent(/datum/reagent/consumable/nutriment, (removednutriment / /datum/reagent/consumable/nutriment::nutriment_factor)) /datum/surgery_operation/limb/lipoplasty/mechanic name = "engage expulsion valve" //gross diff --git a/code/modules/surgery/operations/operation_plastic_surgery.dm b/code/modules/surgery/operations/operation_plastic_surgery.dm index 2ef9db9d8563..1203702d1d61 100644 --- a/code/modules/surgery/operations/operation_plastic_surgery.dm +++ b/code/modules/surgery/operations/operation_plastic_surgery.dm @@ -10,7 +10,7 @@ /obj/item/pen = 5, ) time = 6.4 SECONDS - operation_flags = OPERATION_MORBID | OPERATION_AFFECTS_MOOD | OPERATION_NOTABLE + operation_flags = OPERATION_MORBID | OPERATION_AFFECTS_MOOD | OPERATION_NOTABLE | OPERATION_NO_PATIENT_REQUIRED preop_sound = 'sound/items/handling/surgery/scalpel1.ogg' success_sound = 'sound/items/handling/surgery/scalpel2.ogg' all_surgery_states_required = SURGERY_SKIN_OPEN @@ -25,15 +25,18 @@ return limb.body_zone == BODY_ZONE_HEAD /datum/surgery_operation/limb/plastic_surgery/pre_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args) - if(HAS_TRAIT_FROM(limb.owner, TRAIT_DISFIGURED, TRAIT_GENERIC)) + if(HAS_TRAIT_FROM(limb, TRAIT_DISFIGURED, TRAIT_GENERIC)) return TRUE //skip name selection if fixing disfigurement var/list/names = list() if(isabductor(surgeon)) for(var/j in 1 to 9) names += "Subject [limb.owner.gender == MALE ? "i" : "o"]-[pick("a", "b", "c", "d", "e")]-[rand(10000, 99999)]" - names += limb.owner.generate_random_mob_name(TRUE) //give one normal name in case they want to do regular plastic surgery + if(limb.owner) + names += limb.owner.generate_random_mob_name(TRUE) //give one normal name in case they want to do regular plastic surgery + else + names += generate_random_name_species_based(pick(MALE, FEMALE), TRUE, GLOB.species_list[limb.limb_id] || /datum/species/human) else var/advanced = LIMB_HAS_SURGERY_STATE(limb, SURGERY_PLASTIC_APPLIED) var/obj/item/offhand = surgeon.get_inactive_held_item() @@ -44,8 +47,12 @@ else if(advanced) to_chat(surgeon, span_warning("You have no picture to base the appearance on!")) + for(var/i in 1 to 10) - names += limb.owner.generate_random_mob_name(TRUE) + if(limb.owner) + names += limb.owner.generate_random_mob_name(TRUE) + else + names += generate_random_name_species_based(pick(MALE, FEMALE), TRUE, GLOB.species_list[limb.limb_id] || /datum/species/human) operation_args[OPERATION_NEW_NAME] = tgui_input_list(surgeon, "New name to assign", "Plastic Surgery", names) return !!operation_args[OPERATION_NEW_NAME] @@ -54,34 +61,40 @@ display_results( surgeon, limb.owner, - span_notice("You begin to alter [limb.owner]'s appearance..."), - span_notice("[surgeon] begins to alter [limb.owner]'s appearance."), - span_notice("[surgeon] begins to make an incision in [limb.owner]'s [limb.plaintext_zone]."), + span_notice("You begin to alter [limb.owner || limb]'s appearance..."), + span_notice("[surgeon] begins to alter [limb.owner || limb]'s appearance."), + span_notice("[surgeon] begins to make an incision in [FORMAT_LIMB_OWNER(limb)]."), ) display_pain(limb.owner, "You feel a slicing pain across your face!") -/datum/surgery_operation/limb/plastic_surgery/on_success(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args) - if(HAS_TRAIT_FROM(limb.owner, TRAIT_DISFIGURED, TRAIT_GENERIC)) - REMOVE_TRAIT(limb.owner, TRAIT_DISFIGURED, TRAIT_GENERIC) +/datum/surgery_operation/limb/plastic_surgery/on_success(obj/item/bodypart/head/limb, mob/living/surgeon, obj/item/tool, list/operation_args) + if(!istype(limb)) + CRASH("Plastic surgery finished on a non-head limb [limb]!") + + if(HAS_TRAIT_FROM(limb, TRAIT_DISFIGURED, TRAIT_GENERIC)) + REMOVE_TRAIT(limb, TRAIT_DISFIGURED, TRAIT_GENERIC) display_results( surgeon, limb.owner, - span_notice("You successfully restore [limb.owner]'s appearance."), - span_notice("[surgeon] successfully restores [limb.owner]'s appearance!"), - span_notice("[surgeon] finishes the operation on [limb.owner]'s face."), + span_notice("You successfully restore [limb.owner || limb]'s appearance."), + span_notice("[surgeon] successfully restores [limb.owner || limb]'s appearance!"), + span_notice("[surgeon] finishes the operation on [limb.owner ? "[limb.owner]'s face." : limb]"), ) display_pain(limb.owner, "The pain fades, your face feels normal again!") return - var/oldname = limb.owner.real_name - limb.owner.real_name = operation_args[OPERATION_NEW_NAME] - var/newname = limb.owner.real_name //something about how the code handles names required that I use this instead of target.real_name + var/oldname = limb.owner?.real_name || limb.real_name + if (limb.owner) + limb.owner.real_name = operation_args[OPERATION_NEW_NAME] + else + limb.real_name = operation_args[OPERATION_NEW_NAME] + display_results( surgeon, limb.owner, - span_notice("You alter [oldname]'s appearance completely, [limb.owner.p_they()] is now [newname]."), - span_notice("[surgeon] alters [oldname]'s appearance completely, [limb.owner.p_they()] is now [newname]!"), - span_notice("[surgeon] finishes the operation on [limb.owner]'s face."), + span_notice("You alter [oldname]'s appearance completely, [limb.owner.p_they()] is now [operation_args[OPERATION_NEW_NAME]]."), + span_notice("[surgeon] alters [oldname]'s appearance completely, [limb.owner.p_they()] is now [operation_args[OPERATION_NEW_NAME]]!"), + span_notice("[surgeon] finishes the operation on [limb.owner ? "[limb.owner]'s face." : limb]."), ) display_pain(limb.owner, "The pain fades, your face feels new and unfamiliar!") if(ishuman(limb.owner)) @@ -95,12 +108,12 @@ display_results( surgeon, limb.owner, - span_warning("Your screw up, leaving [limb.owner]'s appearance disfigured!"), - span_warning("[surgeon] screws up, disfiguring [limb.owner]'s appearance!"), - span_notice("[surgeon] finishes the operation on [limb.owner]'s face."), + span_warning("Your screw up, leaving [limb.owner || limb]'s appearance disfigured!"), + span_warning("[surgeon] screws up, disfiguring [limb.owner || limb]'s appearance!"), + span_notice("[surgeon] finishes the operation on [limb.owner ? "[limb.owner]'s face." : limb]."), ) display_pain(limb.owner, "Your face feels horribly scarred and deformed!") - ADD_TRAIT(limb.owner, TRAIT_DISFIGURED, TRAIT_GENERIC) + ADD_TRAIT(limb, TRAIT_DISFIGURED, TRAIT_GENERIC) #undef OPERATION_NEW_NAME @@ -111,7 +124,7 @@ /obj/item/stack/sheet/plastic = 1, ) time = 4.8 SECONDS - operation_flags = OPERATION_MORBID | OPERATION_LOCKED + operation_flags = OPERATION_MORBID | OPERATION_LOCKED | OPERATION_NO_PATIENT_REQUIRED preop_sound = 'sound/effects/blob/blobattack.ogg' success_sound = 'sound/effects/blob/attackblob.ogg' failure_sound = 'sound/effects/blob/blobattack.ogg' @@ -128,9 +141,9 @@ display_results( surgeon, limb.owner, - span_notice("You begin to apply plastic to [limb.owner]'s [limb.plaintext_zone]..."), - span_notice("[surgeon] begins to apply plastic to [limb.owner]'s [limb.plaintext_zone]."), - span_notice("[surgeon] begins to perform surgery on [limb.owner]'s [limb.plaintext_zone]."), + span_notice("You begin to apply plastic to [FORMAT_LIMB_OWNER(limb)]..."), + span_notice("[surgeon] begins to apply plastic to [FORMAT_LIMB_OWNER(limb)]."), + span_notice("[surgeon] begins to perform surgery on [FORMAT_LIMB_OWNER(limb)]."), ) display_pain(limb.owner, "You feel a strange sensation as something is applied to your face!") diff --git a/code/modules/surgery/organs/external/_visual_organs.dm b/code/modules/surgery/organs/external/_visual_organs.dm index ec576568ab2a..f7ba0b1ce6b6 100644 --- a/code/modules/surgery/organs/external/_visual_organs.dm +++ b/code/modules/surgery/organs/external/_visual_organs.dm @@ -163,6 +163,24 @@ Unlike normal organs, we're actually inside a persons limbs at all times organ_flags = parent_type::organ_flags | ORGAN_EXTERNAL + /// Offset to apply to equipment worn on the mouth we give to the head. + var/datum/worn_feature_offset/worn_mask_offset + +/obj/item/organ/snout/on_bodypart_insert(obj/item/bodypart/head/limb) + . = ..() + if(isnull(limb.worn_mask_offset)) + worn_mask_offset = limb.worn_mask_offset = new( + attached_part = limb, + feature_key = OFFSET_FACEMASK, + offset_x = list("east" = 1, "west" = -1), + ) + +/obj/item/organ/snout/on_bodypart_remove(obj/item/bodypart/head/limb, movement_flags) + if(worn_mask_offset) + QDEL_NULL(worn_mask_offset) + limb.worn_mask_offset = null + return ..() + /datum/bodypart_overlay/mutant/snout layers = EXTERNAL_ADJACENT feature_key = FEATURE_SNOUT diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm index 3f993dabdcf7..910c39c076f5 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_chest.dm @@ -301,7 +301,7 @@ . = ..() if(!owner || . & EMP_PROTECT_SELF) return - to_chat(owner, span_warning("You feel sheering pain as your body is crushed like a soda can!")) + to_chat(owner, span_warning("You feel shearing pain as your body is crushed like a soda can!")) owner.apply_damage(20/severity, BRUTE, def_zone = BODY_ZONE_CHEST) /obj/item/organ/cyberimp/chest/spine/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm index 510468ffa0f1..8765b0dc373b 100644 --- a/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm +++ b/code/modules/surgery/organs/internal/cyberimp/augments_internal.dm @@ -210,9 +210,7 @@ owner.set_stamina_loss(0) addtimer(CALLBACK(owner, TYPE_PROC_REF(/mob/living, set_stamina_loss), 0), stun_resistance_time) - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - sparks.set_up(5, 1, src) - sparks.start() + do_sparks(5, TRUE, src) give_stun_buffs(owner) addtimer(CALLBACK(src, PROC_REF(remove_stun_buffs), owner), stun_resistance_time) diff --git a/code/modules/surgery/organs/internal/ears/_ears.dm b/code/modules/surgery/organs/internal/ears/_ears.dm index fb086e8bd362..e1259f5c82d8 100644 --- a/code/modules/surgery/organs/internal/ears/_ears.dm +++ b/code/modules/surgery/organs/internal/ears/_ears.dm @@ -48,11 +48,16 @@ . = ..() if(temporary_deafness) on_deafened() + REMOVE_TRAIT(organ_owner, TRAIT_DEAF, NO_EARS) /obj/item/organ/ears/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags) . = ..() if(temporary_deafness) on_undeafened(organ_owner) + // Do not apply with special flag, even if it would ultimately be redundant by new ears being hot-swapped in. + // This is so we don't trip signal_addtrait when hot-swapping ears, which could cause inappropriate behavior like nuking sound effects. + if(!special) + ADD_TRAIT(organ_owner, TRAIT_DEAF, NO_EARS) /obj/item/organ/ears/get_status_appendix(advanced, add_tooltips) if(owner.stat == DEAD || !HAS_TRAIT(owner, TRAIT_DEAF)) diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm index 20bf1ecfac55..b6152e8b0ce6 100644 --- a/code/modules/surgery/organs/internal/eyes/_eyes.dm +++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm @@ -620,10 +620,10 @@ name = "undead eyes" desc = "Somewhat counterintuitively, these half-rotten eyes actually have superior vision to those of a living human." color_cutoffs = list(25, 35, 5) - penlight_message = "are rotten and decayed" + penlight_message = "are rotten and decayed!" /obj/item/organ/eyes/zombie/penlight_examine(mob/living/viewer, obj/item/examtool) - return span_danger(penlight_message) + return span_danger("[owner.p_Their()] eyes [penlight_message]") /obj/item/organ/eyes/alien name = "alien eyes" @@ -707,8 +707,9 @@ eye_color_left = "#3cb8a5" eye_color_right = "#3cb8a5" sight_flags = SEE_MOBS | SEE_OBJS | SEE_TURFS + flash_protect = FLASH_PROTECTION_SENSITIVE organ_traits = list(TRAIT_XRAY_VISION) - penlight_message = "replaced by small radiation emitters and detectors" + penlight_message = "are replaced by small radiation emitters and detectors" /obj/item/organ/eyes/robotic/thermals name = "thermal eyes" @@ -1090,7 +1091,7 @@ eye_color_left = "#3c4e52" eye_color_right = "#3c4e52" blink_animation = FALSE - flash_protect = FLASH_PROTECTION_SENSITIVE + flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE pupils_name = "aperture clusters" /obj/item/organ/eyes/robotic/shield/moth @@ -1115,13 +1116,14 @@ penlight_message = "are bulbous clusters of LEDs and cameras" pupils_name = "aperture clusters" -/obj/item/organ/eyes/robotic/thermals/moth //we inherit flash weakness from thermals +/obj/item/organ/eyes/robotic/thermals/moth name = "thermal moth eyes" icon_state = "eyes_moth_cyber_thermal" eye_icon_state = "motheyes_white" eye_color_left = "#901f38" eye_color_right = "#901f38" blink_animation = FALSE + flash_protect = FLASH_PROTECTION_HYPER_SENSITIVE pupils_name = "sensor clusters" penlight_message = "are two clustered hemispheres of thermal sensors" @@ -1173,13 +1175,13 @@ low_light_cutoff = list(5, 12, 20) medium_light_cutoff = list(15, 20, 30) high_light_cutoff = list(30, 35, 50) - penlight_message = "glow a foggy red, sizzling under the light" + penlight_message = "glow a foggy red, sizzling under the light!" /obj/item/organ/eyes/night_vision/maintenance_adapted/penlight_examine(mob/living/viewer, obj/item/examtool) if(!owner.is_blind()) to_chat(owner, span_danger("Your eyes sizzle agonizingly as light is shone on them!")) apply_organ_damage(20 * examtool.light_power) //that's 0.5 lightpower for a penlight, so one penlight shining is equivalent to two seconds in a lit area - return span_danger("[owner.p_Their()] eyes [penlight_message].") + return span_danger("[owner.p_Their()] eyes [penlight_message]") /obj/item/organ/eyes/night_vision/maintenance_adapted/on_life(seconds_per_tick) if(owner.get_eye_protection() <= FLASH_PROTECTION_SENSITIVE && !owner.is_blind() && isturf(owner.loc) && owner.has_light_nearby(light_amount=0.5)) //we allow a little more than usual so we can produce light from the adapted eyes diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 89d25e98516b..60b45bf0eb46 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -287,6 +287,7 @@ ), ) var/data = custom_data || with_data && src_object.ui_data(user) + SEND_SIGNAL(src_object, COMSIG_UI_DATA, user, data) if(data) json_data["data"] = data var/static_data = with_static_data && src_object.ui_static_data(user) diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm index 594c2f443fe8..364c0e24c4ce 100644 --- a/code/modules/transport/tram/tram_controller.dm +++ b/code/modules/transport/tram/tram_controller.dm @@ -519,15 +519,11 @@ /datum/transport_controller/linear/tram/proc/set_status_code(code, value) if(code != DOORS_READY) log_transport("TC: [specific_transport_id] status change [value ? "+" : "-"][english_list(bitfield_to_list(code, TRANSPORT_FLAGS))].") - switch(value) - if(TRUE) - controller_status |= code - if(FALSE) - controller_status &= ~code - else - stack_trace("Transport controller received invalid status code request [code]/[value]") - return + if(value) + controller_status |= code + else + controller_status &= ~code send_transport_active_signal() /datum/transport_controller/linear/tram/proc/send_transport_active_signal() diff --git a/code/modules/transport/tram/tram_signals.dm b/code/modules/transport/tram/tram_signals.dm index e160e3184724..6ed73cb5471e 100644 --- a/code/modules/transport/tram/tram_signals.dm +++ b/code/modules/transport/tram/tram_signals.dm @@ -275,13 +275,11 @@ if(updated_controller.specific_transport_id != configured_transport_id) return - switch(new_status) - if(TRUE) - if(operating_status == TRANSPORT_REMOTE_FAULT) - operating_status = TRANSPORT_SYSTEM_NORMAL - if(FALSE) - if(operating_status == TRANSPORT_SYSTEM_NORMAL) - operating_status = TRANSPORT_REMOTE_FAULT + if(new_status) + if(operating_status == TRANSPORT_REMOTE_FAULT) + operating_status = TRANSPORT_SYSTEM_NORMAL + else if(operating_status == TRANSPORT_SYSTEM_NORMAL) + operating_status = TRANSPORT_REMOTE_FAULT /** * Update processing state. diff --git a/code/modules/tutorials/tutorials/drop.dm b/code/modules/tutorials/tutorials/drop.dm index 6ace8bb88a78..6be38f234d27 100644 --- a/code/modules/tutorials/tutorials/drop.dm +++ b/code/modules/tutorials/tutorials/drop.dm @@ -57,10 +57,10 @@ if (STAGE_PICK_SOMETHING_UP) show_instruction("Pick something up!") -/datum/tutorial/drop/proc/on_swap_hands() +/datum/tutorial/drop/proc/on_swap_hands(mob/living/source, obj/item/swapped_to, obj/item/swapped_from) SIGNAL_HANDLER - if (isnull(user.get_active_held_item())) + if (isnull(swapped_to)) if (stage != STAGE_PICK_SOMETHING_UP) stage = STAGE_PICK_SOMETHING_UP show_instructions() diff --git a/code/modules/tutorials/tutorials/switch_hands.dm b/code/modules/tutorials/tutorials/switch_hands.dm index d423667ac383..724d99fca81e 100644 --- a/code/modules/tutorials/tutorials/switch_hands.dm +++ b/code/modules/tutorials/tutorials/switch_hands.dm @@ -68,14 +68,14 @@ if (STAGE_PICK_UP_ITEM) show_instruction("Pick something up!") -/datum/tutorial/switch_hands/proc/on_swap_hands() +/datum/tutorial/switch_hands/proc/on_swap_hands(mob/living/source, obj/item/swapped_to, obj/item/swapped_from) SIGNAL_HANDLER //FIXME: this checking breaks easily - if (isnull(user.get_active_held_item())) + if (isnull(swapped_to)) stage = STAGE_PICK_UP_ITEM show_instructions() - else if (isnull(user.get_inactive_held_item())) + else if (isnull(swapped_from)) stage = STAGE_SHOULD_SWAP_HAND show_instructions() else diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 93f2059e84e7..01a901dd28fb 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -195,6 +195,8 @@ #include "hydroponics_harvest.dm" #include "hydroponics_self_mutations.dm" #include "hydroponics_validate_genes.dm" +#include "id_access.dm" +#include "id_card.dm" #include "inhands.dm" #include "interaction_door.dm" #include "interaction_silicon.dm" diff --git a/code/modules/unit_tests/combat.dm b/code/modules/unit_tests/combat.dm index 80ccbaf8dadd..113abac1339c 100644 --- a/code/modules/unit_tests/combat.dm +++ b/code/modules/unit_tests/combat.dm @@ -2,6 +2,7 @@ var/mob/living/carbon/human/puncher = allocate(/mob/living/carbon/human/consistent) var/mob/living/carbon/human/victim = allocate(/mob/living/carbon/human/consistent) + puncher.st_set_stat(STAT_STRENGTH, 1) // DARKPACK EDIT ADD // Avoid all randomness in tests ADD_TRAIT(puncher, TRAIT_PERFECT_ATTACKER, INNATE_TRAIT) diff --git a/code/modules/unit_tests/death_moodlets.dm b/code/modules/unit_tests/death_moodlets.dm index ded468abbb1d..4b1b2fc3512b 100644 --- a/code/modules/unit_tests/death_moodlets.dm +++ b/code/modules/unit_tests/death_moodlets.dm @@ -6,6 +6,7 @@ /datum/unit_test/death_moodlets/Run() var/mob/living/carbon/human/consistent/dummy = allocate(__IMPLIED_TYPE__) + dummy.mind_initialize() prepare_dummy(dummy) var/mob/living/dying = get_dying_mob() @@ -47,7 +48,7 @@ desired_moodlet = /datum/mood_event/conditional/see_death/desensitized /datum/unit_test/death_moodlets/human/desensitized/prepare_dummy(mob/living/carbon/human/consistent/dummy) - ADD_TRAIT(dummy, TRAIT_DESENSITIZED, TRAIT_SOURCE_UNIT_TESTS) + dummy.mind.desensitized_level = DESENSITIZED_THRESHOLD /// Test callous moodlet /datum/unit_test/death_moodlets/human/callous @@ -61,8 +62,8 @@ desired_moodlet = /datum/mood_event/conditional/see_death/dontcare /datum/unit_test/death_moodlets/human/desensitized_and_callous/prepare_dummy(mob/living/carbon/human/consistent/dummy) - ADD_TRAIT(dummy, TRAIT_DESENSITIZED, TRAIT_SOURCE_UNIT_TESTS) dummy.add_personality(/datum/personality/callous) + dummy.mind.desensitized_level = DESENSITIZED_THRESHOLD /// Test cultist positive moodlet /datum/unit_test/death_moodlets/human/cultist @@ -87,7 +88,7 @@ desired_moodlet = /datum/mood_event/conditional/see_death/pet /datum/unit_test/death_moodlets/pet/desensitized_to_pet/prepare_dummy(mob/living/carbon/human/consistent/dummy) - ADD_TRAIT(dummy, TRAIT_DESENSITIZED, TRAIT_SOURCE_UNIT_TESTS) + dummy.mind.desensitized_level = DESENSITIZED_THRESHOLD /// Tests callous moodlet when a pet dies /datum/unit_test/death_moodlets/pet/callous_to_pet @@ -102,3 +103,23 @@ /datum/unit_test/death_moodlets/pet/animal_disliker_to_pet/prepare_dummy(mob/living/carbon/human/consistent/dummy) dummy.add_personality(/datum/personality/animal_disliker) + +// This one just tests that the death desensitized status effect works as expected +/datum/unit_test/desensitized_status_effect + +/datum/unit_test/desensitized_status_effect/Run() + var/mob/living/carbon/human/consistent/normal_dummy = EASY_ALLOCATE() + var/mob/living/carbon/human/consistent/desensitized_dummy = EASY_ALLOCATE() + desensitized_dummy.mind_initialize() + + desensitized_dummy.apply_status_effect(/datum/status_effect/desensitized, TRAIT_SOURCE_UNIT_TESTS, DESENSITIZED_THRESHOLD) + TEST_ASSERT_EQUAL(desensitized_dummy.mind.desensitized_level, DESENSITIZED_THRESHOLD, "Desensitized level not set correctly on application of desensitized status effect.") + + desensitized_dummy.mind.transfer_to(normal_dummy) + TEST_ASSERT_EQUAL(normal_dummy.mind.desensitized_level, 1.0, "Desensitized level not reset to normal on transfer of mind from desensitized mob.") + + normal_dummy.mind.transfer_to(desensitized_dummy) + TEST_ASSERT_EQUAL(desensitized_dummy.mind.desensitized_level, DESENSITIZED_THRESHOLD, "Desensitized level not restored on transfer of mind back to desensitized mob.") + + desensitized_dummy.remove_status_effect(/datum/status_effect/desensitized, TRAIT_SOURCE_UNIT_TESTS) + TEST_ASSERT_EQUAL(desensitized_dummy.mind.desensitized_level, 1.0, "Desensitized level not reset to normal on removal of desensitized status effect.") diff --git a/code/modules/unit_tests/find_reference_sanity.dm b/code/modules/unit_tests/find_reference_sanity.dm index 4bc6445f0243..a9c1e60179f3 100644 --- a/code/modules/unit_tests/find_reference_sanity.dm +++ b/code/modules/unit_tests/find_reference_sanity.dm @@ -6,12 +6,14 @@ var/atom/movable/ref_test/test var/list/test_list = list() var/list/test_assoc_list = list() + var/alist/test_alist = alist() /atom/movable/ref_holder/Destroy() test = null static_test = null test_list.Cut() test_assoc_list.Cut() + test_alist.Cut() return ..() /atom/movable/ref_test @@ -45,14 +47,16 @@ testbed.test = victim testbed.test_list += victim testbed.test_assoc_list["baseline"] = victim + testbed.test_alist["baseline"] = victim var/refcount = refcount(victim) - TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + TEST_ASSERT_EQUAL(refcount, 7, "Should be: test references: 4 + baseline references: 3 (victim var,loc,allocated list)") victim.DoSearchVar(testbed, "First Run") TEST_ASSERT(LAZYACCESS(victim.found_refs, "test"), "The ref-tracking tool failed to find a regular value") TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_list), "The ref-tracking tool failed to find a list entry") TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_assoc_list), "The ref-tracking tool failed to find an assoc list value") + TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_alist), "The ref-tracking tool failed to find an alist value") SSgarbage.should_save_refs = FALSE /datum/unit_test/find_reference_exotic/Run() @@ -64,15 +68,17 @@ testbed.overlays += victim testbed.vis_contents += victim testbed.test_assoc_list[victim] = TRUE + testbed.test_alist[victim] = TRUE var/refcount = refcount(victim) - TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + TEST_ASSERT_EQUAL(refcount, 7, "Should be: test references: 4 + baseline references: 3 (victim var,loc,allocated list)") victim.DoSearchVar(testbed, "Second Run") //This is another sanity check TEST_ASSERT(!LAZYACCESS(victim.found_refs, testbed.overlays), "The ref-tracking tool found an overlays entry? That shouldn't be possible") TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.vis_contents), "The ref-tracking tool failed to find a vis_contents entry") TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_assoc_list), "The ref-tracking tool failed to find an assoc list key") + TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_alist), "The ref-tracking tool failed to find an alist key") SSgarbage.should_save_refs = FALSE /datum/unit_test/find_reference_esoteric/Run() @@ -86,15 +92,18 @@ testbed.test_list += list(to_find) var/list/to_find_assoc = list(victim) testbed.test_assoc_list["Nesting"] = to_find_assoc + var/list/to_find_alist = list(victim) + testbed.test_alist["Nesting"] = to_find_alist var/refcount = refcount(victim) - TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + TEST_ASSERT_EQUAL(refcount, 7, "Should be: test references: 4 + baseline references: 3 (victim var,loc,allocated list)") victim.DoSearchVar(victim, "Third Run Self") victim.DoSearchVar(testbed, "Third Run Testbed") TEST_ASSERT(LAZYACCESS(victim.found_refs, "self_ref"), "The ref-tracking tool failed to find a self reference") TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find), "The ref-tracking tool failed to find a nested list entry") TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_assoc), "The ref-tracking tool failed to find a nested assoc list entry") + TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_alist), "The ref-tracking tool failed to find a nested alist entry") SSgarbage.should_save_refs = FALSE /datum/unit_test/find_reference_null_key_entry/Run() @@ -104,11 +113,13 @@ //Calm before the storm testbed.test_assoc_list = list(null = victim) + testbed.test_alist = alist(null = victim) var/refcount = refcount(victim) - TEST_ASSERT_EQUAL(refcount, 4, "Should be: test references: 1 + baseline references: 3 (victim var,loc,allocated list)") + TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)") victim.DoSearchVar(testbed, "Fourth Run") TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_assoc_list), "The ref-tracking tool failed to find a null key'd assoc list entry") + TEST_ASSERT(LAZYACCESS(victim.found_refs, testbed.test_alist), "The ref-tracking tool failed to find a null key'd alist entry") /datum/unit_test/find_reference_assoc_investigation/Run() var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test) @@ -129,6 +140,28 @@ TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_null_assoc_nested), "The ref-tracking tool failed to find a null key'd nested assoc list entry") SSgarbage.should_save_refs = FALSE +/datum/unit_test/find_reference_alist_investigation/Run() + var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test) + var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder) + SSgarbage.should_save_refs = TRUE + + //Alists are evil let's fuck em up a little + var/list/to_find_in_key = list(victim) + testbed.test_alist[to_find_in_key] = list("memes") + var/list/to_find_null_assoc_nested = list(victim) + testbed.test_alist[null] = to_find_null_assoc_nested + var/list/to_find_number_assoc_nested = list(victim) + testbed.test_alist[0] = to_find_number_assoc_nested + + var/refcount = refcount(victim) + TEST_ASSERT_EQUAL(refcount, 6, "Should be: test references: 3 + baseline references: 3 (victim var,loc,allocated list)") + victim.DoSearchVar(testbed, "Sixth Run") + + TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_in_key), "The ref-tracking tool failed to find a nested assoc list key") + TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_null_assoc_nested), "The ref-tracking tool failed to find a null key'd nested assoc list entry") + TEST_ASSERT(LAZYACCESS(victim.found_refs, to_find_number_assoc_nested), "The ref-tracking tool failed to find a number key'd alist entry") + SSgarbage.should_save_refs = FALSE + /datum/unit_test/find_reference_static_investigation/Run() var/atom/movable/ref_test/victim = allocate(/atom/movable/ref_test) var/atom/movable/ref_holder/testbed = allocate(/atom/movable/ref_holder) @@ -146,7 +179,7 @@ var/refcount = refcount(victim) TEST_ASSERT_EQUAL(refcount, 5, "Should be: test references: 2 + baseline references: 3 (victim var,loc,allocated list)") - victim.DoSearchVar(global_vars, "Sixth Run") + victim.DoSearchVar(global_vars, "Seventh Run") TEST_ASSERT(LAZYACCESS(victim.found_refs, global_vars), "The ref-tracking tool failed to find a natively global variable") SSgarbage.should_save_refs = FALSE diff --git a/code/modules/unit_tests/font_awesome_icons.dm b/code/modules/unit_tests/font_awesome_icons.dm index 9ebba7657917..2e9b0019c0c2 100644 --- a/code/modules/unit_tests/font_awesome_icons.dm +++ b/code/modules/unit_tests/font_awesome_icons.dm @@ -32,10 +32,7 @@ * Verifies that all quirk icons are valid. */ /datum/unit_test/font_awesome_icons/proc/verify_quirk_icons() - for(var/datum/quirk/quirk as anything in subtypesof(/datum/quirk)) - if(quirk == initial(quirk.abstract_parent_type)) - continue - + for(var/datum/quirk/quirk as anything in valid_subtypesof(/datum/quirk)) var/quirk_icon = initial(quirk.icon) if(findtext(quirk_icon, "tg-") == 1) // TODO: Validate these as well continue diff --git a/code/modules/unit_tests/greyscale_config.dm b/code/modules/unit_tests/greyscale_config.dm index edd6df20c5aa..1c9663806a20 100644 --- a/code/modules/unit_tests/greyscale_config.dm +++ b/code/modules/unit_tests/greyscale_config.dm @@ -25,6 +25,13 @@ if(belt && !belt.icon_states[inside_belt_icon_state]) TEST_FAIL("[belt.DebugName()] is missing a sprite for the belt overlay for [item_path]. Expected icon state: '[inside_belt_icon_state]'") + // DARKPACK EDIT ADD START - ONFLOOR_ICONS + var/datum/greyscale_config/onfloor = SSgreyscale.configurations["[initial(item_path.greyscale_config_onfloor)]"] + var/onflooricon_state = item_path::onflooricon_state || item_path::post_init_icon_state || item_path::icon_state + if(onfloor && !onfloor.icon_states[onflooricon_state]) + TEST_FAIL("[onfloor.DebugName()] is missing a sprite for the onfloor icon for [item_path]. Expected icon state: '[onflooricon_state]'") + // DARKPACK EDIT ADD END + /// Makes sure objects using greyscale configs have, if any, the correct number of colors /datum/unit_test/greyscale_color_count diff --git a/code/modules/unit_tests/heretic_knowledge.dm b/code/modules/unit_tests/heretic_knowledge.dm index 134835a61693..c1f900d99903 100644 --- a/code/modules/unit_tests/heretic_knowledge.dm +++ b/code/modules/unit_tests/heretic_knowledge.dm @@ -26,11 +26,7 @@ all_knowledges += flatten_list(draft) - var/list/all_possible_knowledge = typesof(/datum/heretic_knowledge) - - for(var/datum/heretic_knowledge/knowledge_type as anything in all_possible_knowledge) - if(initial(knowledge_type.abstract_parent_type) == knowledge_type) - all_possible_knowledge -= knowledge_type + var/list/all_possible_knowledge = valid_subtypesof(/datum/heretic_knowledge) var/list/list_to_check = get_knowledge_unlockables(start_knowledges, all_knowledges) diff --git a/code/modules/unit_tests/id_access.dm b/code/modules/unit_tests/id_access.dm new file mode 100644 index 000000000000..1a25cd298514 --- /dev/null +++ b/code/modules/unit_tests/id_access.dm @@ -0,0 +1,52 @@ +/datum/unit_test/id_access + +/datum/unit_test/id_access/Run() + var/mob/living/carbon/human/consistent/subject = EASY_ALLOCATE() + subject.equip_to_appropriate_slot(new /obj/item/clothing/under/color/grey) + var/obj/item/card/id/advanced/card = EASY_ALLOCATE() + + card.set_access(list(ACCESS_HYDROPONICS), FORCE_ADD_ALL) + TEST_ASSERT_EQUAL(length(card.GetAccess()), 1, "ID card access length incorrect after setting access.") + + subject.put_in_hands(card) + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access on holding ID card.") + subject.dropItemToGround(card) + TEST_ASSERT(check_access(subject, null), "Subject still had access after dropping ID card.") + subject.equip_to_appropriate_slot(card) + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access on equipping ID card.") + subject.dropItemToGround(card) + TEST_ASSERT(check_access(subject, null), "Subject still had access after unequipping ID card.") + + var/obj/item/storage/wallet/wallet = EASY_ALLOCATE() + card.forceMove(wallet) + subject.equip_to_appropriate_slot(wallet) + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access on equipping wallet with ID card inside.") + subject.dropItemToGround(wallet) + TEST_ASSERT(check_access(subject, null), "Subject still had access after unequipping wallet with ID card inside.") + subject.equip_to_appropriate_slot(wallet) + click_wrapper(subject, card) // withdraw id card from wallet + TEST_ASSERT(card.loc == subject && card == subject.get_active_held_item(), "Subject failed to withdraw ID card from wallet.") + click_wrapper(subject, wallet) // reinsert id card into wallet + TEST_ASSERT(card.loc == wallet, "Subject failed to reinsert ID card into wallet.") + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access after reinserting ID card into equipped wallet.") + subject.dropItemToGround(wallet) + + var/obj/item/modular_computer/pda/pda = EASY_ALLOCATE() + pda.insert_id(card) + subject.equip_to_appropriate_slot(pda) + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access on equipping PDA with ID card inside.") + subject.dropItemToGround(pda) + TEST_ASSERT(check_access(subject, null), "Subject still had access after unequipping PDA with ID card inside.") + subject.equip_to_appropriate_slot(pda) + pda.remove_id(subject) + TEST_ASSERT(card.loc == subject && card == subject.get_active_held_item(), "Subject failed to withdraw ID card from PDA.") + click_wrapper(subject, pda) // reinsert id card into pda + TEST_ASSERT(card.loc == pda, "Subject failed to reinsert ID card into PDA.") + TEST_ASSERT(check_access(subject, ACCESS_HYDROPONICS), "Subject did not have the access after reinserting ID card into equipped PDA.") + subject.dropItemToGround(pda) + +/datum/unit_test/id_access/proc/check_access(mob/living/carbon/human/consistent/subject, expected) + var/list/subject_access = subject.get_access() + if(isnull(expected)) + return length(subject_access) == 0 + return length(subject_access) == 1 && subject_access[1] == expected diff --git a/code/modules/unit_tests/id_card.dm b/code/modules/unit_tests/id_card.dm new file mode 100644 index 000000000000..b0658b558990 --- /dev/null +++ b/code/modules/unit_tests/id_card.dm @@ -0,0 +1,6 @@ +/// Ensures the captain's spare id keeps the name "captain's spare id", and doesn't get changed by id label +/datum/unit_test/spare_id_name + +/datum/unit_test/spare_id_name/Run() + var/obj/item/card/id/advanced/gold/captains_spare/card = EASY_ALLOCATE() + TEST_ASSERT_EQUAL(card.name, initial(card.name), "Captain's spare ID card should not change its name by default.") diff --git a/code/modules/unit_tests/quirks.dm b/code/modules/unit_tests/quirks.dm index 65864d2d58fa..3fe8c465b158 100644 --- a/code/modules/unit_tests/quirks.dm +++ b/code/modules/unit_tests/quirks.dm @@ -4,10 +4,7 @@ /datum/unit_test/quirk_icons/Run() var/list/used_icons = list() - for (var/datum/quirk/quirk_type as anything in subtypesof(/datum/quirk)) - if (initial(quirk_type.abstract_parent_type) == quirk_type) - continue - + for (var/datum/quirk/quirk_type as anything in valid_subtypesof(/datum/quirk)) var/icon = initial(quirk_type.icon) if (isnull(icon)) @@ -24,12 +21,10 @@ /datum/unit_test/quirk_initial_medical_records /datum/unit_test/quirk_initial_medical_records/Run() + /* DARKPACK EDIT REMOVAL - MERITS/FLAWS - we don't need this and darkpack quirk splat/clan exclusion makes it impossible to add to a random test character with no splats var/mob/living/carbon/human/patient = allocate(/mob/living/carbon/human/consistent) - for(var/datum/quirk/quirk_type as anything in subtypesof(/datum/quirk)) - if (initial(quirk_type.abstract_parent_type) == quirk_type) - continue - + for(var/datum/quirk/quirk_type as anything in valid_subtypesof(/datum/quirk)) if(!isnull(quirk_type.medical_record_text)) continue @@ -41,6 +36,7 @@ TEST_ASSERT_NOTNULL(quirk.medical_record_text,"[quirk_type] has no medical record description!") patient.remove_quirk(quirk_type) + */ /// Ensures the blood deficiency quirk updates its mail goodies correctly /datum/unit_test/blood_deficiency_mail @@ -53,6 +49,7 @@ ) /datum/unit_test/blood_deficiency_mail/Run() + /* DARKPACK EDIT REMOVAL - MERITS/FLAWS - we are not using /tg/ quirks var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent) dummy.add_quirk(/datum/quirk/blooddeficiency) var/datum/quirk/blooddeficiency/quirk = dummy.get_quirk(/datum/quirk/blooddeficiency) @@ -79,6 +76,7 @@ if(!isnull(species_to_test[last_species])) TEST_ASSERT(!(species_to_test[last_species] in quirk.mail_goodies), \ "Blood deficiency quirk did not update correctly for [species_type]! ([last_species] did not get its blood bag removed)") + */ /// Ensures that all quirks correctly initialized when added /datum/unit_test/quirk_validity @@ -88,10 +86,12 @@ // Assigning this manually as config is empty GLOB.uncommon_roundstart_languages = list(/datum/language/uncommon) - for (var/datum/quirk/quirk_type as anything in subtypesof(/datum/quirk)) - if (initial(quirk_type.abstract_parent_type) == quirk_type) - continue - + for (var/datum/quirk/darkpack/quirk_type as anything in valid_subtypesof(/datum/quirk/darkpack)) // DARKPACK EDIT CHANGE - Original: for (var/datum/quirk/quirk_type as anything in valid_subtypesof(/datum/quirk)) + // DARKPACK EDIT ADD START - MERITS/FLAWS + var/list/forbidden_splats_test = quirk_type.forbidden_splats + var/list/allowed_splats_test = quirk_type.allowed_splats + var/list/excluded_clans_test = quirk_type.excluded_clans + // DARKPACK EDIT ADD END - MERITS/FLAWS var/mob/dead/new_player/abstract_player = allocate(/mob/dead/new_player) var/datum/client_interface/roundstart_mock_client = new() abstract_player.mock_client = roundstart_mock_client @@ -99,16 +99,58 @@ var/mob/living/carbon/human/new_character = allocate(/mob/living/carbon/human/consistent) new_character.mind_initialize() abstract_player.new_character = new_character - if (!new_character.add_quirk(quirk_type, roundstart_mock_client)) - TEST_FAIL("Failed to initialize quirk [quirk_type] on a roundstart character!") + + // DARKPACK EDIT ADD START - MERITS/FLAWS + // if allowed splats, add the allowed splat, then test, failure if its not added + if(allowed_splats_test) + for(var/datum/splat/allowed_splat in allowed_splats_test) + new_character.add_splat(allowed_splat) + if (!new_character.add_quirk(quirk_type, roundstart_mock_client)) + TEST_FAIL("Failed to initialize quirk [quirk_type] on a roundstart character with allowed splat [allowed_splat]!") + new_character.clear_splats() //clear after for the next test + + // if forbidden splats, add the disallowed splat, then test, failure if its added + if(forbidden_splats_test) + for(var/datum/splat/forbidden_splat in forbidden_splats_test) + new_character.add_splat(forbidden_splat) + if (new_character.add_quirk(quirk_type, roundstart_mock_client)) + TEST_FAIL("Successfully initialized quirk [quirk_type] on a roundstart character that had a forbidden splat [forbidden_splat]!") + new_character.clear_splats() + + // if all are null, then its an allowed quirk for all, failure if cannot add + if(!forbidden_splats_test && !allowed_splats_test && !excluded_clans_test) + if (!new_character.add_quirk(quirk_type, roundstart_mock_client)) + TEST_FAIL("Failed to initialize quirk [quirk_type] on a roundstart character!") + // DARKPACK EDIT ADD END - MERITS/FLAWS var/mob/living/carbon/human/latejoin_character = allocate(/mob/living/carbon/human/consistent) var/datum/client_interface/latejoin_mock_client = new() latejoin_mock_client.prefs = new(latejoin_mock_client) latejoin_character.mock_client = latejoin_mock_client latejoin_character.mind_initialize() - if (!latejoin_character.add_quirk(quirk_type, latejoin_mock_client)) - TEST_FAIL("Failed to initialize quirk [quirk_type] on a latejoin character!") + + // DARKPACK EDIT ADD - MERITS/FLAWS + // if allowed splats, add the allowed splat, then test, failure if its not added + if(allowed_splats_test) + for(var/datum/splat/allowed_splat in allowed_splats_test) + latejoin_character.add_splat(allowed_splat) + if (!latejoin_character.add_quirk(quirk_type, latejoin_mock_client)) + TEST_FAIL("Failed to initialize quirk [quirk_type] on a latejoin character with allowed splat [allowed_splat]!") + latejoin_character.clear_splats() + + // if forbidden splats, add the allowed splat, then test, failure if its added + if(forbidden_splats_test) + for(var/datum/splat/forbidden_splat in forbidden_splats_test) + latejoin_character.add_splat(forbidden_splat) + if (latejoin_character.add_quirk(quirk_type, latejoin_mock_client)) + TEST_FAIL("Successfully initialized quirk [quirk_type] on a latejoin character that had a forbidden splat [forbidden_splat]!") + latejoin_character.clear_splats() + + // if all are null, then its an allowed quirk for all, failure if cannot add + if(!forbidden_splats_test && !allowed_splats_test && !excluded_clans_test) + if (!latejoin_character.add_quirk(quirk_type, latejoin_mock_client)) + TEST_FAIL("Failed to initialize quirk [quirk_type] on a latejoin character!") + // DARKPACK EDIT ADD END - MERITS/FLAWS // Clean up after ourselves GLOB.uncommon_roundstart_languages.Cut() diff --git a/code/modules/unit_tests/screenshot_humanoids.dm b/code/modules/unit_tests/screenshot_humanoids.dm index c392b888e413..95ffe8e64d26 100644 --- a/code/modules/unit_tests/screenshot_humanoids.dm +++ b/code/modules/unit_tests/screenshot_humanoids.dm @@ -2,7 +2,7 @@ /datum/unit_test/screenshot_humanoids /datum/unit_test/screenshot_humanoids/Run() - var/list/testable_species = subtypesof(/datum/species) + var/list/testable_species = valid_subtypesof(/datum/species) // DARKPACK EDIT CHANGE // Test lizards as their own thing so we can get more coverage on their features var/mob/living/carbon/human/lizard = allocate(/mob/living/carbon/human/dummy/consistent) diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_nightmare.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_nightmare.png index 853ef8453830..4e969bd71fb6 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_nightmare.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_nightmare.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_spaceninja.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_spaceninja.png index 8c8ad95aff7c..60e1196d47fc 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_spaceninja.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_spaceninja.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_bestial.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_bestial.png new file mode 100644 index 000000000000..456a5ce1a860 Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_bestial.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_dire.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_dire.png new file mode 100644 index 000000000000..f881901300db Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_dire.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_feral.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_feral.png new file mode 100644 index 000000000000..bfb6d4dd4bab Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_feral.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_homid.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_homid.png new file mode 100644 index 000000000000..18687734ab1f Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_homid.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_war.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_war.png new file mode 100644 index 000000000000..f26cac5b753e Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_war.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow.png index 7f31e342050c..70beeb061b9f 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow_nightmare.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow_nightmare.png index 5cdeedeffca7..7490e063de0b 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow_nightmare.png and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_shadow_nightmare.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_husk_body.png b/code/modules/unit_tests/screenshots/screenshot_husk_body.png index f695cacac7bd..2ebf1d5ef181 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_husk_body.png and b/code/modules/unit_tests/screenshots/screenshot_husk_body.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png b/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png index 07d36bb9664d..74d68bff9e0f 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png and b/code/modules/unit_tests/screenshots/screenshot_husk_body_missing_limbs.png differ diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm index e90473a7ba06..f89f59a4cbfe 100644 --- a/code/modules/unit_tests/spawn_humans.dm +++ b/code/modules/unit_tests/spawn_humans.dm @@ -13,3 +13,4 @@ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent) TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_AGEUSIA, NO_TONGUE_TRAIT), "Dummy has ageusia on init, when it should've been removed by its default tongue.") TEST_ASSERT(!dummy.is_blind_from(NO_EYES), "Dummy is blind on init, when it should've been removed by its default eyes.") + TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_DEAF, NO_EARS), "Dummy is deaf on init, when it should've been removed by its default ears.") diff --git a/code/modules/uplink/uplink_items/spy_unique.dm b/code/modules/uplink/uplink_items/spy_unique.dm index abc7449e567a..78afc487c123 100644 --- a/code/modules/uplink/uplink_items/spy_unique.dm +++ b/code/modules/uplink/uplink_items/spy_unique.dm @@ -174,7 +174,7 @@ /datum/uplink_item/spy_unique/katana name = "Katana" desc = "A really sharp Katana. Did I mention it's sharp?" - item = /obj/item/katana + item = /obj/item/storage/belt/sheath/katana cost = /datum/uplink_item/dangerous/doublesword::cost // Puts it in the same pool as Desword uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm index 1567eed0f97b..8137bca7509d 100644 --- a/code/modules/vehicles/atv.dm +++ b/code/modules/vehicles/atv.dm @@ -108,9 +108,7 @@ return PROCESS_KILL if(SPT_PROB(10, seconds_per_tick)) return - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = src) - smoke.start() + do_smoke(0, src, src) /obj/vehicle/ridden/atv/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked) if(prob(50) || !LAZYLEN(buckled_mobs)) diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm index 67424be210d7..0672586220c9 100644 --- a/code/modules/vehicles/cars/clowncar.dm +++ b/code/modules/vehicles/cars/clowncar.dm @@ -97,11 +97,7 @@ . = ..() if(prob(33)) visible_message(span_danger("[src] spews out a ton of space lube!")) - var/datum/effect_system/fluid_spread/foam/foam = new - var/datum/reagents/foamreagent = new /datum/reagents(25) - foamreagent.add_reagent(/datum/reagent/lube, 25) - foam.set_up(4, holder = src, location = loc, carry = foamreagent) - foam.start() + do_foam(4, src, loc, /datum/reagent/lube, 25) /obj/vehicle/sealed/car/clowncar/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!istype(tool, /obj/item/food/grown/banana)) @@ -218,12 +214,7 @@ new /obj/item/grown/bananapeel/specialpeel(loc) if(2) visible_message(span_danger("[user] presses one of the colorful buttons on [src], and unknown chemicals flood out of it.")) - var/datum/reagents/randomchems = new/datum/reagents(300) - randomchems.my_atom = src - randomchems.add_reagent(get_random_reagent_id(), 100) - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(200, holder = src, location = loc, carry = randomchems) - foam.start(log = TRUE) + do_foam(200, src, loc, get_random_reagent_id(), 100, log = TRUE) if(3) visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car turns on its singularity disguise system.")) icon = 'icons/obj/machines/engine/singularity.dmi' @@ -231,13 +222,8 @@ addtimer(CALLBACK(src, PROC_REF(reset_icon)), 10 SECONDS) if(4) visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car spews out a cloud of laughing gas.")) - var/datum/reagents/funnychems = new/datum/reagents(300) - funnychems.my_atom = src - funnychems.add_reagent(/datum/reagent/consumable/superlaughter, 50) - var/datum/effect_system/fluid_spread/smoke/chem/smoke = new() - smoke.set_up(4, holder = src, location = src, carry = funnychems) - smoke.attach(src) - smoke.start(log = TRUE) + do_chem_smoke(4, src, src, /datum/reagent/consumable/superlaughter, 50, log = TRUE) + if(5) visible_message(span_danger("[user] presses one of the colorful buttons on [src], and the clown car starts dropping an oil trail.")) RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(cover_in_oil)) diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index 44690288175e..7bd4ffd709a4 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -59,7 +59,7 @@ var/mecha_flags = CAN_STRAFE | IS_ENCLOSED | HAS_LIGHTS | MMI_COMPATIBLE | BEACON_TRACKABLE | AI_COMPATIBLE | BEACON_CONTROLLABLE ///Spark effects are handled by this datum - var/datum/effect_system/spark_spread/spark_system + var/datum/effect_system/basic/spark_spread/spark_system ///How powerful our lights are var/lights_power = 6 ///Just stop the mech from doing anything @@ -230,13 +230,9 @@ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) RegisterSignal(src, COMSIG_LIGHT_EATER_ACT, PROC_REF(on_light_eater)) - spark_system = new - spark_system.set_up(2, 0, src) + spark_system = new(src, 2, FALSE) spark_system.attach(src) - - smoke_system = new - smoke_system.set_up(3, holder = src, location = src) - smoke_system.attach(src) + smoke_system = new(src, 3, holder = src) cabin_air = new(cabin_volume) diff --git a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm index 7e68aaf91cbc..d85189ea8ea7 100644 --- a/code/modules/vehicles/mecha/equipment/weapons/weapons.dm +++ b/code/modules/vehicles/mecha/equipment/weapons/weapons.dm @@ -265,7 +265,7 @@ playsound(chassis, 'sound/items/airhorn/airhorn.ogg', 100, TRUE) to_chat(source, "[icon2html(src, source)]HONK") for(var/mob/living/carbon/M in ohearers(6, chassis)) - if(!M.can_hear()) + if(HAS_TRAIT(M, TRAIT_DEAF)) continue var/turf/turf_check = get_turf(M) if(isspaceturf(turf_check) && !turf_check.Adjacent(src)) //in space nobody can hear you honk. diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm index d3f1ff5ba10a..16d084cf5181 100644 --- a/code/modules/vehicles/scooter.dm +++ b/code/modules/vehicles/scooter.dm @@ -42,7 +42,7 @@ density = FALSE custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10) ///Sparks datum for when we grind on tables - var/datum/effect_system/spark_spread/sparks + var/datum/effect_system/basic/spark_spread/sparks ///Whether the board is currently grinding var/grinding = FALSE ///Stores the time of the last crash plus a short cooldown, affects availability and outcome of certain actions @@ -53,19 +53,23 @@ var/instability = 10 ///If true, riding the skateboard with walk intent on will prevent crashing. var/can_slow_down = TRUE + ///The actual item for the skateboard + var/obj/item/melee/skateboard/board_item -/obj/vehicle/ridden/scooter/skateboard/Initialize(mapload) +/obj/vehicle/ridden/scooter/skateboard/Initialize(mapload, obj/item/melee/skateboard/board_item) . = ..() - sparks = new - sparks.set_up(1, 0, src) + sparks = new(src, 1, FALSE) sparks.attach(src) + if(!istype(board_item)) + src.board_item = new board_item_type(src) + else + src.board_item = board_item /obj/vehicle/ridden/scooter/skateboard/make_ridable() AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter/skateboard) /obj/vehicle/ridden/scooter/skateboard/Destroy() - if(sparks) - QDEL_NULL(sparks) + QDEL_NULL(sparks) return ..() /obj/vehicle/ridden/scooter/skateboard/relaymove(mob/living/user, direction) @@ -176,7 +180,7 @@ if(has_buckled_mobs()) to_chat(skater, span_warning("You can't lift this up when somebody's on it.")) return - skater.put_in_hands(new board_item_type(get_turf(skater))) + skater.put_in_hands(board_item) qdel(src) /obj/vehicle/ridden/scooter/skateboard/pro diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm index b1af55c67e9d..4b69d9907623 100644 --- a/code/modules/vehicles/sealed.dm +++ b/code/modules/vehicles/sealed.dm @@ -41,7 +41,7 @@ if(istype(A, /obj/machinery/door)) var/obj/machinery/door/conditionalwall = A for(var/mob/occupant as anything in return_controllers_with_flag(access_provider_flags)) - if(conditionalwall.try_safety_unlock(occupant)) + if(conditionalwall.try_safety_unlock(occupant) || !conditionalwall.can_open_with_hands) return conditionalwall.bumpopen(occupant) diff --git a/code/modules/vehicles/secway.dm b/code/modules/vehicles/secway.dm index cf784e77ca47..86e26b735d3c 100644 --- a/code/modules/vehicles/secway.dm +++ b/code/modules/vehicles/secway.dm @@ -30,9 +30,7 @@ return PROCESS_KILL if(SPT_PROB(10, seconds_per_tick)) return - var/datum/effect_system/fluid_spread/smoke/smoke = new - smoke.set_up(0, holder = src, location = src) - smoke.start() + do_smoke(0, src, src) /obj/vehicle/ridden/secway/welder_act(mob/living/user, obj/item/W) if(user.combat_mode) diff --git a/code/modules/vending/boozeomat.dm b/code/modules/vending/boozeomat.dm index 09c7cbacb8f0..c03be93356ee 100644 --- a/code/modules/vending/boozeomat.dm +++ b/code/modules/vending/boozeomat.dm @@ -45,10 +45,11 @@ "icon" = "bottle-water", "products" = list( /obj/item/reagent_containers/cup/glass/ice = 10, + /obj/item/reagent_containers/cup/glass/bottle/juice/lemonjuice = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/limejuice = 4, + /obj/item/reagent_containers/cup/glass/bottle/juice/orangejuice = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/menthol = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/cream = 4, - /obj/item/reagent_containers/cup/glass/bottle/juice/orangejuice = 4, /obj/item/reagent_containers/cup/glass/bottle/juice/tomatojuice = 4, /obj/item/reagent_containers/cup/soda_cans/sodawater = 15, /obj/item/reagent_containers/cup/soda_cans/sol_dry = 8, diff --git a/code/modules/vending/liberation_toy.dm b/code/modules/vending/liberation_toy.dm index 9ddb14e78c9c..6544830232fb 100644 --- a/code/modules/vending/liberation_toy.dm +++ b/code/modules/vending/liberation_toy.dm @@ -20,7 +20,7 @@ contraband = list( /obj/item/toy/balloon/syndicate = 1, /obj/item/gun/ballistic/shotgun/toy/crossbow/riot = 8, - /obj/item/toy/katana = 12, + /obj/item/storage/belt/sheath/katana/toy = 12, ) premium = list( /obj/item/toy/cards/deck/syndicate = 12, diff --git a/code/modules/vending/toys.dm b/code/modules/vending/toys.dm index fae9bbc5d466..500dac812948 100644 --- a/code/modules/vending/toys.dm +++ b/code/modules/vending/toys.dm @@ -20,7 +20,7 @@ contraband = list( /obj/item/toy/balloon/syndicate = 1, /obj/item/gun/ballistic/shotgun/toy/crossbow = 8, - /obj/item/toy/katana = 12, + /obj/item/storage/belt/sheath/katana/toy = 12, /obj/item/ammo_box/foambox/riot/mini = 20, ) premium = list( diff --git a/code/modules/vending/youtool.dm b/code/modules/vending/youtool.dm index 819ddbe22089..c339c2d687a9 100644 --- a/code/modules/vending/youtool.dm +++ b/code/modules/vending/youtool.dm @@ -24,7 +24,7 @@ ) premium = list( /obj/item/storage/belt/utility = 2, - /obj/item/multitool = 2, + /obj/item/multitool = 4, /obj/item/weldingtool/hugetank = 2, /obj/item/clothing/head/utility/welding = 2, /obj/item/clothing/gloves/color/yellow = 1, diff --git a/config/darkpack_config.txt b/config/darkpack_config.txt index 7cb2c1e362db..b540b9b946dd 100644 --- a/config/darkpack_config.txt +++ b/config/darkpack_config.txt @@ -21,6 +21,8 @@ TIME_TILL_ROUNDEND 216000 ## World of Darkness splats ROUNDSTART_SPLATS splat_kindred ROUNDSTART_SPLATS splat_ghoul +ROUNDSTART_SPLATS splat_kinfolk +ROUNDSTART_SPLATS splat_garou ## If dead people and ghosts can LOOC to people who are alive. DISABLE_GHOST_LOOC @@ -31,4 +33,7 @@ DISABLE_GHOST_LOOC MAX_SAVE_SLOTS 5 EXTRA_SAVE_SLOTS_BYOND_MEMBER 5 +## log stat and pref changes +LOG_STATS +#HUMANITY_SUNLIGHT_RESISTANCE diff --git a/config/game_options.txt b/config/game_options.txt index acb47502f499..a7e9527451c9 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -557,7 +557,7 @@ NEGATIVE_STATION_TRAITS 3 1 # Uncomment to disable Quirk point balancing for the server and clients. # If enabled, players will be able to select positive quirks without first selecting negative quirks. # If enabled, randomized Quirks will still use points internally, in order to maintain balance. -#DISABLE_QUIRK_POINTS +DISABLE_QUIRK_POINTS # The maximum amount of positive quirks one character can have at roundstart. # If set to -1, then players will be able to select any quantity of positive quirks. diff --git a/html/changelogs/AutoChangeLog-pr-489.yml b/html/changelogs/AutoChangeLog-pr-489.yml new file mode 100644 index 000000000000..55ad941befe3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-489.yml @@ -0,0 +1,13 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "Readds garou" + - rscadd: "Moon phase is calculated mostly accurately based on in-game day" + - rscadd: "Rage gained from looking at the moon is now based on phase" + - rscadd: "Howl emote that can be used by dogs, dogs (garou), or dogs (gangrel)" + - rscadd: "Re-implements 1 gift for each auspice" + - bugfix: "Blood-pool icon is properly updated when its added to you" + - admin: "Adjustments to stats and important prefs can be properly viewed in the log viewers" + - balance: "Makes generic helpers to represent \"Heal this amount of \"dots\" of damage\". Gives this to garou at 1 dot every \"turn\" (5 seconds at the time of writing)" + - balance: "Punching basic mobs and humans now calculate damage based on strength" + - balance: "W20 silver bullet handling. No agg damage in breed form. 5 bullets are a guaranteed loss of gnosis." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-531.yml b/html/changelogs/AutoChangeLog-pr-531.yml deleted file mode 100644 index 6455110dc219..000000000000 --- a/html/changelogs/AutoChangeLog-pr-531.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: "dwinters99, KCartridge, Major00" -delete-after: True -changes: - - rscadd: "DP Wolves" - - rscadd: "PSG1 magazine and ammo" - - code_imp: "gun code organization" - - rscadd: "undense bin" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-542.yml b/html/changelogs/AutoChangeLog-pr-542.yml new file mode 100644 index 000000000000..b6b359cac6d3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-542.yml @@ -0,0 +1,4 @@ +author: "chazzyjazzy" +delete-after: True +changes: + - code_imp: "makes the basic structure for merits and flaws" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-549.yml b/html/changelogs/AutoChangeLog-pr-549.yml new file mode 100644 index 000000000000..504a168d77bb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-549.yml @@ -0,0 +1,4 @@ +author: "chazzyjazzy, INFARED_BARON" +delete-after: True +changes: + - rscadd: "reimplements SWAT and NG ERTs with new roles" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-563.yml b/html/changelogs/AutoChangeLog-pr-563.yml deleted file mode 100644 index 4c2ec020eecd..000000000000 --- a/html/changelogs/AutoChangeLog-pr-563.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "XeonMations, yooriss, RatFromTheJungle, OrbisAnimal, FlufflesTheDog, sunnyaries" -delete-after: True -changes: - - rscadd: "Added Do emotes" - - rscadd: "Added subtle and subtler emotes" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-619.yml b/html/changelogs/AutoChangeLog-pr-619.yml new file mode 100644 index 000000000000..d7d8de36391b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-619.yml @@ -0,0 +1,6 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - qol: "you can set how many turns blood power is active for with right click" + - balance: "bonus to damage from bloodpower is based entirely on the stat buffs it grants" + - balance: "allows you to set buff granted by blood power according to your gen spending limit" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-652.yml b/html/changelogs/AutoChangeLog-pr-652.yml deleted file mode 100644 index 03006e15b8da..000000000000 --- a/html/changelogs/AutoChangeLog-pr-652.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "FalloutFalcon" -delete-after: True -changes: - - rscadd: "Pharmacy has sutures, gauze, and mesh" - - balance: "bruise packs and ointment is cheaper" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-654.yml b/html/changelogs/AutoChangeLog-pr-654.yml new file mode 100644 index 000000000000..dc9b3f3305e9 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-654.yml @@ -0,0 +1,6 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "Readds old interactions of Lasombra's tech bane" + - balance: "Computers require at-least 1 dot in computer to not have a do_after" + - qol: "Candles last a full hour" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-657.yml b/html/changelogs/AutoChangeLog-pr-657.yml deleted file mode 100644 index a50383d4d91d..000000000000 --- a/html/changelogs/AutoChangeLog-pr-657.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "FalloutFalcon" -delete-after: True -changes: - - sound: "the canon round sound effect is 1/4 the default volume as the code intended" - - sound: "other sounds are now the intended volume" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-658.yml b/html/changelogs/AutoChangeLog-pr-658.yml deleted file mode 100644 index 4d550f9f61aa..000000000000 --- a/html/changelogs/AutoChangeLog-pr-658.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Knightscholar" -delete-after: True -changes: - - rscadd: "Hand tazers have cells in them" - - balance: "Hand tazers can hit twice per regular cell, instead of once." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-668.yml b/html/changelogs/AutoChangeLog-pr-668.yml new file mode 100644 index 000000000000..00b829cd6c48 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-668.yml @@ -0,0 +1,5 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "Particle effect when phones are ringing" + - image: "Tweaks smartphone onworld" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-671.yml b/html/changelogs/AutoChangeLog-pr-671.yml new file mode 100644 index 000000000000..55d48c175e95 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-671.yml @@ -0,0 +1,5 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - image: "Adds onfloors for berets" + - code_imp: "Onfloor sprites now support GAGS" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-675.yml b/html/changelogs/AutoChangeLog-pr-675.yml new file mode 100644 index 000000000000..ba2dac3e5a9e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-675.yml @@ -0,0 +1,4 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "Pool tables has balls and cue's that creates a little 8-Ball mini-game." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-651.yml b/html/changelogs/AutoChangeLog-pr-678.yml similarity index 54% rename from html/changelogs/AutoChangeLog-pr-651.yml rename to html/changelogs/AutoChangeLog-pr-678.yml index 5c8a84eda268..7af55ea813c0 100644 --- a/html/changelogs/AutoChangeLog-pr-651.yml +++ b/html/changelogs/AutoChangeLog-pr-678.yml @@ -1,4 +1,4 @@ author: "FalloutFalcon" delete-after: True changes: - - sound: "Makes thunder quieter" \ No newline at end of file + - bugfix: "Fixes final death sprite issue" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-681.yml b/html/changelogs/AutoChangeLog-pr-681.yml new file mode 100644 index 000000000000..db7dd41a0c28 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-681.yml @@ -0,0 +1,4 @@ +author: "chazzyjazzy" +delete-after: True +changes: + - rscadd: "Readds character height customization as a numerical preference slider" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-682.yml b/html/changelogs/AutoChangeLog-pr-682.yml new file mode 100644 index 000000000000..f95a381049d8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-682.yml @@ -0,0 +1,4 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "Readds the funny cardboard box waddle and sound effect" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-684.yml b/html/changelogs/AutoChangeLog-pr-684.yml new file mode 100644 index 000000000000..37e6829e5068 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-684.yml @@ -0,0 +1,4 @@ +author: "FalloutFalcon" +delete-after: True +changes: + - rscadd: "re-adds reporting masquerade violations and reinforcements" \ No newline at end of file diff --git a/html/changelogs/archive/2026-02.yml b/html/changelogs/archive/2026-02.yml index 02371a0b3f82..b6ddf6665193 100644 --- a/html/changelogs/archive/2026-02.yml +++ b/html/changelogs/archive/2026-02.yml @@ -42,3 +42,60 @@ 2026-02-09: XeonMations: - bugfix: Typhonic beasts now break the masquerade. +2026-02-16: + Beautiful TG coders: + - code_imp: TG Pull. Thank you TG. + FalloutFalcon: + - bugfix: tiles next to the graveyard gate are made dense automaticly + - refactor: Gravekeeper gate should behavior more consistently in line with atom + integrity + - rscadd: Custom dice rolling verbs for scenes and filling the gaps in unimplemented + content/rolls. + - rscadd: Most rolls now play a dice roll sound effect + - qol: Massively prettified the roll to_chat and fills it with useful information + for rolls + - refactor: Some dice rolls now use a new datumized system of rolling, most use + it with a wrapper through the old system so they dont gain as much new behavoir + - balance: Identifying artifacts only requires 3 occult but now has a roll on a + scene length cooldown + - rscadd: Pharmacy has sutures, gauze, and mesh + - balance: bruise packs and ointment is cheaper + - sound: Makes thunder quieter + - qol: first key checked on a door has no delay + - bugfix: abyss tentacles should be able to acctually make a sound as intended + - bugfix: fixed tranforming BP dupe + - bugfix: typhon brew is craftable + - bugfix: humanity 10 vamps wont burn in the sun (IF ENABLED VIA CONFIG) + - image: blood hunt skull uses its onfloor + - code_imp: more non-combat item interactions are no longer in attack-by + - code_imp: all changes in BP should update your hud + - bugfix: mobs are now cleared from the manifest upon matrixing + - bugfix: matrixing clears a breacher from the masq + - sound: the canon round sound effect is 1/4 the default volume as the code intended + - sound: other sounds are now the intended volume + - balance: Doubles the define for TTRPG damage to represent 10 instead of 5 force + FalloutFalcon, dwinters99: + - balance: More melee can be stored on the belt + FalloutFalcon, tzui5020, EnchantedCrocolisk: + - rscadd: New outfits have been added to the loadout and Hot Ishu inspired by the + scene girl era. + - rscadd: Three new shoes. + - rscadd: Two new hoodies. + Knightscholar: + - rscadd: Hand tazers have cells in them + - balance: Hand tazers can hit twice per regular cell, instead of once. + - rscadd: Added the ability to toggle Aura Perception + - rscdel: Removed Industrial SFX from The Spirit's Touch + SELFHELL: + - rscadd: Pentex FIRST Team ERT + XeonMations: + - qol: Made our character preferences nicer to look at. + - code_imp: Fixed some hard deletes + XeonMations, yooriss, RatFromTheJungle, OrbisAnimal, FlufflesTheDog, sunnyaries: + - rscadd: Added Do emotes + - rscadd: Added subtle and subtler emotes + dwinters99, KCartridge, Major00: + - rscadd: DP Wolves + - rscadd: PSG1 magazine and ammo + - code_imp: gun code organization + - rscadd: undense bin diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 23560e411eae..16bbde48e016 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi index a7860bd720d1..0c36dc8d6864 100644 Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi index 5c45e80f426a..b3113ed457a8 100644 Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi index a02c4fa668d5..06b8b4e1d9bc 100644 Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ diff --git a/icons/mob/human/bodyparts.dmi b/icons/mob/human/bodyparts.dmi index e7de7089b184..cc8a60276a81 100644 Binary files a/icons/mob/human/bodyparts.dmi and b/icons/mob/human/bodyparts.dmi differ diff --git a/icons/mob/human/species/monkey/bodyparts.dmi b/icons/mob/human/species/monkey/bodyparts.dmi index 9e95cbd0c72f..9dba64eb7d35 100644 Binary files a/icons/mob/human/species/monkey/bodyparts.dmi and b/icons/mob/human/species/monkey/bodyparts.dmi differ diff --git a/icons/mob/inhands/antag/nightmare_lefthand.dmi b/icons/mob/inhands/antag/nightmare_lefthand.dmi new file mode 100644 index 000000000000..b98dee3aa0aa Binary files /dev/null and b/icons/mob/inhands/antag/nightmare_lefthand.dmi differ diff --git a/icons/mob/inhands/antag/nightmare_righthand.dmi b/icons/mob/inhands/antag/nightmare_righthand.dmi new file mode 100644 index 000000000000..87f650059fe4 Binary files /dev/null and b/icons/mob/inhands/antag/nightmare_righthand.dmi differ diff --git a/icons/mob/inhands/equipment/belt_lefthand.dmi b/icons/mob/inhands/equipment/belt_lefthand.dmi index 7675b73543b5..e312f6df4e46 100644 Binary files a/icons/mob/inhands/equipment/belt_lefthand.dmi and b/icons/mob/inhands/equipment/belt_lefthand.dmi differ diff --git a/icons/mob/inhands/equipment/belt_righthand.dmi b/icons/mob/inhands/equipment/belt_righthand.dmi index c1cb44abf4a7..d2e2117422ac 100644 Binary files a/icons/mob/inhands/equipment/belt_righthand.dmi and b/icons/mob/inhands/equipment/belt_righthand.dmi differ diff --git a/icons/obj/banner.dmi b/icons/obj/banner.dmi index fd5372de46e0..73ac08b94587 100644 Binary files a/icons/obj/banner.dmi and b/icons/obj/banner.dmi differ diff --git a/icons/obj/clothing/belts.dmi b/icons/obj/clothing/belts.dmi index 39e8daa97425..58d0f4bfb8bc 100644 Binary files a/icons/obj/clothing/belts.dmi and b/icons/obj/clothing/belts.dmi differ diff --git a/icons/obj/devices/modular_laptop.dmi b/icons/obj/devices/modular_laptop.dmi index 1accc56f4c18..82afd2d1acee 100644 Binary files a/icons/obj/devices/modular_laptop.dmi and b/icons/obj/devices/modular_laptop.dmi differ diff --git a/icons/obj/devices/modular_pda.dmi b/icons/obj/devices/modular_pda.dmi index a427eb143ce1..c5610ccebe2b 100644 Binary files a/icons/obj/devices/modular_pda.dmi and b/icons/obj/devices/modular_pda.dmi differ diff --git a/icons/obj/drinks/boxes.dmi b/icons/obj/drinks/boxes.dmi index 0871bf550068..99676e79290d 100644 Binary files a/icons/obj/drinks/boxes.dmi and b/icons/obj/drinks/boxes.dmi differ diff --git a/icons/obj/food/containers.dmi b/icons/obj/food/containers.dmi index f20b54d9c31f..bd3bd177bfe5 100644 Binary files a/icons/obj/food/containers.dmi and b/icons/obj/food/containers.dmi differ diff --git a/icons/obj/machines/computer.dmi b/icons/obj/machines/computer.dmi index 17b6dc1973c2..5a6cd0c7e516 100644 Binary files a/icons/obj/machines/computer.dmi and b/icons/obj/machines/computer.dmi differ diff --git a/icons/obj/machines/modular_console.dmi b/icons/obj/machines/modular_console.dmi index 6a8348e1b8ec..1ec20af5013d 100644 Binary files a/icons/obj/machines/modular_console.dmi and b/icons/obj/machines/modular_console.dmi differ diff --git a/icons/obj/medical/chemical.dmi b/icons/obj/medical/chemical.dmi index 0bc3c3fd0751..8f3267c07d4a 100644 Binary files a/icons/obj/medical/chemical.dmi and b/icons/obj/medical/chemical.dmi differ diff --git a/icons/obj/medical/organs/shadow_organs.dmi b/icons/obj/medical/organs/shadow_organs.dmi index 303b4e11cb3b..8ed64bb5754d 100644 Binary files a/icons/obj/medical/organs/shadow_organs.dmi and b/icons/obj/medical/organs/shadow_organs.dmi differ diff --git a/icons/obj/mining_zones/terrain.dmi b/icons/obj/mining_zones/terrain.dmi index fd930fe709dc..5351cde085c3 100644 Binary files a/icons/obj/mining_zones/terrain.dmi and b/icons/obj/mining_zones/terrain.dmi differ diff --git a/icons/obj/weapons/Nightmare_items.dmi b/icons/obj/weapons/Nightmare_items.dmi new file mode 100644 index 000000000000..9a9f6e9e631e Binary files /dev/null and b/icons/obj/weapons/Nightmare_items.dmi differ diff --git a/icons/obj/weapons/baton.dmi b/icons/obj/weapons/baton.dmi index c9dc238a651b..181c635c72f5 100644 Binary files a/icons/obj/weapons/baton.dmi and b/icons/obj/weapons/baton.dmi differ diff --git a/icons/obj/weapons/grenade.dmi b/icons/obj/weapons/grenade.dmi index 415166f9c407..a70529cd60d7 100644 Binary files a/icons/obj/weapons/grenade.dmi and b/icons/obj/weapons/grenade.dmi differ diff --git a/icons/ui/achievements/achievements.dmi b/icons/ui/achievements/achievements.dmi index 80ebc9cd2972..d5bef5c42f87 100644 Binary files a/icons/ui/achievements/achievements.dmi and b/icons/ui/achievements/achievements.dmi differ diff --git a/modular_darkpack/master_files/code/_onclick/hud/hud.dm b/modular_darkpack/master_files/code/_onclick/hud/hud.dm index 5db6a23dda0d..a4ac881ee3bd 100644 --- a/modular_darkpack/master_files/code/_onclick/hud/hud.dm +++ b/modular_darkpack/master_files/code/_onclick/hud/hud.dm @@ -1,6 +1,10 @@ /datum/hud var/atom/movable/screen/block_icon var/atom/movable/screen/jump_icon - var/atom/movable/screen/bloodpool_icon - var/atom/movable/screen/rage_icon + var/atom/movable/screen/bloodpool/bloodpool_icon + var/atom/movable/screen/auspice/auspice_icon // WEREWOLF + var/atom/movable/screen/rage_and_gnosis/rage_and_gnosis_icon // WEREWOLF + var/atom/movable/screen/fera_transform/homid/homid_trans_icon // WEREWOLF + var/atom/movable/screen/fera_transform/war/war_trans_icon // WEREWOLF + var/atom/movable/screen/fera_transform/feral/feral_trans_icon // WEREWOLF var/atom/movable/screen/zone_icon diff --git a/modular_darkpack/master_files/code/_onclick/hud/living.dm b/modular_darkpack/master_files/code/_onclick/hud/living.dm index 157ee31eaf71..3c5ed1d1f62f 100644 --- a/modular_darkpack/master_files/code/_onclick/hud/living.dm +++ b/modular_darkpack/master_files/code/_onclick/hud/living.dm @@ -1,5 +1,6 @@ /datum/hud/living/New(mob/living/owner) ..() - bloodpool_icon = new /atom/movable/screen/bloodpool(null, src) + bloodpool_icon = new(null, src) infodisplay += bloodpool_icon + diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/carbon.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/carbon.dm index 2591ada46031..28a1b83162b5 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/carbon.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/carbon.dm @@ -1,4 +1,13 @@ +/mob/living/carbon/Initialize(mapload) + . = ..() + if(!examine_panel_tgui) + examine_panel_tgui = new() + examine_panel_tgui.holder = src + /mob/living/carbon/Destroy() + examine_panel_tgui.holder = null + QDEL_NULL(examine_panel_tgui) + client?.images -= suckbar QDEL_NULL(suckbar) suckbar_loc = null GLOB.masquerade_breakers_list -= src diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/examine.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/examine.dm index 629a2c982e78..72435558b539 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/examine.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/examine.dm @@ -1,4 +1,5 @@ -/mob/living/carbon/proc/display_darkpack_examine_text() +/mob/living/carbon/proc/display_darkpack_examine_text(mob/user) + . = list() if(obscured_slots & HIDEFACE) return @@ -23,19 +24,27 @@ . += span_danger("[p_They()] [p_are()] a decayed corpse!
") if(HAS_TRAIT(src, TRAIT_SERPENTIS_SKIN) && !(HIDEJUMPSUIT)) // 'hidden by modest clothing' - . += span_danger("[p_They()] [p_are()] covered in... scales!?") + . += span_danger("[p_They()] [p_are()] covered in... scales!?
") if(!(obscured_slots & HIDEFACE)) switch(st_get_stat(STAT_APPEARANCE)) if(0) - . += span_bolddanger("AGHH!!! WHAT THE HELL IS THAT THING!!!") + . += span_bolddanger("AGHH!!! WHAT THE HELL IS THAT THING!!!
") if(1) - . += span_danger("Eugh. [p_They()] [p_are()] really ugly.") + . += span_danger("Eugh. [p_They()] [p_are()] really ugly.
") if(2) - . += span_notice("[p_They()] [p_are()] about average looking.") + . += span_notice("[p_They()] [p_are()] about average looking.
") if(3) - . += span_nicegreen("[p_They()] [p_are()] quite nice looking!") + . += span_nicegreen("[p_They()] [p_are()] quite nice looking!
") if(4) - . += span_purple("[p_They()] [p_are()] very attractive!") + . += span_purple("[p_They()] [p_are()] very attractive!
") if(5 to INFINITY) - . += span_rose(span_bold("Oh... My God... [p_They()] [p_are()] absolutely stunning! You find yourself blushing just looking at them!")) + . += span_rose(span_bold("Oh... My God... [p_They()] [p_are()] absolutely stunning! You find yourself blushing just looking at them!
")) + if (HAS_TRAIT(src, TRAIT_PERMAFANGS)) + . += span_warning("[p_They()] [p_have()] visible fangs in [p_their()] mouth.
") + + // WEREWOLF + var/datum/splat/werewolf/werewolf_splat = iswerewolfsplat(user) + if(werewolf_splat && !(obscured_slots & HIDEFACE)) + . += werewolf_splat.examine_other_human(src) + // WEREWOLF diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/_species.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/_species.dm new file mode 100644 index 000000000000..4c429d28433c --- /dev/null +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/_species.dm @@ -0,0 +1,4 @@ + +/datum/species + /// Used in get_generic_name to replace gender + var/visible_gender_override diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/death.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/death.dm index 5e1cc763e372..7ca2cf4febe0 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/death.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/death.dm @@ -1,39 +1,9 @@ /mob/living/carbon/human/death(gibbed) . = ..() + if(!.) + return . SEND_SIGNAL(SSdcs, COMSIG_GLOB_REPORT_CRIME, CRIME_MURDER, get_turf(src)) GLOB.masquerade_breakers_list -= src GLOB.sabbatites -= src - - if(!iskindred(src)) - return - can_be_embraced = FALSE - var/obj/item/organ/brain/brain = get_organ_slot(ORGAN_SLOT_BRAIN) //NO REVIVAL EVER - if(brain) - brain.organ_flags |= ORGAN_FAILING - - /* - if(HAS_TRAIT(src, TRAIT_IN_FRENZY)) - exit_frenzymod() - */ - SEND_SOUND(src, sound('modular_darkpack/modules/vampire_the_masquerade/sounds/final_death.ogg', volume = 50)) - - switch (chronological_age) - if(-INFINITY to 10) //normal corpse - return - if(10 to 50) - rot_body(1) //skin takes on a weird colouration - visible_message(span_notice("[src]'s skin loses some of its colour.")) - if(50 to 100) - rot_body(2) //looks slightly decayed - visible_message(span_notice("[src]'s skin rapidly decays.")) - if(100 to 150) - rot_body(3) //looks very decayed - visible_message(span_warning("[src]'s body rapidly decomposes!")) - if(150 to 200) - rot_body(4) //mummified skeletonised corpse - visible_message(span_warning("[src]'s body rapidly skeletonises!")) - if(200 to INFINITY) //turn to ash - playsound(src, 'modular_darkpack/modules/vampire_the_masquerade/sounds/burning_death.ogg', 80, TRUE) - dust(just_ash = TRUE, drop_items = TRUE, force = TRUE) diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm index 0dd7c8a1eb1e..f78afb1a0081 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human.dm @@ -1,9 +1,35 @@ /mob/living/carbon/human/Topic(href, href_list) // DARKPACK TODO - reimplement in a sane way. if(href_list["masquerade_violation"]) - return + if(!ismundane(usr)) + return + var/mob/living/carbon/human/reporter = usr + if(reporter.stat > UNCONSCIOUS) + return + if(usr == src) + return + var/reason = tgui_input_text(reporter, "Write a description of violation", "Spot a Masquerade violation", null, MAX_MESSAGE_LEN) + if(!reason) + return + reason = sanitize(reason) + if(!SEND_SIGNAL(reporter, COMSIG_SEEN_MASQUERADE_VIOLATION, src)) + return + message_admins("[ADMIN_LOOKUPFLW(reporter)] spotted [ADMIN_LOOKUPFLW(src)]'s Masquerade violation. Description: [reason]") + log_game("[ADMIN_LOOKUPFLW(reporter)] spotted [ADMIN_LOOKUPFLW(src)]'s Masquerade violation. Description: [reason]") + to_chat(src, span_danger("You were found to be violating the masquereade for: [reason]")) if(href_list["masquerade_reinforcement"]) - return + if(!ismundane(usr)) + return + var/mob/living/carbon/human/reporter = usr + if(reporter.stat > UNCONSCIOUS) + return + if(usr == src) + return + if(!SEND_SIGNAL(reporter, COMSIG_MASQUERADE_REINFORCE, src)) + return + message_admins("[ADMIN_LOOKUPFLW(reporter)] repaired [ADMIN_LOOKUPFLW(src)]'s Masquerade violation.") + log_game("[ADMIN_LOOKUPFLW(reporter)] repaired [ADMIN_LOOKUPFLW(src)]'s Masquerade violation.") + . = ..() diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human_defines.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human_defines.dm index 37e038ebf4e8..5ef248c4bd44 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human_defines.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,4 +1,8 @@ /mob/living/carbon/human + // Humans have a default bloodpool of 10 + maxbloodpool = 10 + bloodpool = 10 + // NPC humans get the area of effect, player humans dont. var/violation_aoe = FALSE /// List of ownership types the player has claimed keys for (e.g., "apartment", "car") @@ -6,6 +10,3 @@ // Visible adjectives, used for Guestbooks. var/visible_adjective = "" - // Humans have a default bloodpool of 10 - maxbloodpool = 10 - bloodpool = 10 diff --git a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/species_types/zombies.dm b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/species_types/zombies.dm index f4462420ba9e..351d0c9ad7af 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -33,13 +33,6 @@ C.maxHealth = 300 //tanky C.health = 300 - /* // DARKPACK TODO -- kj shit? - C.yang_chi = 0 - C.max_yang_chi = 0 - C.yin_chi = 6 - C.max_yin_chi = 6 - */ - //zombies resist vampire bites better than mortals RegisterSignal(C, COMSIG_MOB_VAMPIRE_SUCKED, PROC_REF(on_zombie_bitten)) ADD_TRAIT(C, TRAIT_MASQUERADE_VIOLATING_FACE, "zombie") diff --git a/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm b/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm index c2910fff93f8..318ed24e6997 100644 --- a/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm +++ b/modular_darkpack/master_files/code/modules/mob/living/living_defines.dm @@ -6,7 +6,7 @@ var/list/drunked_of = list() var/discipline_time_plus = 0 - var/bloodpower_time_plus = 0 + var/blood_efficiency = 1 var/thaum_damage_plus = 0 var/resistant_to_disciplines = FALSE diff --git a/modular_darkpack/master_files/code/modules/rituals/code/ritual_rune_drawing.dm b/modular_darkpack/master_files/code/modules/rituals/code/ritual_rune_drawing.dm index d12162f03e49..acf8a2df62f2 100644 --- a/modular_darkpack/master_files/code/modules/rituals/code/ritual_rune_drawing.dm +++ b/modular_darkpack/master_files/code/modules/rituals/code/ritual_rune_drawing.dm @@ -72,7 +72,7 @@ if(do_after(user, draw_time, user)) new rune_path(user.loc) - user.bloodpool = max(user.bloodpool - rune_cost, 0) + user.adjust_blood_pool(-rune_cost) SEND_SIGNAL(user, COMSIG_MASQUERADE_VIOLATION) drawing = FALSE diff --git a/modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi b/modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi index 5035f0cdcb8f..96425f0b24dc 100644 Binary files a/modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi and b/modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi differ diff --git a/modular_darkpack/modules/aggravated_damage/code/generic_healing.dm b/modular_darkpack/modules/aggravated_damage/code/generic_healing.dm new file mode 100644 index 000000000000..938dd1c8db78 --- /dev/null +++ b/modular_darkpack/modules/aggravated_damage/code/generic_healing.dm @@ -0,0 +1,54 @@ +// Generic helpers to simulate the healing of the TTRPG + +/// Returns amount of dots healed +/mob/living/proc/heal_storyteller_health(dots_to_heal, heal_aggravated = FALSE, heal_scars = FALSE, heal_blood = FALSE) + if(dots_to_heal <= 0) + return 0 + + var/healed_dots = 0 + + if(heal_blood) + adjust_blood_volume(dots_to_heal * 2) + + if(heal_scars && dots_to_heal > 0) + healed_dots += heal_storyteller_scars(dots_to_heal) + + if(heal_aggravated) + while(dots_to_heal > 0 && get_agg_loss()+get_fire_loss() > 0) + heal_ordered_damage(1 TTRPG_DAMAGE, list(BURN, AGGRAVATED)) + dots_to_heal-- + healed_dots++ + + while(dots_to_heal > 0 && get_brute_loss()+get_tox_loss()+get_oxy_loss() > 0) + heal_ordered_damage(1 TTRPG_DAMAGE, list(BRUTE, TOX, OXY)) + dots_to_heal-- + healed_dots++ + + if(healed_dots) + updatehealth() + + return healed_dots + +/mob/living/proc/heal_storyteller_scars(dots_to_heal) + return + +/mob/living/carbon/heal_storyteller_scars(dots_to_heal) + var/healed_dots = 0 + + for(var/datum/wound/our_wound in all_wounds) + if(dots_to_heal <= 0) + break + our_wound.remove_wound() + dots_to_heal-- + healed_dots++ + + // W20 p. 259: describes "battle scars" to be inclusive of stuff like organ damage, brain damage or lost limbs. + for(var/obj/item/organ/target_organ as anything in organs) + if(!target_organ.damage) + continue + if(target_organ.apply_organ_damage(-dots_to_heal TTRPG_DAMAGE, required_organ_flag = ORGAN_ORGANIC)) + dots_to_heal-- + healed_dots++ + + return healed_dots + diff --git a/modular_darkpack/modules/antediluvian_sarcophagus/code/sarcophagus.dm b/modular_darkpack/modules/antediluvian_sarcophagus/code/sarcophagus.dm index afa3f69b8930..7f62b6e51eb5 100644 --- a/modular_darkpack/modules/antediluvian_sarcophagus/code/sarcophagus.dm +++ b/modular_darkpack/modules/antediluvian_sarcophagus/code/sarcophagus.dm @@ -80,6 +80,11 @@ GLOBAL_LIST_INIT(caesar_cipher, list( return final_message +/datum/storyteller_roll/sarcophagus_cipher + bumper_text = "examine" + difficulty = 10 + applicable_stats = list(STAT_INTELLIGENCE, STAT_OCCULT) + reroll_cooldown = 1 SCENES /obj/sarcophagus name = "unknown sarcophagus" @@ -90,9 +95,9 @@ GLOBAL_LIST_INIT(caesar_cipher, list( density = TRUE anchored = TRUE pixel_w = -8 - COOLDOWN_DECLARE(roll_cooldown) var/password = "Brongus" var/passkey = 5 + var/datum/storyteller_roll/sarcophagus_cipher/cipher_roll /obj/sarcophagus/Initialize(mapload) . = ..() @@ -102,15 +107,16 @@ GLOBAL_LIST_INIT(caesar_cipher, list( else passkey = rand(-15, -5) //to_chat(world, span_userdanger("UNKNOWN SARCOPHAGUS POSITION HAS BEEN LEAKED")) - SEND_SOUND(world, sound('modular_darkpack/master_files/sounds/announce.ogg')) + if(!mapload) + SEND_SOUND(world, sound('modular_darkpack/master_files/sounds/announce.ogg')) /obj/sarcophagus/examine(mob/user) . = ..() var/message = "You see an engraved text on it: [encipher(password, passkey)]." - if(isliving(user) && COOLDOWN_FINISHED(src, roll_cooldown)) - COOLDOWN_START(src, roll_cooldown, 1 SCENES) - var/mob/living/living_user = user - var/roll_result = SSroll.storyteller_roll(living_user.st_get_stat(STAT_INTELLIGENCE) + living_user.st_get_stat(STAT_OCCULT), 10, list(user), user) + if(isliving(user)) + if(!cipher_roll) + cipher_roll = new() + var/roll_result = cipher_roll.st_roll(user, src) if(roll_result == ROLL_SUCCESS) message += " It's an ancient cipher. You shift letters in your head till you end up with [uppertext(password)]." else diff --git a/modular_darkpack/modules/areas/code/interiors/city_interiors.dm b/modular_darkpack/modules/areas/code/interiors/city_interiors.dm index baaca99a3058..e92196985534 100644 --- a/modular_darkpack/modules/areas/code/interiors/city_interiors.dm +++ b/modular_darkpack/modules/areas/code/interiors/city_interiors.dm @@ -156,13 +156,6 @@ ambience_index = AMBIENCE_OFFICE fire_controled = TRUE -/area/vtm/interior/substation - name = "Grid Substation" - icon_state = "hotel" - music_index = MUSIC_CITY - ambience_index = AMBIENCE_OFFICE - fire_controled = TRUE - /area/vtm/interior/church name = "Church" icon_state = "church" @@ -172,18 +165,13 @@ /area/vtm/interior/church/staff name = "Church - Backrooms" - icon_state = "church" zone_type = ZONE_ELYSIUM music_index = MUSIC_CITY - ambience_index = AMBIENCE_OFFICE - fire_controled = TRUE /area/vtm/interior/church/haven name = "Church - Restricted Floor" icon_state = "old_clan_tzimisce" music_index = MUSIC_CITY - ambience_index = AMBIENCE_OFFICE - fire_controled = TRUE zone_type = ZONE_ELYSIUM /area/vtm/interior/elevator @@ -303,3 +291,6 @@ name = "Grid Substation" icon_state = "hotel" fire_controled = TRUE + music_index = MUSIC_CITY + ambience_index = AMBIENCE_OFFICE + fire_controled = TRUE diff --git a/modular_darkpack/modules/battering_ram/code/battering_ram.dm b/modular_darkpack/modules/battering_ram/code/battering_ram.dm index 90a11d3c9a98..51a2f4265e41 100644 --- a/modular_darkpack/modules/battering_ram/code/battering_ram.dm +++ b/modular_darkpack/modules/battering_ram/code/battering_ram.dm @@ -22,8 +22,8 @@ var/obj/structure/vampdoor/target_door = target if(target_door.door_broken) return COMPONENT_CANCEL_ATTACK_CHAIN - var/dice_result = SSroll.storyteller_roll(user.st_get_stat(STAT_STRENGTH)+user.st_get_stat(STAT_MELEE), 6, user, user, TRUE) - if(!do_after(user, ((5 SECONDS) / max(1, dice_result)), target)) + var/dice_result = SSroll.storyteller_roll(user.st_get_stat(STAT_STRENGTH)+user.st_get_stat(STAT_MELEE), 6, user, TRUE) + if(!do_after(user, ((1 TURNS) / max(1, dice_result)), target)) return COMPONENT_CANCEL_ATTACK_CHAIN if(prob(80 / max(1, dice_result)) || !dice_result) target_door.pixel_z = target_door.pixel_z+rand(-1, 1) diff --git a/modular_darkpack/modules/billiards/code/billiard.dm b/modular_darkpack/modules/billiards/code/billiard.dm new file mode 100644 index 000000000000..39caecbc5455 --- /dev/null +++ b/modular_darkpack/modules/billiards/code/billiard.dm @@ -0,0 +1,247 @@ +#define SOLID_BALL "Solid Ball" +#define STRIPED_BALL "Striped Ball" +#define EIGHT_BALL "8-Ball" +#define ZERO_BALL "0-Ball" + +#define TABLE_BOUNDS 11 + +/obj/item/pool_cue + name = "pool cue" + desc = "Used for playing a game of 8 ball." + icon = 'modular_darkpack/modules/billiards/icons/billiard.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/billiards/icons/billiard_onfloor.dmi') + icon_state = "cue" + base_icon_state = "cue" + worn_icon = 'modular_darkpack/modules/billiards/icons/back.dmi' + lefthand_file = 'modular_darkpack/modules/billiards/icons/lefthand.dmi' + righthand_file = 'modular_darkpack/modules/billiards/icons/righthand.dmi' + force = 10 + w_class = WEIGHT_CLASS_BULKY + slot_flags = ITEM_SLOT_BACK + throwforce = 15 + throw_speed = 2 + attack_verb_continuous = list("smashes", "slams", "whacks", "thwacks") + attack_verb_simple = list("smash", "slam", "whack", "thwack") + +/obj/item/pool_cue/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, \ + force_unwielded = /obj/item/pool_cue::force, \ + force_wielded = 15, \ + ) + +/obj/item/pool_cue/update_icon_state() + inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" + return ..() + +/obj/item/pool_ball + name = "pool ball" + desc = "Used for playing a game of 8 ball." + icon = 'modular_darkpack/modules/billiards/icons/billiard.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/billiards/icons/billiard_onfloor.dmi') + icon_state = "0ball" + var/ball_number = 0 + +/obj/item/pool_ball/update_name(updates) + . = ..() + if(ball_number == 0) + name = "cue ball" + else + name = "\improper [ball_number]-ball" + +/obj/item/pool_ball/update_icon_state() + . = ..() + icon_state = "[ball_number]ball" + +/obj/item/pool_ball/random/Initialize(mapload) + . = ..() + ball_number = rand(0,15) + update_appearance() + +/obj/structure/table/wood/billiard + name = "billiard table" + desc = "Come here, play some BALLS. I know you want it so much..." + icon = 'modular_darkpack/modules/billiards/icons/32x48.dmi' + icon_state = "billiard1" + smoothing_flags = NONE + smoothing_groups = null + canSmoothWith = null + pixel_y = -16 + + can_flip = FALSE + + var/start_with_cues = TRUE + var/start_min_cues = 1 + var/start_max_cues = 4 + + var/start_with_balls = TRUE + +/obj/structure/table/wood/billiard/Initialize() + . = ..() + + var/turf/my_turf = get_turf(src) + if(start_with_balls) + for(var/ball_num in 0 to 15) + var/obj/item/pool_ball/new_ball = new(my_turf) + new_ball.ball_number = ball_num + new_ball.update_appearance() + new_ball.pixel_x += rand(-TABLE_BOUNDS,TABLE_BOUNDS) + new_ball.pixel_y += rand(-TABLE_BOUNDS-6,TABLE_BOUNDS) + + if(start_with_cues) + for(var/i in 1 to rand(start_min_cues, start_max_cues)) + var/obj/item/pool_cue/new_cue = new(my_turf) + new_cue.pixel_x += rand(-TABLE_BOUNDS,TABLE_BOUNDS) + new_cue.pixel_y += rand(-TABLE_BOUNDS,TABLE_BOUNDS) + +/obj/structure/table/wood/billiard/atom_deconstruct(disassembled) + . = ..() + dump_contents() + +/obj/structure/table/wood/billiard/dump_contents() + var/turf/my_turf = get_turf(src) + for(var/obj/item/ball in contents) + ball.forceMove(my_turf) + ball.pixel_x = rand(-TABLE_BOUNDS,TABLE_BOUNDS) + ball.pixel_y = rand(-TABLE_BOUNDS,TABLE_BOUNDS) + +/obj/structure/table/wood/billiard/examine(mob/user) + . = ..() + . += span_notice("There are [length(get_balls_on_table(SOLID_BALL))] solid and [length(get_balls_on_table(STRIPED_BALL))] striped balls left.") + if(!length(get_balls_on_table(EIGHT_BALL))) + . += span_warning("The 8-Ball has been sunk.") + +/obj/structure/table/wood/billiard/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + . = ..() + + if(istype(held_item, /obj/item/pool_cue)) + context[SCREENTIP_CONTEXT_LMB] = "Strike ball" + . = CONTEXTUAL_SCREENTIP_SET + else if(!held_item) + context[SCREENTIP_CONTEXT_RMB] = "Reset Table" + . = CONTEXTUAL_SCREENTIP_SET + + return . || NONE + +/obj/structure/table/wood/billiard/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/pool_cue)) + var/static/list/cue_options = list( + SOLID_BALL = image(icon = 'modular_darkpack/modules/billiards/icons/billiard.dmi', icon_state = "1ball"), + STRIPED_BALL = image(icon = 'modular_darkpack/modules/billiards/icons/billiard.dmi', icon_state = "15ball"), + EIGHT_BALL = image(icon = 'modular_darkpack/modules/billiards/icons/billiard.dmi', icon_state = "8ball"), + ) + var/choice = show_radial_menu(user, src, cue_options, require_near = TRUE) + if(!choice) + return ITEM_INTERACT_BLOCKING + if(!length(get_balls_on_table(choice))) + to_chat(user, span_warning("You cant aim for a [lowertext(choice)] because they are all sunk!")) + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] begins lining up a shot to hit a [lowertext(choice)]."), span_notice("You begin lining up a shot to hit a [lowertext(choice)].")) + if(!do_after(user, 1 TURNS, src)) + return ITEM_INTERACT_BLOCKING + user.visible_message(span_notice("[user] strikes a [lowertext(choice)]!"), span_notice("You strike your target!")) + playsound(src, 'modular_darkpack/modules/billiards/sounds/poolball_strike.ogg', 75) + + var/datum/storyteller_roll/pool_aiming/accuracy_roll = new() + var/accuracy_result = accuracy_roll.st_roll(user, src) * 2 + var/datum/storyteller_roll/pool_hits/amount_to_hit_roll = new() + var/amount_to_hit_result = amount_to_hit_roll.st_roll(user, src) + for(var/i in 1 to amount_to_hit_result) + if(!length(get_balls_on_table())) + break + sink_ball(user, choice, accuracy_result, amount_to_hit_result) + return ITEM_INTERACT_SUCCESS + +/datum/storyteller_roll/pool_aiming + bumper_text = "billiard aiming" + applicable_stats = list(STAT_DEXTERITY) + numerical = TRUE + // spammy_roll = TRUE + difficulty = 4 + +/datum/storyteller_roll/pool_hits + bumper_text = "billiard hit" + applicable_stats = list(STAT_PERCEPTION, STAT_STREETWISE) + numerical = TRUE + // spammy_roll = TRUE + difficulty = 4 + +/obj/structure/table/wood/billiard/attack_hand_secondary(mob/user, list/modifiers) + . = ..() + to_chat(user, span_notice("You begin reseting the table to play another game of 8-Ball.")) + if(do_after(user, 1 TURNS, src)) + reset_table() + user.visible_message(span_notice("[user] resets the table for another game of 8-Ball"), span_notice("You finish reseting the table. Ready for another game?")) + update_appearance() + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + +/obj/structure/table/wood/billiard/proc/sink_ball(mob/living/user, target_ball_type, accuracy_result, amount_to_hit_result, obj/item/pool_ball/sunk_ball) + if(!sunk_ball) + sunk_ball = random_ball(target_ball_type, accuracy_result) + + if(!sunk_ball) + return + if(num_to_ball_type(sunk_ball.ball_number) == EIGHT_BALL) + user.visible_message(span_warning("[user] [pick("Pitted", "Sank", "Sunk")] the 8-Ball.. Damn.."), span_warning("[pick("Fuck", "Shit", "Piss")].. You [pick("Pitted", "Sank", "Sunk")] the 8-Ball")) + else + user.visible_message(span_notice("[user] sinks [sunk_ball]. [length(get_balls_on_table(num_to_ball_type(sunk_ball.ball_number)))] left."), span_notice("You sink [sunk_ball]!")) + sunk_ball.forceMove(src) + + for(var/obj/item/pool_ball/ball in get_balls_on_table(list(SOLID_BALL, STRIPED_BALL, EIGHT_BALL, ZERO_BALL))) + if(prob(50 + amount_to_hit_result * 10)) + animate(ball, time = rand(0.5 SECONDS, 3 SECONDS) , pixel_x = rand(-TABLE_BOUNDS, TABLE_BOUNDS), pixel_y = rand(-TABLE_BOUNDS-6, TABLE_BOUNDS), easing = CUBIC_EASING|EASE_OUT) + +/obj/structure/table/wood/billiard/proc/random_ball(desired_ball_type, accuracy_result = 2) + var/list/obj/item/pool_ball/sorted_balls = get_balls_on_table() + var/list/obj/item/pool_ball/weighted_balls = list() + for(var/obj/item/pool_ball/entry in sorted_balls) + if(desired_ball_type == num_to_ball_type(entry.ball_number)) + weighted_balls[entry] = accuracy_result + else + weighted_balls[entry] = 1 + return pick_weight(weighted_balls) + +/obj/structure/table/wood/billiard/proc/reset_table() + var/turf/my_turf = get_turf(src) + for(var/obj/item/pool_ball/ball in contents) + ball.forceMove(my_turf) + ball.pixel_x = rand(-TABLE_BOUNDS,TABLE_BOUNDS) + ball.pixel_y = rand(-TABLE_BOUNDS-6,TABLE_BOUNDS) + +/obj/structure/table/wood/billiard/proc/get_balls_on_table(list/looking_for = list(SOLID_BALL, STRIPED_BALL, EIGHT_BALL), sorted = FALSE) + RETURN_TYPE(/list/obj/item/pool_ball) + + var/turf/my_turf = get_turf(src) + + // Lets us pass a single item and turn it into a list + if(looking_for && !islist(looking_for)) + looking_for = list(looking_for) + + var/list/all_balls = list() + for(var/obj/item/pool_ball/ball in my_turf) + if(!(num_to_ball_type(ball.ball_number) in looking_for)) + continue + all_balls += ball + + if(sorted) + all_balls = sort_list(all_balls, GLOBAL_PROC_REF(cmp_num_string_asc)) + return all_balls + +/obj/structure/table/wood/billiard/proc/num_to_ball_type(number) + switch(number) + if(1 to 7) + return SOLID_BALL + if(9 to 15) + return STRIPED_BALL + if(8) + return EIGHT_BALL + if(0) + return ZERO_BALL + +#undef SOLID_BALL +#undef STRIPED_BALL +#undef EIGHT_BALL +#undef ZERO_BALL + +#undef TABLE_BOUNDS diff --git a/modular_darkpack/modules/billiards/icons/32x48.dmi b/modular_darkpack/modules/billiards/icons/32x48.dmi new file mode 100644 index 000000000000..1ae617561d4c Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/32x48.dmi differ diff --git a/modular_darkpack/modules/billiards/icons/back.dmi b/modular_darkpack/modules/billiards/icons/back.dmi new file mode 100644 index 000000000000..aa62e3c46fda Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/back.dmi differ diff --git a/modular_darkpack/modules/billiards/icons/billiard.dmi b/modular_darkpack/modules/billiards/icons/billiard.dmi new file mode 100644 index 000000000000..bd8690c25e7d Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/billiard.dmi differ diff --git a/modular_darkpack/modules/billiards/icons/billiard_onfloor.dmi b/modular_darkpack/modules/billiards/icons/billiard_onfloor.dmi new file mode 100644 index 000000000000..8da85d0e53e6 Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/billiard_onfloor.dmi differ diff --git a/modular_darkpack/modules/billiards/icons/lefthand.dmi b/modular_darkpack/modules/billiards/icons/lefthand.dmi new file mode 100644 index 000000000000..06dfeaf61094 Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/lefthand.dmi differ diff --git a/modular_darkpack/modules/billiards/icons/righthand.dmi b/modular_darkpack/modules/billiards/icons/righthand.dmi new file mode 100644 index 000000000000..4e7136d5c65d Binary files /dev/null and b/modular_darkpack/modules/billiards/icons/righthand.dmi differ diff --git a/modular_darkpack/modules/billiards/sounds/poolball_strike.ogg b/modular_darkpack/modules/billiards/sounds/poolball_strike.ogg new file mode 100644 index 000000000000..f693b8f29bab Binary files /dev/null and b/modular_darkpack/modules/billiards/sounds/poolball_strike.ogg differ diff --git a/modular_darkpack/modules/blood_drinking/code/bite_helper_procs.dm b/modular_darkpack/modules/blood_drinking/code/bite_helper_procs.dm index af88af18c100..342545a73f1f 100644 --- a/modular_darkpack/modules/blood_drinking/code/bite_helper_procs.dm +++ b/modular_darkpack/modules/blood_drinking/code/bite_helper_procs.dm @@ -2,19 +2,19 @@ /mob/living/proc/adjust_blood_pool(amount, updating_health = TRUE, on_spawn) if(on_spawn) bloodpool = 0 - if(iskindred(src)) - var/mob/living/carbon/human/kindred = src - var/datum/splat/vampire/kindred/kindred_species = iskindred(kindred) - var/hunger_threshold = 7 - (kindred_species.enlightenment ? st_get_stat(STAT_INSTINCT) : st_get_stat(STAT_SELF_CONTROL)) - var/previous_hunger = HAS_TRAIT(kindred, TRAIT_NEEDS_BLOOD) + + var/datum/splat/vampire/kindred/kindred_splat = iskindred(src) + if(kindred_splat) + var/hunger_threshold = 7 - (kindred_splat.enlightenment ? st_get_stat(STAT_INSTINCT) : st_get_stat(STAT_SELF_CONTROL)) + var/previous_hunger = HAS_TRAIT(src, TRAIT_NEEDS_BLOOD) var/will_be_hungry = (clamp(bloodpool + amount, 0, maxbloodpool) < hunger_threshold) if(!previous_hunger && will_be_hungry) // enter hunger - ADD_TRAIT(src, TRAIT_NEEDS_BLOOD, SPECIES_TRAIT) + ADD_TRAIT(src, TRAIT_NEEDS_BLOOD, TRAIT_GENERIC) to_chat(src, span_bolddanger("The Beast awakens as the pangs of hunger set in...")) else if(previous_hunger && !will_be_hungry) // leave hunger - REMOVE_TRAIT(src, TRAIT_NEEDS_BLOOD, SPECIES_TRAIT) + REMOVE_TRAIT(src, TRAIT_NEEDS_BLOOD, TRAIT_GENERIC) to_chat(src, span_notice("Your hunger is satisfied as the Beast inside retreats.")) //DARKPACK TODO: roll for frenzy when hungry and seeing, smelling, tasting blood, maybe like the old system where you roll every once in a while. the roll is @@ -23,6 +23,11 @@ if(updating_health) update_blood_hud() +/mob/living/proc/set_blood_pool(amount, updating_health = TRUE, on_spawn) + amount = amount - bloodpool + + adjust_blood_pool(amount, updating_health, on_spawn) + //runs a bite animation for biting people and biting people and biting p /mob/living/carbon/human/proc/add_bite_animation() remove_overlay(HALO_LAYER) @@ -38,7 +43,7 @@ //Here is where you handle any circumstantial modifiers to bloodpool gains //VTR has a lot of these. -/mob/living/carbon/human/proc/calculate_drink_modifier(var/mob/living/mob) +/mob/living/carbon/human/proc/calculate_drink_modifier(mob/living/drunk_from) var/drink_mod = 1 if(HAS_TRAIT(src, TRAIT_HUNGRY)) drink_mod *= 0.5 @@ -51,16 +56,16 @@ COOLDOWN_RESET(src, drinkblood_use_cd) if(client) client.images -= suckbar - qdel(suckbar) + QDEL_NULL(suckbar) return //Updates the circular suck bar that displays the amount of blood a victim has left. -/mob/living/carbon/human/proc/update_drinking_overlay(var/mob/living/mob) +/mob/living/carbon/human/proc/update_drinking_overlay(mob/living/drunk_from) if(client) client.images -= suckbar - qdel(suckbar) - suckbar_loc = mob - suckbar = image('modular_darkpack/modules/blood_drinking/icons/bloodcounter.dmi', suckbar_loc, "[round(14*(mob.bloodpool/mob.maxbloodpool))]", HUD_PLANE) + QDEL_NULL(suckbar) + suckbar_loc = drunk_from + suckbar = image('modular_darkpack/modules/blood_drinking/icons/bloodcounter.dmi', suckbar_loc, "[round(14*(drunk_from.bloodpool/drunk_from.maxbloodpool))]", HUD_PLANE) suckbar.pixel_z = 40 suckbar.plane = ABOVE_HUD_PLANE suckbar.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA diff --git a/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm b/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm index 894985d9eee0..883508000700 100644 --- a/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm +++ b/modular_darkpack/modules/blood_drinking/code/drinksomeblood.dm @@ -1,87 +1,87 @@ -/mob/living/carbon/human/proc/drinksomeblood(var/mob/living/mob, first_drink = FALSE) +/mob/living/carbon/human/proc/drinksomeblood(mob/living/drunk_from, first_drink = FALSE) COOLDOWN_START(src, drinkblood_use_cd, 3 SECONDS) - update_drinking_overlay(mob) + update_drinking_overlay(drunk_from) if(HAS_TRAIT(src, TRAIT_BLOODY_SUCKER)) src.emote("moan") Immobilize(30, TRUE) - if(isnpc(mob)) - var/mob/living/carbon/human/npc/NPC = mob + if(isnpc(drunk_from)) + var/mob/living/carbon/human/npc/NPC = drunk_from NPC.danger_source = null - mob.Stun(40) //NPCs don't get to resist + drunk_from.Stun(40) //NPCs don't get to resist - if(mob.blood_volume <= BLOOD_VOLUME_BAD) + if(drunk_from.blood_volume <= BLOOD_VOLUME_BAD) to_chat(src, span_warning("Your victim's heart beats only weakly. Death comes for them.")) //Check if we can drink this person to death - if(mob.bloodpool <= 0 && !check_can_drink_dry(mob)) - remove_drinking_overlay(mob) + if(drunk_from.bloodpool <= 0 && !check_can_drink_dry(drunk_from)) + remove_drinking_overlay(drunk_from) return - if(mob.bloodpool <= 1 && mob.maxbloodpool > 1) + if(drunk_from.bloodpool <= 1 && drunk_from.maxbloodpool > 1) to_chat(src, span_warning("You feel small amount of BLOOD in your victim.")) if(!HAS_TRAIT(src, TRAIT_BLOODY_LOVER)) SEND_SIGNAL(src, COMSIG_MASQUERADE_VIOLATION) - if(!do_after(src, 3 SECONDS, target = mob, timed_action_flags = NONE, progress = FALSE)) - remove_drinking_overlay(mob) - if(!(SEND_SIGNAL(mob, COMSIG_MOB_VAMPIRE_SUCKED, mob) & COMPONENT_RESIST_VAMPIRE_KISS)) - mob.apply_status_effect(/datum/status_effect/kissed) + if(!do_after(src, 3 SECONDS, target = drunk_from, timed_action_flags = NONE, progress = FALSE)) + remove_drinking_overlay(drunk_from) + if(!(SEND_SIGNAL(drunk_from, COMSIG_MOB_VAMPIRE_SUCKED, drunk_from) & COMPONENT_RESIST_VAMPIRE_KISS)) + drunk_from.apply_status_effect(/datum/status_effect/kissed) return - mob.adjust_blood_pool(-1) - suckbar.icon_state = "[round(14*(mob.bloodpool/mob.maxbloodpool))]" + drunk_from.adjust_blood_pool(-1) + suckbar.icon_state = "[round(14*(drunk_from.bloodpool/drunk_from.maxbloodpool))]" - if(ishuman(mob)) - var/mob/living/carbon/human/H = mob + if(ishuman(drunk_from)) + var/mob/living/carbon/human/H = drunk_from drunked_of |= "[H.dna.real_name]" - if(!iskindred(mob)) + if(!iskindred(drunk_from)) H.blood_volume = max(H.blood_volume-50, 150) if(H.reagents) if(length(H.reagents.reagent_list)) if(prob(50)) - H.reagents.trans_to(src, min(10, H.reagents.total_volume), transferred_by = mob, methods = INGEST) + H.reagents.trans_to(src, min(10, H.reagents.total_volume), transferred_by = drunk_from, methods = INGEST) if(HAS_TRAIT(src, TRAIT_PAINFUL_VAMPIRE_KISS)) - mob.adjust_brute_loss(20, TRUE) + drunk_from.adjust_brute_loss(20, TRUE) //Ventrue can suck on normal people, but not homeless people and animals. //BLOOD_QUALITY_LOV - 1, BLOOD_QUALITY_NORMAL - 2, BLOOD_QUALITY_HIGH - 3. Blue blood gives +1 to suction - if(HAS_TRAIT(src, TRAIT_FEEDING_RESTRICTION) && mob.bloodquality < BLOOD_QUALITY_NORMAL) + if(HAS_TRAIT(src, TRAIT_FEEDING_RESTRICTION) && drunk_from.bloodquality < BLOOD_QUALITY_NORMAL) to_chat(src, span_warning("You are too privileged to drink that awful BLOOD. Go get something better.")) visible_message(span_danger("[src] throws up!"), span_userdanger("You throw up!")) playsound(get_turf(src), 'modular_darkpack/modules/deprecated/sounds/vomit.ogg', 75, TRUE) if(isturf(loc)) add_splatter_floor(loc) - remove_drinking_overlay(mob) + remove_drinking_overlay(drunk_from) return - if(iskindred(mob)) - to_chat(src, span_userdanger("[mob]'s blood tastes HEAVENLY...")) + if(iskindred(drunk_from)) + to_chat(src, span_userdanger("[drunk_from]'s blood tastes HEAVENLY...")) adjust_brute_loss(-25, TRUE) adjust_fire_loss(-25, TRUE) else to_chat(src, span_warning("You sip some BLOOD from your victim. It feels good.")) - var/drink_mod = calculate_drink_modifier(mob) + var/drink_mod = calculate_drink_modifier(drunk_from) if(drink_mod) - adjust_blood_pool(drink_mod*max(1, mob.bloodquality-1)) + adjust_blood_pool(drink_mod*max(1, drunk_from.bloodquality-1)) adjust_brute_loss(-10, TRUE) adjust_fire_loss(-10, TRUE) update_damage_overlays() update_health_hud() - if(mob.bloodpool <= 0) - handle_drink_dry(mob) - remove_drinking_overlay(mob) + if(drunk_from.bloodpool <= 0) + handle_drink_dry(drunk_from) + remove_drinking_overlay(drunk_from) return if(grab_state >= GRAB_PASSIVE) stop_sound_channel(CHANNEL_BLOOD) - drinksomeblood(mob) + drinksomeblood(drunk_from) diff --git a/modular_darkpack/modules/blood_drinking/code/movable_screens/blood_hud.dm b/modular_darkpack/modules/blood_drinking/code/movable_screens/blood_hud.dm index 5dab25e00803..c6fdab35ecb9 100644 --- a/modular_darkpack/modules/blood_drinking/code/movable_screens/blood_hud.dm +++ b/modular_darkpack/modules/blood_drinking/code/movable_screens/blood_hud.dm @@ -1,14 +1,27 @@ -#define ui_living_bloodpool "EAST-1:28,CENTER-4:14" +#define UI_LIVING_BLOODPOOL "EAST-1:28,CENTER-4:14" /atom/movable/screen/bloodpool name = "bloodpool" //icon = 'modular_darkpack/modules/blood_drinking/icons/bloodpool.dmi' //32x32 version icon = 'modular_darkpack/modules/blood_drinking/icons/old_bloodpool.dmi' icon_state = "blood0" - screen_loc = ui_living_bloodpool + screen_loc = UI_LIVING_BLOODPOOL mouse_over_pointer = MOUSE_HAND_POINTER -/atom/movable/screen/bloodpool/Click() +/atom/movable/screen/bloodpool/Initialize(mapload, datum/hud/hud_owner) + . = ..() + + update_icon() + register_context() + +/atom/movable/screen/bloodpool/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + context[SCREENTIP_CONTEXT_LMB] = "Check blood points" + + return CONTEXTUAL_SCREENTIP_SET + +/atom/movable/screen/bloodpool/Click(location, control, params) if(isliving(usr)) var/mob/living/bloodbag = usr bloodbag.update_blood_hud() @@ -19,13 +32,19 @@ else to_chat(bloodbag, span_notice("You've got [bloodbag.bloodpool]/[bloodbag.maxbloodpool] blood points.")) - . = ..() + return ..() + +/atom/movable/screen/bloodpool/update_icon_state() + var/mob/living/owner = hud?.mymob + if(!istype(owner)) + return + var/bp_amount = clamp(round((owner.bloodpool/owner.maxbloodpool)*10), 0, 10) + icon_state = "blood[bp_amount]" + return ..() /mob/living/proc/update_blood_hud() - if(!client || !hud_used) + if(!hud_used) return - if(hud_used.bloodpool_icon) - var/emm = clamp(round((bloodpool/maxbloodpool)*10), 0, 10) - hud_used.bloodpool_icon.icon_state = "blood[emm]" + hud_used.bloodpool_icon?.update_icon() -#undef ui_living_bloodpool +#undef UI_LIVING_BLOODPOOL diff --git a/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/adjust_generation.dm b/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/adjust_generation.dm index 42a6c8697911..b8a2bde5ec3e 100644 --- a/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/adjust_generation.dm +++ b/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/adjust_generation.dm @@ -8,4 +8,4 @@ new_generation = max(generation - 1, MAX_TRUSTED_GENERATION) kindred.set_generation(new_generation) - client.prefs.write_preference_midround(GLOB.preference_entries[/datum/preference/numeric/generation], new_generation) + write_preference_midround(/datum/preference/numeric/generation, new_generation) diff --git a/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/make_diablerist.dm b/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/make_diablerist.dm index f10fcb5d7931..df28216398a2 100644 --- a/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/make_diablerist.dm +++ b/modular_darkpack/modules/blood_drinking/code/overfeeding/diablerie/make_diablerist.dm @@ -1,8 +1,5 @@ /mob/living/carbon/human/proc/make_diablerist() - if(!client?.prefs) - return - - client.prefs.write_preference_midround(GLOB.preference_entries[/datum/preference/toggle/diablerist], TRUE) + write_preference_midround(/datum/preference/toggle/diablerist, TRUE) ADD_TRAIT(src, TRAIT_DIABLERIE, TRAIT_DIABLERIE) SEND_SIGNAL(src, COMSIG_MOB_UPDATE_AURA) diff --git a/modular_darkpack/modules/blood_drinking/code/vamp_bite.dm b/modular_darkpack/modules/blood_drinking/code/vamp_bite.dm index 49b9a03a1c5d..5317381c1521 100644 --- a/modular_darkpack/modules/blood_drinking/code/vamp_bite.dm +++ b/modular_darkpack/modules/blood_drinking/code/vamp_bite.dm @@ -1,62 +1,51 @@ //this code is what should be called every time blood drinking is used on a character /mob/living/carbon/human/proc/vamp_bite() - update_blood_hud() if(!COOLDOWN_FINISHED(src, drinkblood_use_cd) || !COOLDOWN_FINISHED(src, drinkblood_click_cd)) return COOLDOWN_START(src, drinkblood_click_cd, 1 SECONDS) if(grab_state > GRAB_PASSIVE) - if(ishuman(pulling)) - var/mob/living/carbon/human/PB = pulling - if(isghoul(src)) - if(!iskindred(PB)) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("You're not desperate enough to try that.")) - return - if(!isghoul(src) && !iskindred(src)) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("You're not desperate enough to try that.")) - return - if(PB.stat == DEAD && !HAS_TRAIT(src, TRAIT_GULLET)) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("Your Beast requires life, not the tepid swill of corpses.")) - return - if(PB.blood_volume <= 50 && (!iskindred(pulling) || !iskindred(src))) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("This vessel is empty. You'll have to find another.")) - return - if(PB.bloodpool <= 0 && (!iskindred(pulling) || !iskindred(src))) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("This vessel is empty. You'll have to find another.")) - return - if(iskindred(src)) - PB.emote("groan") - if(isghoul(src)) - PB.emote("scream") - PB.add_bite_animation() if(isliving(pulling)) - if(!iskindred(src)) + var/mob/living/bit_living = pulling + if(!isvampiresplat(src)) SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) to_chat(src, span_warning("You're not desperate enough to try that.")) return - var/mob/living/LV = pulling - if(LV.blood_volume <= 50 && (!iskindred(pulling) || !iskindred(src))) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("This vessel is empty. You'll have to find another.")) - if(LV.bloodpool <= 0 && (!iskindred(pulling) || !iskindred(src))) - SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) - to_chat(src, span_warning("This vessel is empty. You'll have to find another.")) - return - if(LV.stat == DEAD && !HAS_TRAIT(src, TRAIT_GULLET)) + // Allow ghouls to steal viate? + if(isghoul(src)) + if(!iskindred(bit_living)) + SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) + to_chat(src, span_warning("You're not desperate enough to try that.")) + return + // Prevent drinking from corspes... Not V20 accurate as far as I can tell? + if(bit_living.stat == DEAD && !HAS_TRAIT(src, TRAIT_GULLET)) SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) to_chat(src,span_warning("Your Beast requires life, not the tepid swill of corpses.")) return + // Allow for diablor? + if(!iskindred(bit_living) || !iskindred(src)) + if(!CAN_HAVE_BLOOD(bit_living) || (bit_living.blood_volume <= 50) || (bit_living.bloodpool <= 0)) + SEND_SOUND(src, sound('modular_darkpack/modules/blood_drinking/sounds/need_blood.ogg', volume = 75)) + to_chat(src, span_warning("This vessel is empty. You'll have to find another.")) + return + + if(iskindred(src)) + bit_living.emote("groan") + else if(isghoul(src)) + bit_living.emote("scream") + + if(ishuman(bit_living)) + var/mob/living/carbon/human/bit_human = bit_living + bit_human.add_bite_animation() + var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE)) if(!skipface) if(iskindred(src) && HAS_TRAIT(src, TRAIT_NEEDS_BLOOD)) var/datum/splat/vampire/kindred/kindred_species = iskindred(src) var/stat_to_roll = kindred_species.enlightenment ? STAT_INSTINCT : STAT_SELF_CONTROL - var/frenzy_check = SSroll.storyteller_roll(st_get_stat(stat_to_roll), 6, src) - if(frenzy_check != ROLL_SUCCESS) + var/datum/storyteller_roll/frezy_roll = new() + frezy_roll.applicable_stats = list(stat_to_roll) + var/frenzy_result = frezy_roll.st_roll(src, bit_living) + if(frenzy_result != ROLL_SUCCESS) to_chat(src, span_userdanger("The taste of blood sends you into a frenzy as you feed!")) // DARKPACK TODO: frenzy, please put the call here else @@ -64,10 +53,10 @@ if(!HAS_TRAIT(src, TRAIT_BLOODY_LOVER)) playsound(src, 'modular_darkpack/modules/blood_drinking/sounds/drinkblood1.ogg', 50, TRUE) - LV.visible_message(span_warning(span_bold("[src] bites [LV]'s neck!")), span_warning(span_bold("[src] bites your neck!"))) + bit_living.visible_message(span_warning(span_bold("[src] bites [bit_living]'s neck!")), span_warning(span_bold("[src] bites your neck!"))) if(!HAS_TRAIT(src, TRAIT_BLOODY_LOVER)) SEND_SIGNAL(src, COMSIG_MASQUERADE_VIOLATION) else playsound(src, 'modular_darkpack/modules/blood_drinking/sounds/kiss.ogg', 50, TRUE) - LV.visible_message(span_italics(span_bold("[src] kisses [LV]!")), span_userlove(span_bold("[src] kisses you!"))) - drinksomeblood(LV, TRUE) + bit_living.visible_message(span_italics(span_bold("[src] kisses [bit_living]!")), span_userlove(span_bold("[src] kisses you!"))) + drinksomeblood(bit_living, TRUE) diff --git a/modular_darkpack/modules/cars/code/car.dm b/modular_darkpack/modules/cars/code/car.dm index 982599ec5639..f5a83940f720 100644 --- a/modular_darkpack/modules/cars/code/car.dm +++ b/modular_darkpack/modules/cars/code/car.dm @@ -244,11 +244,12 @@ var/total_lockpicking = user.st_get_stat(STAT_LARCENY) if(CONFIG_GET(flag/punishing_zero_dots) && total_lockpicking < 1) to_chat(user, span_warning("How do I do this...?")) - if(do_after(user, 10 SECONDS, src, interaction_key = DOAFTER_SOURCE_CAR)) + if(do_after(user, 1 TURNS, src, interaction_key = DOAFTER_SOURCE_CAR)) if(!locked) return - var/roll_result = SSroll.storyteller_roll(total_lockpicking + user.st_get_stat(STAT_DEXTERITY), lockpick_difficulty, list(user), user) - switch(roll_result) + var/datum/storyteller_roll/lockpick/our_roll = new() + our_roll.difficulty = lockpick_difficulty + switch(our_roll.st_roll(user, src)) if(ROLL_SUCCESS) to_chat(user, span_notice("You've managed to open [src]'s lock.")) playsound(src, 'modular_darkpack/modules/cars/sounds/open.ogg', 50, TRUE) @@ -671,6 +672,8 @@ return if(user.IsUnconscious() || HAS_TRAIT(user, TRAIT_INCAPACITATED) || HAS_TRAIT(user, TRAIT_RESTRAINED)) return + if(!ISADVANCEDTOOLUSER(user)) + return var/turn_speed = min(abs(speed_in_pixels) / 10, 3) switch(direction) if(NORTH) diff --git a/modular_darkpack/modules/cars/code/car_actions.dm b/modular_darkpack/modules/cars/code/car_actions.dm index 15c9c0771f7a..7d4ea543c544 100644 --- a/modular_darkpack/modules/cars/code/car_actions.dm +++ b/modular_darkpack/modules/cars/code/car_actions.dm @@ -88,6 +88,9 @@ to_chat(owner, span_danger("You don't know what you're doing!")) return FALSE + if(!ISADVANCEDTOOLUSER(clicker)) + return + var/obj/darkpack_car/owned_car = owner.loc if(!owned_car.on) if((owned_car.get_integrity() == owned_car.max_integrity) || (prob(100*(owned_car.get_integrity()/owned_car.max_integrity)))) diff --git a/modular_darkpack/modules/cars/code/gas.dm b/modular_darkpack/modules/cars/code/gas.dm index 2b553ed627ec..fac793c55c63 100644 --- a/modular_darkpack/modules/cars/code/gas.dm +++ b/modular_darkpack/modules/cars/code/gas.dm @@ -45,9 +45,9 @@ return NONE if(istype(get_turf(interacting_with), /turf/open/floor)) if(locate(/obj/effect/decal/cleanable/gasoline) in get_turf(interacting_with)) - return ITEM_INTERACT_FAILURE + return ITEM_INTERACT_BLOCKING if(stored_gasoline < 50) - return ITEM_INTERACT_FAILURE + return ITEM_INTERACT_BLOCKING stored_gasoline = max(0, stored_gasoline-50) new /obj/effect/decal/cleanable/gasoline(get_turf(interacting_with)) playsound(get_turf(src), 'modular_darkpack/modules/cars/sounds/gas_splat.ogg', 50, TRUE) @@ -125,11 +125,11 @@ continue oil.fire_act() -/obj/effect/decal/cleanable/gasoline/attackby(obj/item/I, mob/living/user) - var/attacked_by_hot_thing = I.get_temperature() +/obj/effect/decal/cleanable/gasoline/attackby(obj/item/tool, mob/living/user) + var/attacked_by_hot_thing = tool.get_temperature() if(attacked_by_hot_thing) - visible_message(span_warning("[user] tries to ignite [src] with [I]!"), span_warning("You try to ignite [src] with [I].")) - log_combat(user, src, (attacked_by_hot_thing < 480) ? "tried to ignite" : "ignited", I) + visible_message(span_warning("[user] tries to ignite [src] with [tool]!"), span_warning("You try to ignite [src] with [tool].")) + log_combat(user, src, (attacked_by_hot_thing < 480) ? "tried to ignite" : "ignited", tool) fire_act(attacked_by_hot_thing) return return ..() @@ -154,21 +154,25 @@ /obj/structure/fuelstation/examine(mob/user) . = ..() - . += "Balance: [stored_money] dollars" + . += "Balance: [stored_money] [MONEY_NAME]" -/obj/structure/fuelstation/attackby(obj/item/I, mob/living/user, params) - if(iscash(I)) - stored_money += I.get_item_credit_value() - to_chat(user, span_notice("You insert [I.get_item_credit_value()] dollars into [src].")) - qdel(I) +/obj/structure/fuelstation/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(iscash(tool)) + stored_money += tool.get_item_credit_value() + to_chat(user, span_notice("You insert [tool.get_item_credit_value()] [MONEY_NAME] into [src].")) + qdel(tool) say("Payment received.") - if(istype(I, /obj/item/gas_can)) - var/obj/item/gas_can/G = I + return ITEM_INTERACT_SUCCESS + if(istype(tool, /obj/item/gas_can)) + var/obj/item/gas_can/G = tool if(G.stored_gasoline < 1000 && stored_money) var/gas_to_dispense = min(stored_money*20, 1000-G.stored_gasoline) var/money_to_spend = round(gas_to_dispense/20) G.stored_gasoline = min(1000, G.stored_gasoline+gas_to_dispense) stored_money = max(0, stored_money-money_to_spend) playsound(loc, 'modular_darkpack/master_files/sounds/effects/gas_fill.ogg', 50, TRUE) - to_chat(user, span_notice("You fill [I].")) + to_chat(user, span_notice("You fill [tool].")) say("Gas filled.") + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + return NONE diff --git a/modular_darkpack/modules/city_time/code/status_effects.dm b/modular_darkpack/modules/city_time/code/status_effects.dm index a8e63e4c1e85..df63a9eac878 100644 --- a/modular_darkpack/modules/city_time/code/status_effects.dm +++ b/modular_darkpack/modules/city_time/code/status_effects.dm @@ -22,12 +22,17 @@ /datum/status_effect/sunlight_burning/on_apply() if(!SScity_time.daytime_started) return FALSE - if(!iskindred(owner)) - return FALSE var/area/my_area = get_area(owner) if(!istype(my_area) || !my_area.outdoors) return FALSE + var/datum/splat/vampire/kindred/kindred_owner = iskindred(owner) + if(!kindred_owner) + return FALSE + // Humanity 10 vamps are immume to the light. atleast for the amount of time our day lasts. + if(CONFIG_GET(flag/humanity_sunlight_resistance) && !kindred_owner.enlightenment && (owner.st_get_stat(STAT_MORALITY) >= 10)) + return FALSE + to_chat(owner, span_danger("THE SUN SEARS YOUR FLESH")) return TRUE @@ -45,3 +50,5 @@ desc = "Get inside!" icon = 'modular_darkpack/modules/deprecated/icons/hud/screen_alert.dmi' icon_state = "fire" + +/datum/config_entry/flag/humanity_sunlight_resistance diff --git a/modular_darkpack/modules/city_traits/code/positive_traits.dm b/modular_darkpack/modules/city_traits/code/positive_traits.dm index 4fa6ecac98cf..16406a2f10fc 100644 --- a/modular_darkpack/modules/city_traits/code/positive_traits.dm +++ b/modular_darkpack/modules/city_traits/code/positive_traits.dm @@ -17,5 +17,4 @@ /datum/station_trait/full_moon/on_round_start() . = ..() - GLOB.moon_state = "Full" set_starlight(null, GLOB.starlight_range*1.2, GLOB.starlight_power*1.2) diff --git a/modular_darkpack/modules/clothes/code/feet.dm b/modular_darkpack/modules/clothes/code/feet.dm index 8a77f453a42f..113c5f085184 100644 --- a/modular_darkpack/modules/clothes/code/feet.dm +++ b/modular_darkpack/modules/clothes/code/feet.dm @@ -52,6 +52,21 @@ /obj/item/clothing/shoes/vampire/sneakers/red icon_state = "sneakers_red" +/obj/item/clothing/shoes/vampire/blackfur + name = "black fur boots" + desc = "A furry pair of black and white boots" + icon_state = "furboots_black" + +/obj/item/clothing/shoes/vampire/brownfur + name = "brown fur boots" + desc = "A furry pair of brown boots" + icon_state = "furboots_brown" + +/obj/item/clothing/shoes/vampire/pumped + name = "knee-high sneakers" + desc = "Sneakers from the popular brand Converts" + icon_state = "pumped_up_kicks" + /obj/item/clothing/shoes/vampire/heels name = "heels" desc = "Rich-looking heels." diff --git a/modular_darkpack/modules/clothes/code/head.dm b/modular_darkpack/modules/clothes/code/head.dm index 734741c7fc10..594215ef8d7c 100644 --- a/modular_darkpack/modules/clothes/code/head.dm +++ b/modular_darkpack/modules/clothes/code/head.dm @@ -367,13 +367,11 @@ target.emote("scream") target.Stun(0.5 SECONDS) + H = target.get_item_by_slot(ITEM_SLOT_HEAD) // Refetch it if it changes between do_after target.dropItemToGround(H) target.equip_to_slot_if_possible(src, ITEM_SLOT_HEAD) /obj/item/clothing/head/beret/black name = "black beret" desc = "A black beret, perfect for war veterans and dark, brooding, anti-hero mimes." - icon_state = "beret" - greyscale_config = /datum/greyscale_config/beret - greyscale_config_worn = /datum/greyscale_config/beret/worn greyscale_colors = "#3f3c40" diff --git a/modular_darkpack/modules/clothes/code/suit.dm b/modular_darkpack/modules/clothes/code/suit.dm index ef4cbfc28583..87093cd3eeff 100644 --- a/modular_darkpack/modules/clothes/code/suit.dm +++ b/modular_darkpack/modules/clothes/code/suit.dm @@ -220,6 +220,49 @@ name = "brown fur coat" icon_state = "winter2" +/obj/item/clothing/suit/vampire/coat/leopard + name = "leopard coat" + desc = "A coat made from synthetic fur." + icon_state = "leopard_coat" + + +/obj/item/clothing/suit/hooded/hoodie + name = "hoodie" + desc = "A simple hoodie." + icon_state = "hoodie" + icon = 'modular_darkpack/modules/clothes/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/clothes/icons/worn.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi') + body_parts_covered = CHEST|GROIN|ARMS + cold_protection = CHEST|GROIN|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + hoodtype = /obj/item/clothing/head/hooded/hood_hood + +/obj/item/clothing/head/hooded/hood_hood + name = "hoodie hood" + desc = "A hoodies hoodie hood." + icon_state = "hoodie_hood" + icon = 'modular_darkpack/modules/clothes/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/clothes/icons/worn.dmi' + // You should not expect this to have an onfloor + body_parts_covered = HEAD + cold_protection = HEAD + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + flags_inv = HIDEEARS + hair_mask = /datum/hair_mask/winterhood + +/obj/item/clothing/suit/hooded/hoodie/hoodie_pim + name = "intruder zim hoodie" + desc = "A hoodie of a favorite Intruder Zim character, Ger." + icon_state = "hoodie_zim" + hoodtype = /obj/item/clothing/head/hooded/hood_hood/hood_pim + +/obj/item/clothing/head/hooded/hood_hood/hood_pim + name = "intruder zim hoodie hood" + desc = "A hood resembling a favorite Intruder Zim character, Ger." + icon_state = "hoodie_zim_hood" + + /obj/item/clothing/suit/vampire/slickbackcoat name = "opulent coat" desc = "Lavish, luxurious, and deeply purple. Slickback Clothing Co. It exudes immense energy." @@ -298,9 +341,14 @@ wound = 25 /obj/item/clothing/suit/vampire/trench/alt/armored - name = "armored trenchcoat" + name = "armored brown trenchcoat" icon_state = "trench2" - max_integrity = 1000; + max_integrity = 400 + armor_type = /datum/armor/armored_jackets + +/obj/item/clothing/suit/vampire/trench/armored + name = "armored black trenchcoat" + max_integrity = 400 armor_type = /datum/armor/armored_jackets /obj/item/clothing/suit/vampire/trench @@ -539,5 +587,3 @@ desc = "A crisp white labcoat. This one has a green trim and the " + MAIN_EVIL_COMPANY + " International logo stiched onto the breast!" icon_state = "pentex_labcoat_alt" armor_type = /datum/armor/labcoat - - diff --git a/modular_darkpack/modules/clothes/code/under.dm b/modular_darkpack/modules/clothes/code/under.dm index 58945a5784a3..878540cca562 100644 --- a/modular_darkpack/modules/clothes/code/under.dm +++ b/modular_darkpack/modules/clothes/code/under.dm @@ -98,6 +98,26 @@ desc = "A white, sweat stained shirt with a giant black skull on the front, it makes a statement. Maybe 'I don't use deoderant' but, a statement nontheless." icon_state = "dirty" +/obj/item/clothing/under/vampire/sceneleopard + name = "revealing outfit" + desc = "You never thought you needed spaghetti straps." + icon_state = "scenetop_leopard" + +/obj/item/clothing/under/vampire/scenemoody + name = "moody attire" + desc = "A classic My Chemistry Romance top." + icon_state = "scenetop_moody" + +/obj/item/clothing/under/vampire/scenezim + name = "intruder zim attire" + desc = "A top from your favorite show, Intruder Zim" + icon_state = "scenetop_zim" + +/obj/item/clothing/under/vampire/scenepink + name = "popular Outfit" + desc = "It almost makes you feel like a mean girl" + icon_state = "scenetop_pink" + /obj/item/clothing/under/vampire/turtleneck_white name = "white turtleneck" desc = "For me, it's always like this." @@ -489,4 +509,3 @@ desc = "A white designer suitskirt with a green dress shirt. This one has an Endron International tag on it!" icon_state = "pentex_executiveskirt" - diff --git a/modular_darkpack/modules/clothes/icons/clothing.dmi b/modular_darkpack/modules/clothes/icons/clothing.dmi index 69af6462be80..4a0c32ca2413 100644 Binary files a/modular_darkpack/modules/clothes/icons/clothing.dmi and b/modular_darkpack/modules/clothes/icons/clothing.dmi differ diff --git a/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi b/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi index 46cb2d37a0d7..c7f87b733003 100644 Binary files a/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi and b/modular_darkpack/modules/clothes/icons/clothing_onfloor.dmi differ diff --git a/modular_darkpack/modules/clothes/icons/head_onfloor.dmi b/modular_darkpack/modules/clothes/icons/head_onfloor.dmi new file mode 100644 index 000000000000..edcb059e7e98 Binary files /dev/null and b/modular_darkpack/modules/clothes/icons/head_onfloor.dmi differ diff --git a/modular_darkpack/modules/clothes/icons/worn.dmi b/modular_darkpack/modules/clothes/icons/worn.dmi index 164fa7f576b7..2f94c37b3afc 100644 Binary files a/modular_darkpack/modules/clothes/icons/worn.dmi and b/modular_darkpack/modules/clothes/icons/worn.dmi differ diff --git a/modular_darkpack/modules/computers/code/computer.dm b/modular_darkpack/modules/computers/code/computer.dm index 4fe138f09ffb..9fa5424129a6 100644 --- a/modular_darkpack/modules/computers/code/computer.dm +++ b/modular_darkpack/modules/computers/code/computer.dm @@ -51,6 +51,17 @@ GLOBAL_LIST_EMPTY(vampire_computers) /obj/vampire_computer/attack_hand(mob/user) . = ..() + /* I cant verify the lore accuracy of "rejection past your embrace age" and we dont have a invention date for tech to represent it either + var/bad_at_computers = FALSE + if(isliving(user)) + var/mob/living/living_user = user + bad_at_computers = (living_user.st_get_stat(STAT_COMPUTER) < 1) || HAS_TRAIT(user, TRAIT_REJECTED_BY_TECHNOLOGY) + if(bad_at_computers) + to_chat(user, span_warning("You start interacting with [src]. Confounded machine...")) + if(!do_after(user, 1 TURNS, src)) + to_chat(user, span_warning("Bah! You didn't need the machine anyways.")) + return + */ ui_interact(user) /obj/vampire_computer/Destroy() diff --git a/modular_darkpack/modules/decor/code/decor.dm b/modular_darkpack/modules/decor/code/decor.dm index 2f7a49a99f5c..9e2303d42538 100644 --- a/modular_darkpack/modules/decor/code/decor.dm +++ b/modular_darkpack/modules/decor/code/decor.dm @@ -437,18 +437,6 @@ /obj/underplate/stuff icon_state = "stuff" -/obj/structure/billiard_table - name = "billiard table" - desc = "Come here, play some BALLS. I know you want it so much..." - icon = 'modular_darkpack/modules/deprecated/icons/32x48.dmi' - icon_state = "billiard1" - anchored = TRUE - density = TRUE - -/obj/structure/billiard_table/Initialize(mapload) - . = ..() - icon_state = "billiard[rand(1, 3)]" - /obj/structure/pole name = "stripper pole" desc = "A pole fastened to the ceiling and floor, used to show of ones goods to company." @@ -652,21 +640,6 @@ icon = 'modular_darkpack/modules/deprecated/icons/64x64.dmi' icon_state = "kover" -/obj/were_ice - name = "ice block" - desc = "Stores some precious organs..." - icon = 'modular_darkpack/modules/deprecated/icons/werewolf_lupus.dmi' - icon_state = "ice_man" - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - -/obj/were_ice/lupus - icon_state = "ice_wolf" - -/obj/were_ice/crinos - icon = 'modular_darkpack/modules/deprecated/icons/werewolf.dmi' - icon_state = "ice" - pixel_w = -8 - /obj/structure/bury_pit name = "bury pit" desc = "You can bury someone here." @@ -677,41 +650,43 @@ anchored = TRUE density = FALSE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/burying = FALSE - -// DARKPACK TODO - reimplement -/* -/obj/structure/bury_pit/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/shovel/vamp)) - if(!burying) - burying = TRUE - user.visible_message(span_warning("[user] starts to dig [src]"), span_warning("You start to dig [src].")) - if(do_mob(user, src, 10 SECONDS)) - burying = FALSE - if(icon_state == "pit0") - for(var/mob/living/L in get_turf(src)) - L.forceMove(src) - icon_state = "pit1" - user.visible_message(span_warning("[user] digs a hole in [src]."), span_warning("You dig a hole in [src].")) - else - for(var/mob/living/L in src) - L.forceMove(get_turf(src)) - icon_state = "pit0" - user.visible_message(span_warning("[user] digs a hole in [src]."), span_warning("You dig a hole in [src].")) - else - burying = FALSE - -/obj/structure/bury_pit/container_resist_act(mob/living/user) - if(!burying) - burying = TRUE - if(do_mob(user, src, 30 SECONDS)) + var/pit_busy = FALSE + +/obj/structure/bury_pit/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(tool.tool_behaviour == TOOL_SHOVEL) + if(pit_busy) + return ITEM_INTERACT_BLOCKING + + pit_busy = TRUE + user.visible_message(span_warning("[user] starts to dig [src]"), span_warning("You start to dig [src].")) + if(!do_after(user, 10 SECONDS, src)) + pit_busy = FALSE + + pit_busy = FALSE + if(icon_state == "pit0") + for(var/mob/living/L in get_turf(src)) + L.forceMove(src) + icon_state = "pit1" + user.visible_message(span_warning("[user] digs a hole in [src]."), span_warning("You dig a hole in [src].")) + else for(var/mob/living/L in src) L.forceMove(get_turf(src)) icon_state = "pit0" - burying = FALSE - else - burying = FALSE -*/ + user.visible_message(span_warning("[user] digs a hole in [src]."), span_warning("You dig a hole in [src].")) + +/obj/structure/bury_pit/container_resist_act(mob/living/user) + if(pit_busy) + return + + pit_busy = TRUE + if(!do_after(user, 30 SECONDS, src)) + pit_busy = FALSE + + for(var/mob/living/L in src) + L.forceMove(get_turf(src)) + icon_state = "pit0" + pit_busy = FALSE + /obj/structure/fluff/tv name = "\improper TV" diff --git a/modular_darkpack/modules/decor/code/stick.dm b/modular_darkpack/modules/decor/code/stick.dm index 7a0fb8cdaee2..e068f41a54ac 100644 --- a/modular_darkpack/modules/decor/code/stick.dm +++ b/modular_darkpack/modules/decor/code/stick.dm @@ -43,8 +43,10 @@ if(isliving(triggerer)) var/mob/living/stepper = triggerer if(stepper.mob_size >= MOB_SIZE_HUMAN) - var/roll = SSroll.storyteller_roll(stepper.st_get_stat(STAT_PERCEPTION) + stepper.st_get_stat(STAT_STEALTH), 6, stepper) - if(!roll == ROLL_SUCCESS) + var/datum/storyteller_roll/step_roll = new() + step_roll.applicable_stats = list(STAT_PERCEPTION, STAT_STEALTH) + var/roll_result = step_roll.st_roll(triggerer, src) + if(!roll_result == ROLL_SUCCESS) mineEffect(triggerer) if(isitem(triggerer)) diff --git a/modular_darkpack/modules/deprecated/icons/32x48.dmi b/modular_darkpack/modules/deprecated/icons/32x48.dmi index f29a6a2c7af1..d3b06fc711f3 100644 Binary files a/modular_darkpack/modules/deprecated/icons/32x48.dmi and b/modular_darkpack/modules/deprecated/icons/32x48.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/48x32.dmi b/modular_darkpack/modules/deprecated/icons/48x32.dmi index 492b10ca9158..608b9520cbec 100644 Binary files a/modular_darkpack/modules/deprecated/icons/48x32.dmi and b/modular_darkpack/modules/deprecated/icons/48x32.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/48x48.dmi b/modular_darkpack/modules/deprecated/icons/48x48.dmi index 91e622fc89a1..b36897ceb1b9 100644 Binary files a/modular_darkpack/modules/deprecated/icons/48x48.dmi and b/modular_darkpack/modules/deprecated/icons/48x48.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/onfloor.dmi b/modular_darkpack/modules/deprecated/icons/onfloor.dmi index 551fbe5c75f6..617f3f077bc2 100644 Binary files a/modular_darkpack/modules/deprecated/icons/onfloor.dmi and b/modular_darkpack/modules/deprecated/icons/onfloor.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/radio.dmi b/modular_darkpack/modules/deprecated/icons/radio.dmi deleted file mode 100644 index 4b1cc0088ecf..000000000000 Binary files a/modular_darkpack/modules/deprecated/icons/radio.dmi and /dev/null differ diff --git a/modular_darkpack/modules/deprecated/icons/werewolf.dmi b/modular_darkpack/modules/deprecated/icons/werewolf.dmi deleted file mode 100644 index 036acf563686..000000000000 Binary files a/modular_darkpack/modules/deprecated/icons/werewolf.dmi and /dev/null differ diff --git a/modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi b/modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi deleted file mode 100644 index 76eb03fbdf60..000000000000 Binary files a/modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi and /dev/null differ diff --git a/modular_darkpack/modules/deprecated/icons/werewolf_ui.dmi b/modular_darkpack/modules/deprecated/icons/werewolf_ui.dmi deleted file mode 100644 index a24e95976b79..000000000000 Binary files a/modular_darkpack/modules/deprecated/icons/werewolf_ui.dmi and /dev/null differ diff --git a/modular_darkpack/modules/deprecated/sounds/werewolf_fall.ogg b/modular_darkpack/modules/deprecated/sounds/werewolf_fall.ogg deleted file mode 100644 index 6ea5225344aa..000000000000 Binary files a/modular_darkpack/modules/deprecated/sounds/werewolf_fall.ogg and /dev/null differ diff --git a/modular_darkpack/modules/doors/code/keys/keys.dm b/modular_darkpack/modules/doors/code/keys/keys.dm index 1cec601ec2e4..4a1976b572a8 100644 --- a/modular_darkpack/modules/doors/code/keys/keys.dm +++ b/modular_darkpack/modules/doors/code/keys/keys.dm @@ -368,7 +368,7 @@ LOCKACCESS_POLICE_CAPTAIN ) -//===========================GAROU KEYS=========================== +//===========================FERA KEYS=========================== /obj/item/vamp/keys/nps name = "\improper Park Service keys" diff --git a/modular_darkpack/modules/doors/code/vampdoor.dm b/modular_darkpack/modules/doors/code/vampdoor.dm index f39c07046b75..e326d7a8da04 100644 --- a/modular_darkpack/modules/doors/code/vampdoor.dm +++ b/modular_darkpack/modules/doors/code/vampdoor.dm @@ -25,17 +25,14 @@ var/door_broken = FALSE var/door_layer = CLOSED_DOOR_LAYER var/lock_id = null - var/lockpick_timer = LOCKTIMER_1 var/lockpick_difficulty = 6 var/open_sound = 'modular_darkpack/modules/doors/sounds/door_open.ogg' var/close_sound = 'modular_darkpack/modules/doors/sounds/door_close.ogg' var/lock_sound = 'modular_darkpack/modules/doors/sounds/door_locked.ogg' var/burnable = FALSE - /// Cooldown for bashing attempts - COOLDOWN_DECLARE(bash_cooldown) - /// Cooldown for lockpicking attempts - COOLDOWN_DECLARE(lockpick_cooldown) + var/datum/storyteller_roll/lockpick/lockpick_roll + var/datum/storyteller_roll/bash_door/bash_roll /// Difficulty for bashing this door down var/bash_difficulty = 6 /// Number of successes needed to bash down @@ -47,21 +44,6 @@ register_context() AddElement(/datum/element/contextual_screentip_bare_hands, rmb_text = "Try lock") - switch(lockpick_difficulty) //This is fine because any overlap gets intercepted before - if(LOCKDIFFICULTY_7 to INFINITY) - lockpick_timer = LOCKTIMER_7 - if(LOCKDIFFICULTY_6 to LOCKDIFFICULTY_7) - lockpick_timer = LOCKTIMER_6 - if(LOCKDIFFICULTY_5 to LOCKDIFFICULTY_6) - lockpick_timer = LOCKTIMER_5 - if(LOCKDIFFICULTY_4 to LOCKDIFFICULTY_5) - lockpick_timer = LOCKTIMER_4 - if(LOCKDIFFICULTY_3 to LOCKDIFFICULTY_4) - lockpick_timer = LOCKTIMER_3 - if(LOCKDIFFICULTY_2 to LOCKDIFFICULTY_3) - lockpick_timer = LOCKTIMER_2 - if(-INFINITY to LOCKDIFFICULTY_2) //LOCKDIFFICULTY_1 is basically the minimum so we can just do LOCKTIMER_1 from -INFINITY - lockpick_timer = LOCKTIMER_1 /obj/structure/vampdoor/CanAStarPass(to_dir, datum/can_pass_info/pass_info) return !density || !locked @@ -209,27 +191,27 @@ if(ishuman(user)) var/mob/living/carbon/human/human_user = user if(human_user.st_get_stat(STAT_STRENGTH) > 5) - if(!COOLDOWN_FINISHED(src, bash_cooldown)) - var/time_left = COOLDOWN_TIMELEFT(src, bash_cooldown) - to_chat(human_user, span_warning("You must wait [time_left / 10] seconds before attempting to rip the door off it's hinges again.")) - return - var/roll = SSroll.storyteller_roll(human_user.st_get_stat(STAT_STRENGTH), bash_difficulty, human_user, numerical = TRUE) - if(roll >= bash_successes_needed) - to_chat(human_user, span_danger("You wind up a big punch to break down the door...")) - if(do_after(human_user, 3 SECONDS, src)) - proc_unlock(50) - break_door(human_user) - else - to_chat(human_user, span_danger("You must be standing next to the door to break it down.")) - else - pixel_z = pixel_z+rand(-1, 1) - pixel_w = pixel_w+rand(-1, 1) - playsound(get_turf(src), 'modular_darkpack/master_files/sounds/effects/door/get_bent.ogg', 50, TRUE) - proc_unlock(5) - to_chat(user, span_warning("You aren't strong enough to break it down! You hurt your shoulder by punching the door!")) - human_user.adjust_brute_loss(30) - addtimer(CALLBACK(src, PROC_REF(reset_transform)), 2) - COOLDOWN_START(src, bash_cooldown, 1 SCENES) + if(!bash_roll) + bash_roll = new() + bash_roll.difficulty = bash_difficulty + bash_roll.successes_needed = bash_successes_needed + var/roll = bash_roll.st_roll(user, src) + switch(roll) + if(ROLL_SUCCESS) + to_chat(human_user, span_danger("You wind up a big punch to break down the door...")) + if(do_after(human_user, 3 SECONDS, src)) + proc_unlock(50) + break_door(human_user) + else + to_chat(human_user, span_danger("You must be standing next to the door to break it down.")) + if(ROLL_FAILURE, ROLL_BOTCH) + pixel_z = pixel_z+rand(-1, 1) + pixel_w = pixel_w+rand(-1, 1) + playsound(get_turf(src), 'modular_darkpack/master_files/sounds/effects/door/get_bent.ogg', 50, TRUE) + proc_unlock(5) + to_chat(user, span_warning("You aren't strong enough to break it down! You hurt your shoulder by punching the door!")) + human_user.adjust_brute_loss(1 LETHAL_TTRPG_DAMAGE) + addtimer(CALLBACK(src, PROC_REF(reset_transform)), 2) else pixel_z = pixel_z+rand(-1, 1) pixel_w = pixel_w+rand(-1, 1) @@ -249,7 +231,8 @@ var/has_keys = FALSE for(var/obj/item/vamp/keys/found_key in user) - if(!do_after(user, 1 SECONDS, src, interaction_key = DOAFTER_SOURCE_DOOR)) + // check if we already set has_keys so the first key you try and no do_after. + if(has_keys && !do_after(user, 1 SECONDS, src, interaction_key = DOAFTER_SOURCE_DOOR)) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN has_keys = TRUE if(try_keys(user, found_key)) @@ -301,31 +284,26 @@ if(CONFIG_GET(flag/punishing_zero_dots) && user.st_get_stat(STAT_LARCENY) < 1) to_chat(user, span_warning("How do I do this...?")) return - if(!COOLDOWN_FINISHED(src, lockpick_cooldown)) - var/time_left = COOLDOWN_TIMELEFT(src, lockpick_cooldown) - to_chat(user, span_warning("You must wait [time_left / 10] seconds before attempting another lockpick!")) - return if(locked) proc_unlock(5) playsound(src, 'modular_darkpack/modules/doors/sounds/hack.ogg', 100, TRUE) for(var/mob/living/carbon/human/npc/police/P in oviewers(DEFAULT_SIGHT_DISTANCE, src)) P.Aggro(user) - var/total_lockpicking = user.st_get_stat(STAT_LARCENY) - if(do_after(user, lockpick_timer, src, interaction_key = DOAFTER_SOURCE_DOOR)) + if(do_after(user, 1 TURNS, src, interaction_key = DOAFTER_SOURCE_DOOR)) if(!locked) return - var/roll_result = SSroll.storyteller_roll(total_lockpicking + (user.st_get_stat(STAT_DEXTERITY, FALSE)), lockpick_difficulty, list(user), user) - switch(roll_result) + if(!lockpick_roll) + lockpick_roll = new() + lockpick_roll.difficulty = lockpick_difficulty + switch(lockpick_roll.st_roll(user, src)) if(ROLL_SUCCESS) to_chat(user, span_notice("You pick the lock.")) locked = FALSE return TRUE if(ROLL_FAILURE) to_chat(user, span_warning("You failed to pick the lock.")) - COOLDOWN_START(src, lockpick_cooldown, 1 SCENES) if(ROLL_BOTCH) to_chat(user, span_warning("Your lockpick broke!")) - COOLDOWN_START(src, lockpick_cooldown, 1 SCENES) qdel(tool) else to_chat(user, span_warning("You failed to pick the lock.")) diff --git a/modular_darkpack/modules/drugs/code/bloodpacks/morphpack.dm b/modular_darkpack/modules/drugs/code/bloodpacks/morphpack.dm index e22b9dc6f5c0..7d1f1e03dfb6 100644 --- a/modular_darkpack/modules/drugs/code/bloodpacks/morphpack.dm +++ b/modular_darkpack/modules/drugs/code/bloodpacks/morphpack.dm @@ -40,7 +40,6 @@ M.update_damage_overlays() M.update_health_hud() if(iskindred(M)) - M.update_blood_hud() H.reagents.trans_to(M, min(10, H.reagents.total_volume), transfered_by = H, methods = INGEST) //calling the earlier variable to transfer to target, M playsound(M.loc,'sound/items/drink.ogg', 50, TRUE) return diff --git a/modular_darkpack/modules/drugs/code/weed/bong.dm b/modular_darkpack/modules/drugs/code/weed/bong.dm index 401cf1f0609f..0a523d9ce054 100644 --- a/modular_darkpack/modules/drugs/code/weed/bong.dm +++ b/modular_darkpack/modules/drugs/code/weed/bong.dm @@ -37,32 +37,34 @@ . = ..() create_reagents(chem_volume, INJECTABLE | NO_REACT) -/obj/item/bong/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) - if((istype(attacking_item, /obj/item/food/grown) || istype(attacking_item, /obj/item/food/drug))) +/obj/item/bong/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if((istype(tool, /obj/item/food/grown) || istype(tool, /obj/item/food/drug))) if(packeditem) to_chat(user, span_warning("It is already packed!")) - return + return ITEM_INTERACT_BLOCKING - if(istype(attacking_item, /obj/item/food/grown) && !HAS_TRAIT(attacking_item, TRAIT_DRIED)) + if(istype(tool, /obj/item/food/grown) && !HAS_TRAIT(tool, TRAIT_DRIED)) to_chat(user, span_warning("It has to be dried first!")) - return + return ITEM_INTERACT_BLOCKING - to_chat(user, span_notice("You stuff [attacking_item] into [src].")) + to_chat(user, span_notice("You stuff [tool] into [src].")) bong_hits = max_hits - packeditem = attacking_item.name + packeditem = tool.name update_name() - if(attacking_item.reagents) - attacking_item.reagents.trans_to(src, attacking_item.reagents.total_volume, transferred_by = user) + if(tool.reagents) + tool.reagents.trans_to(src, tool.reagents.total_volume, transferred_by = user) reagent_transfer_per_use = reagents.total_volume / max_hits - qdel(attacking_item) + qdel(tool) + return ITEM_INTERACT_SUCCESS else - var/lighting_text = attacking_item.ignition_effect(src, user) + var/lighting_text = tool.ignition_effect(src, user) if(!lighting_text) - return ..() + return NONE if(bong_hits <= 0) to_chat(user, span_warning("Nothing to smoke!")) - return ..() + return ITEM_INTERACT_BLOCKING light(lighting_text) + return ITEM_INTERACT_SUCCESS /obj/item/bong/attack_self(mob/user) var/turf/location = get_turf(user) @@ -75,19 +77,22 @@ empty_out() return -/obj/item/bong/attack(mob/living/target_mob, mob/living/user, list/modifiers, list/attack_modifiers) +/obj/item/bong/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!isliving(interacting_with)) + return NONE + var/mob/living/interacting_living = interacting_with if(!packeditem || !lit) - return - target_mob.visible_message( - span_notice("[user] starts [target_mob == user ? "taking a hit from [src]." : "forcing [target_mob] to take a hit from [src]!"]"), - "[target_mob == user ? span_notice("You start taking a hit from [src].") : span_danger("[user] starts forcing you to take a hit from [src]!")]" + return ITEM_INTERACT_BLOCKING + interacting_with.visible_message( + span_notice("[user] starts [interacting_with == user ? "taking a hit from [src]." : "forcing [interacting_with] to take a hit from [src]!"]"), + "[interacting_with == user ? span_notice("You start taking a hit from [src].") : span_danger("[user] starts forcing you to take a hit from [src]!")]" ) playsound(src, 'modular_darkpack/modules/drugs/sounds/heatdam.ogg', 50, TRUE) if(!do_after(user, 4 SECONDS, src)) - return - to_chat(target_mob, span_notice("You finish taking a hit from [src].")) + return ITEM_INTERACT_BLOCKING + to_chat(interacting_with, span_notice("You finish taking a hit from [src].")) if(reagents.total_volume) - reagents.trans_to(target_mob, reagent_transfer_per_use, methods = INHALE, ignore_stomach = TRUE) + reagents.trans_to(interacting_with, reagent_transfer_per_use, methods = INHALE, ignore_stomach = TRUE) bong_hits-- var/turf/open/pos = get_turf(src) if(istype(pos)) @@ -95,15 +100,16 @@ spawn_cloud(pos, smoke_range) if(moan_chance > 0) if(prob(moan_chance)) - playsound(target_mob, pick('modular_darkpack/modules/drugs/sounds/lungbust_moan1.ogg','modular_darkpack/modules/drugs/sounds/lungbust_moan2.ogg', 'modular_darkpack/modules/drugs/sounds/lungbust_moan3.ogg'), 50, TRUE) - target_mob.emote("moan") + playsound(interacting_with, pick('modular_darkpack/modules/drugs/sounds/lungbust_moan1.ogg','modular_darkpack/modules/drugs/sounds/lungbust_moan2.ogg', 'modular_darkpack/modules/drugs/sounds/lungbust_moan3.ogg'), 50, TRUE) + interacting_living.emote("moan") else - playsound(target_mob, pick('modular_darkpack/modules/drugs/sounds/lungbust_cough1.ogg','modular_darkpack/modules/drugs/sounds/lungbust_cough2.ogg'), 50, TRUE) - target_mob.emote("cough") + playsound(interacting_with, pick('modular_darkpack/modules/drugs/sounds/lungbust_cough1.ogg','modular_darkpack/modules/drugs/sounds/lungbust_cough2.ogg'), 50, TRUE) + interacting_living.emote("cough") if(bong_hits <= 0) - to_chat(target_mob, span_warning("Out of uses!")) + to_chat(interacting_with, span_warning("Out of uses!")) put_out() empty_out() + return ITEM_INTERACT_SUCCESS /obj/item/bong/proc/light(flavor_text = null) if(lit) @@ -115,16 +121,8 @@ name = "lit [initial(name)]" set_light_on(TRUE) - if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire - var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(round(reagents.get_reagent_amount(/datum/reagent/toxin/plasma) * 0.4, 1), get_turf(src), 0, 0) - explosion.start() - qdel(src) - return - if(reagents.get_reagent_amount(/datum/reagent/fuel)) // the fuel explodes, too, but much less violently - var/datum/effect_system/reagents_explosion/explosion = new() - explosion.set_up(round(reagents.get_reagent_amount(/datum/reagent/fuel) * 0.2, 1), get_turf(src), 0, 0) - explosion.start() + if(reagents.spark_act(0, NONE, list()) & SPARK_ACT_DESTRUCTIVE) + usr?.log_message("lit a rigged bong", LOG_VICTIM) qdel(src) return diff --git a/modular_darkpack/modules/economy/code/atm.dm b/modular_darkpack/modules/economy/code/atm.dm index a5ec55400a22..6153f4d31a18 100644 --- a/modular_darkpack/modules/economy/code/atm.dm +++ b/modular_darkpack/modules/economy/code/atm.dm @@ -48,7 +48,7 @@ return ITEM_INTERACT_BLOCKING var/value = tool.get_item_credit_value() inserted_cash += value - to_chat(user, span_notice("You have deposited [value] dollars into [src]. [src] now holds [inserted_cash] dollars.")) + to_chat(user, span_notice("You have deposited [value] [MONEY_NAME] into [src]. [src] now holds [inserted_cash] [MONEY_NAME].")) qdel(tool) return ITEM_INTERACT_SUCCESS @@ -105,7 +105,7 @@ if("deposit") if(inserted_cash > 0) logged_account.adjust_money(inserted_cash, "ATM: Deposit") - to_chat(usr, span_notice("You have deposited [inserted_cash] dollars into your card. Your new balance is [logged_account.account_balance] dollars.")) + to_chat(usr, span_notice("You have deposited [inserted_cash] [MONEY_NAME] into your card. Your new balance is [logged_account.account_balance] [MONEY_NAME].")) total_stored_cash += inserted_cash inserted_cash = 0 return TRUE @@ -124,7 +124,7 @@ to_chat(user, span_notice("[src] has maxed out its withdraw limit")) break var/obj/item/stack/dollar/cash = new(loc, drop_amount) - to_chat(user, span_notice("You have withdrawn [drop_amount] dollars.")) + to_chat(user, span_notice("You have withdrawn [drop_amount] [MONEY_NAME].")) try_put_in_hand(cash, user) amount -= drop_amount total_stored_cash -= drop_amount diff --git a/modular_darkpack/modules/economy/code/dollar.dm b/modular_darkpack/modules/economy/code/dollar.dm index 35d5e93ff266..8dd28635a077 100644 --- a/modular_darkpack/modules/economy/code/dollar.dm +++ b/modular_darkpack/modules/economy/code/dollar.dm @@ -1,5 +1,5 @@ /obj/item/stack/dollar - name = "dollars" + name = MONEY_NAME // Prob overkill to use the define here. desc = "Wow! With enough of these, you could buy a lot! ...Pssh, yeah right." singular_name = "dollar" icon_state = "money1" diff --git a/modular_darkpack/modules/economy/code/selling/lombard.dm b/modular_darkpack/modules/economy/code/selling/lombard.dm index 1bc8f3d4d329..93415d2f9b71 100644 --- a/modular_darkpack/modules/economy/code/selling/lombard.dm +++ b/modular_darkpack/modules/economy/code/selling/lombard.dm @@ -2,6 +2,12 @@ #define SALE_DIFFICULTY 6 #define BOTCH_FAILURE_PENALTY 0.5 +/datum/storyteller_roll/fencing + bumper_text = "fencing" + applicable_stats = list(STAT_CHARISMA, STAT_FINANCE) + difficulty = SALE_DIFFICULTY + numerical = TRUE + /obj/lombard name = "pawnshop" desc = "Sell your stuff." @@ -10,17 +16,19 @@ icon = 'modular_darkpack/modules/retail/icons/vendors_shops.dmi' anchored = TRUE var/black_market = FALSE + var/datum/storyteller_roll/fencing/sell_roll -/obj/lombard/attackby(obj/item/W, mob/living/carbon/human/user, params) - var/datum/component/selling/selling_comp = W.GetComponent(/datum/component/selling) +/obj/lombard/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + var/datum/component/selling/selling_comp = tool.GetComponent(/datum/component/selling) if(!selling_comp) - return ..() + return NONE if(selling_comp.illegal != black_market) to_chat(user, span_warning("[black_market ? "This" : "The pawnshop"] doesn't accept [selling_comp.illegal ? "illegal" : "legal"] goods.")) - return + return ITEM_INTERACT_BLOCKING - sell_one_item(W, user) + sell_one_item(tool, user) + return ITEM_INTERACT_SUCCESS /// Sell a single item /obj/lombard/proc/sell_one_item(obj/item/sold, mob/living/user) @@ -56,13 +64,10 @@ var/list/sold_items = list() var/total_sale_price = 0 + if(!sell_roll) + sell_roll = new() // Make a single roll to sell all your items in bulk - var/negotiation_success_count = 0 - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/negotiation_dice = H.st_get_stat(STAT_CHARISMA) + H.st_get_stat(STAT_FINANCE) - if(negotiation_dice > 0) - negotiation_success_count = SSroll.storyteller_roll(negotiation_dice, SALE_DIFFICULTY, H, src, TRUE) + var/negotiation_success_count = sell_roll.st_roll(user, src) for(var/obj/item/sold in items_to_sell) var/datum/component/selling/selling_comp = sold.GetComponent(/datum/component/selling) @@ -105,20 +110,16 @@ return round(base_price * stack_multiplier * negotiation_success_count) return round(base_price * stack_multiplier * BOTCH_FAILURE_PENALTY) - // otherwise, roll for negotiation in a single item sale - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/negotiation_dice = H.st_get_stat(STAT_CHARISMA) + H.st_get_stat(STAT_FINANCE) - - if(negotiation_dice > 0) - var/success_count = SSroll.storyteller_roll(negotiation_dice, SALE_DIFFICULTY, H, src, TRUE) - if(success_count > 0) - return round(base_price * stack_multiplier * success_count) - return round(base_price * stack_multiplier * BOTCH_FAILURE_PENALTY) + // otherwise, roll for negotiation in a single item sale + if(!sell_roll) + sell_roll = new() + // Make a single roll to sell all your items in bulk + var/success_count = sell_roll.st_roll(user, src) - // No negotiation dice (which should rarely happen) = ZERO! Completely scammed at 0 finance 0 charisma - return 0 + if(success_count > 0) + return round(base_price * stack_multiplier * success_count) + return round(base_price * stack_multiplier * BOTCH_FAILURE_PENALTY) /obj/lombard/proc/spawn_money(amount, atom/spawn_location) if(amount <= 0) diff --git a/modular_darkpack/modules/economy/code/stocks_license.dm b/modular_darkpack/modules/economy/code/stocks_license.dm index df35dd249fa9..daac49ca3183 100644 --- a/modular_darkpack/modules/economy/code/stocks_license.dm +++ b/modular_darkpack/modules/economy/code/stocks_license.dm @@ -32,4 +32,4 @@ /obj/item/stocks_license/examine(mob/user) . = ..() - . += "Balance: [balance] dollars" + . += "Balance: [balance] [MONEY_NAME]" diff --git a/modular_darkpack/modules/electricity/code/fusebox.dm b/modular_darkpack/modules/electricity/code/fusebox.dm index ec09bb1e89df..a0bbd69e9226 100644 --- a/modular_darkpack/modules/electricity/code/fusebox.dm +++ b/modular_darkpack/modules/electricity/code/fusebox.dm @@ -42,8 +42,7 @@ power_area.power_environ = FALSE power_area.power_change() power_area.fire_controled = FALSE - var/datum/effect_system/spark_spread/local_spark = new /datum/effect_system/spark_spread - local_spark.set_up(5, 1, get_turf(src)) + var/datum/effect_system/basic/spark_spread/local_spark = new(get_turf(src), 5, 1) local_spark.start() for(var/obj/machinery/light/L in power_area) L.update(FALSE) @@ -52,32 +51,39 @@ update_icon() update_sound_state() -/obj/fusebox/attackby(obj/item/I, mob/living/user, params) - if(I.tool_behaviour == TOOL_WIRECUTTER) +/obj/fusebox/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(tool.tool_behaviour == TOOL_WIRECUTTER) if(!repairing) repairing = TRUE - if(do_after(user, 10 SECONDS, src)) - damaged = 0 - update_icon_state() - update_sound_state() - playsound(get_turf(src),'modular_darkpack/modules/electricity/sounds/fusebox_fix.ogg', 50, FALSE) - var/area/power_area = get_area(src) - power_area.power_light = TRUE - power_area.power_equip = TRUE - power_area.power_environ = TRUE - power_area.power_change() - if(initial(power_area.fire_controled)) - power_area.fire_controled = TRUE - for(var/obj/machinery/light/L in power_area) - L.update(FALSE) + if(!do_after(user, 10 SECONDS, src)) repairing = FALSE - else - repairing = FALSE - else - ..() - if(I.force) - damaged += I.force - check_damage(user) + return ITEM_INTERACT_BLOCKING + + damaged = 0 + update_icon_state() + update_sound_state() + playsound(get_turf(src),'modular_darkpack/modules/electricity/sounds/fusebox_fix.ogg', 50, FALSE) + var/area/power_area = get_area(src) + power_area.power_light = TRUE + power_area.power_equip = TRUE + power_area.power_environ = TRUE + power_area.power_change() + if(initial(power_area.fire_controled)) + power_area.fire_controled = TRUE + for(var/obj/machinery/light/L in power_area) + L.update(FALSE) + + repairing = FALSE + return ITEM_INTERACT_SUCCESS + + return NONE + +// This sucks. Snowflaking its own integrity system is always bad. +/obj/fusebox/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers) + . = ..() + if(attacking_item.force) + damaged += attacking_item.force + check_damage(user) // transformers (another type of fusebox) /obj/fusebox/transformer diff --git a/modular_darkpack/modules/electricity/code/generator.dm b/modular_darkpack/modules/electricity/code/generator.dm index ab33b51571f0..7d14464bf6f9 100644 --- a/modular_darkpack/modules/electricity/code/generator.dm +++ b/modular_darkpack/modules/electricity/code/generator.dm @@ -57,8 +57,7 @@ SEND_SOUND(M, 'modular_darkpack/modules/electricity/sounds/generator_shutdown.ogg') A.requires_power = TRUE A.fire_controled = FALSE - var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread - s.set_up(5, 1, get_turf(src)) + var/datum/effect_system/basic/spark_spread/s = new(get_turf(src), 5, 1) s.start() for(var/obj/machinery/light/L in A) L.on = FALSE diff --git a/modular_darkpack/modules/ert/code/items/first_team.dm b/modular_darkpack/modules/ert/code/items/first_team.dm new file mode 100644 index 000000000000..022ae62ddde9 --- /dev/null +++ b/modular_darkpack/modules/ert/code/items/first_team.dm @@ -0,0 +1,370 @@ +//------------EQUIPMENT------------ +/datum/armor/first_team + melee = 70 + bullet = 70 + laser = 70 + energy = 70 + fire = 70 + bomb = 70 + acid = 70 + wound = 70 + +//------------SHOES------------ +/obj/item/clothing/shoes/vampire/darkpack_ert + name = "shoes" + desc = "Comfortable-looking shoes." + icon = 'modular_darkpack/modules/ert/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/ert/icons/worn.dmi' + icon_state = "ftboots" + inhand_icon_state = null + gender = PLURAL + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + + +/obj/item/clothing/shoes/vampire/darkpack_ert/firstteam + name = "\improper First team boots" + desc = "Pitch-black boots with hard, industrial laces." + icon_state = "ftboots" + armor_type = /datum/armor/shoes_jackboots + +//------------GLOVES------------ + +/obj/item/clothing/gloves/vampire/darkpack_ert + icon = 'modular_darkpack/modules/ert/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/ert/icons/worn.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "ftgloves" + undyeable = TRUE + + +/obj/item/clothing/gloves/vampire/darkpack_ert/firstteam + name = "\improper First Team gloves" + desc = "Provides protection from the good, the bad and the ugly." + icon_state = "ftgloves" + body_parts_covered = HANDS + armor_type = /datum/armor/gloves_combat + +//------------HELMET------------ + +/obj/item/clothing/head/vampire/darkpack_ert + icon_state = "fthelmet" + icon = 'modular_darkpack/modules/ert/icons/clothing.dmi' + inhand_icon_state = null + worn_icon = 'modular_darkpack/modules/ert/icons/worn.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + +/obj/item/clothing/head/vampire/darkpack_ert/firstteam_helmet + name = "\improper First Team helmet" + desc = "A black helmet with two, green-glowing eye-pieces that seem to stare through your soul." + icon_state = "fthelmet" + armor_type = /datum/armor/first_team + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEHAIR + visor_flags_inv = HIDEFACE|HIDESNOUT + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF + visor_flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF + +//------------ARMOR------------ + +/obj/item/clothing/suit/vampire/darkpack_ert + icon_state = "ftuni" + icon = 'modular_darkpack/modules/ert/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/ert/icons/worn.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + inhand_icon_state = null + + body_parts_covered = CHEST + cold_protection = CHEST|GROIN + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = CHEST|GROIN + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + max_integrity = 250 + resistance_flags = NONE + +/obj/item/clothing/suit/vampire/darkpack_ert/Initialize() + . = ..() + AddComponent(/datum/component/selling, 200, "suit", FALSE) + + +/obj/item/clothing/suit/vampire/darkpack_ert/firstteam_armor + name = "\improper First Team Armoured Vest" + desc = "A strong looking, armoured-vest with a large '1' engraved onto the breast." + icon_state = "ftarmor" + inhand_icon_state = null + armor_type = /datum/armor/first_team + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS + clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED) + +//------------SUIT------------ + +/obj/item/clothing/under/vampire/darkpack_ert + name = "\improper First Team uniform" + desc = "A completely blacked out uniform with a large '1' symbol sewn onto the shoulder-pad." + icon_state = "ftuni" + has_sensor = NO_SENSORS + random_sensor = FALSE + can_adjust = FALSE + icon = 'modular_darkpack/modules/ert/icons/clothing.dmi' + worn_icon = 'modular_darkpack/modules/ert/icons/worn.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + + +/obj/item/clothing/under/vampire/darkpack_ert/Initialize() + . = ..() + AddComponent(/datum/component/selling, 100, "undersuit", FALSE) + +/obj/item/clothing/under/vampire/darkpack_ert/firstteam_uniform + name = "First Team uniform" + desc = "A completely blacked out uniform with a large '1' symbol sewn onto the shoulder-pad." + icon_state = "ftuni" + armor_type = /datum/armor/clothing_under/security_head_of_security + +//------------Glasses------------ + +/obj/item/clothing/glasses/night/thermal + vision_flags = SEE_MOBS + +//------------Weapons------------/obj/item/ammo_casing/vampire/c12gvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv +/obj/item/ammo_box/darkpack/c556/bale //DONT EVER PUT THIS IN A MAP + name = "balefire ammo box (5.56)" + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "556box-bale" + ammo_type = /obj/item/ammo_casing/vampire/c556mm/bale + +/obj/item/ammo_casing/vampire/c556mm/bale + name = "green 5.56mm bullet casing" + desc = "A modified 5.56mm bullet casing." + caliber = CALIBER_556NATO + projectile_type = /obj/projectile/bullet/darkpack/vamp556mm/bale + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "b556" + base_icon_state = "b556" + +/obj/projectile/bullet/darkpack/vamp556mm/bale + armour_penetration = 50 + damage = 45 + var/bloodloss = 1 + +/obj/projectile/bullet/darkpack/vamp556mm/bale/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if(iskindred(target) || isghoul(target)) + var/mob/living/carbon/human/H = target + if(H.bloodpool == 0) + to_chat(H, span_warning("Only ash remains in my veins!")) + H.apply_damage(20, BURN) + return + H.adjust_blood_pool(-bloodloss) + playsound(H, 'modular_darkpack/modules/ert/sounds/balefire.ogg', rand(10,15), TRUE) + to_chat(H, span_warning("Green flames errupt from the bullets impact, boiling your blood!")) +// DARKPACK TODO - GAROU +/* + if(iswerewolf(target) || isgarou(target)) + var/mob/living/carbon/M = target + if(M.auspice.gnosis) + if(prob(50)) + adjust_gnosis(-1, M) + M.apply_damage(20, CLONE) + playsound(M, 'modular_tfn/modules/first_team/audio/balefire.ogg', rand(10,15), TRUE) + M.apply_status_effect(STATUS_EFFECT_SILVER_SLOWDOWN) +*/ +/obj/item/ammo_casing/vampire/c12g/f12g + name = "Frag-12g shell casing" + desc = "A 12g explosive shell casing." + caliber = CALIBER_SHOTGUN + projectile_type = /obj/projectile/bullet/darkpack/f12g + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "f12" + base_icon_state = "f12" + +/obj/projectile/bullet/darkpack/f12g + name = "12g explosive slug" + damage = 60 + armour_penetration = 50 + exposed_wound_bonus = 10 + wound_bonus = 5 + +/obj/projectile/bullet/darkpack/f12g/on_hit(atom/target, blocked = 0, pierce_hit) + ..() + explosion(target, devastation_range = -1, light_impact_range = 2, explosion_cause = src) + return BULLET_ACT_HIT + +/obj/item/ammo_box/darkpack/f12g //DO NOT DISTRIBUTE NORMALLY + name = "ammo box (f12g)" + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "12box_frag" + ammo_type = /obj/item/ammo_casing/vampire/c12g/f12g + max_ammo = 40 + +/obj/item/ammo_box/magazine/darkpack/px66f + name = "\improper PX66F magazine (5.56mm)" + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "px66f" + inhand_icon_state = null + ammo_type = /obj/item/ammo_casing/vampire/c556mm/bale + caliber = CALIBER_556NATO + max_ammo = 30 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/darkpack/px249f + name = "\improper PX249F box magazine (5.56mm)" + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/weapons/icons/weapons_onfloor.dmi') + inhand_icon_state = null + icon_state = "px249f" + ammo_type = /obj/item/ammo_casing/vampire/c556mm/bale + caliber = CALIBER_556NATO + max_ammo = 200 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/internal/px12r + name = "shotgun internal magazine" + ammo_type = /obj/item/ammo_casing/vampire/c12g + caliber = CALIBER_SHOTGUN + max_ammo = 8 + masquerade_violating = FALSE + +/obj/item/ammo_box/magazine/darkpack/mk23 + name = "\improper automatic pistol magazine (.45 ACP)" + icon = 'modular_darkpack/modules/ert/icons/ammo.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + inhand_icon_state = null + icon_state = "mk23_mag" + ammo_type = /obj/item/ammo_casing/vampire/c45acp + caliber = CALIBER_45 + max_ammo = 12 + multiple_sprites = AMMO_BOX_FULL_EMPTY + +/obj/item/ammo_box/magazine/darkpack/mk23/silver + name = "automatic pistol magazine (.45 ACP Silver)" + ammo_type = /obj/item/ammo_casing/vampire/c45acp/silver + +/obj/item/ammo_box/magazine/darkpack/mk23/hp + name = "automatic pistol magazine (.45 ACP HP)" + ammo_type = /obj/item/ammo_casing/vampire/c45acp/HP + +/obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom + name = "\improper Mark 23 SOCOM Pistol" + desc = "A specialized .45 ACP Pistol featuring an integrated supressor and laser sight" + icon = 'modular_darkpack/modules/ert/icons/48x32weapons.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "mk23" + //onflooricon_state = "mk23" + inhand_icon_state = "mk23" + w_class = WEIGHT_CLASS_SMALL + accepted_magazine_type = /obj/item/ammo_box/magazine/darkpack/mk23 + burst_size = 1 + recoil = 0 + projectile_damage_multiplier = 1.3 + actions_types = list() + bolt_type = BOLT_TYPE_LOCKING + suppressed = SUPPRESSED_QUIET + can_suppress = FALSE + can_unsuppress = FALSE + fire_sound = 'modular_darkpack/modules/weapons/sounds/glock.ogg' //Doesnt matter when it's always using the supressed SFX + +/obj/item/gun/ballistic/automatic/darkpack/px66f //DO NOT DISTRIBUTE IN MAPPING + name = "\improper PX66F Rifle" + desc = "A three-round burst 5.56 death machine, with a Spiral brand below the barrel." + icon = 'modular_darkpack/modules/ert/icons/48x32weapons.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + worn_icon = 'modular_darkpack/modules/weapons/icons/worn_guns.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "px66f" + inhand_icon_state = "px66f" + worn_icon_state = "rifle" + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM //Bullpup makes it easy to fire with one hand, but we still don't want these dual-wielded + accepted_magazine_type = /obj/item/ammo_box/magazine/darkpack/px66f + burst_size = 3 + spread = 2 + recoil = 1.5 + bolt_type = BOLT_TYPE_LOCKING + show_bolt_icon = FALSE + mag_display = TRUE + can_suppress = FALSE + fire_sound = 'modular_darkpack/modules/ert/sounds/silenced_rifle.ogg' + masquerade_violating = TRUE + +/obj/item/gun/ballistic/automatic/darkpack/px66f/Initialize() + . = ..() + AddComponent(/datum/component/selling, 350, "aug", FALSE) + AddComponent(/datum/component/automatic_fire, 0.5 SECONDS) + +/obj/item/gun/ballistic/shotgun/darkpack/px12r //DONT DISTRIBUTE IN MAPPING + name = "\improper PX12R Breaching Shotgun" + desc = "A highly modified 12G Shotgun designed to fire Frag-12 explosive breaching rounds" + icon = 'modular_darkpack/modules/ert/icons/48x32weapons.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + worn_icon = 'modular_darkpack/modules/weapons/icons/worn_guns.dmi' + w_class = WEIGHT_CLASS_BULKY + weapon_weight = WEAPON_MEDIUM + worn_icon_state = "pomp" + icon_state = "px12r" + inhand_icon_state = "px12r" + recoil = 3 + fire_delay = 6 + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/px12r + can_be_sawn_off = FALSE + fire_sound = 'modular_darkpack/modules/ert/sounds/shotgun_firing.ogg' + load_sound = 'modular_darkpack/modules/ert/sounds/shell_load.ogg' + rack_sound = 'modular_darkpack/modules/ert/sounds/cycling.ogg' + inhand_x_dimension = 32 + inhand_y_dimension = 32 + + +/obj/item/gun/ballistic/automatic/l6_saw/darkpack + name = "\improper PX249F Light Machine Gun" + desc = "A modified M249 Machine Gun with an engraving of a Hydra on the grip" + icon = 'modular_darkpack/modules/ert/icons/48x32weapons.dmi' + lefthand_file = 'modular_darkpack/modules/ert/icons/righthand.dmi' + righthand_file = 'modular_darkpack/modules/ert/icons/lefthand.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/onfloor.dmi') + icon_state = "px249f" + inhand_icon_state = "px249f" + base_icon_state = "px249f" + w_class = WEIGHT_CLASS_HUGE + bolt_type = BOLT_TYPE_LOCKING + show_bolt_icon = FALSE + slot_flags = 0 + pin = /obj/item/firing_pin + accepted_magazine_type = /obj/item/ammo_box/magazine/darkpack/px249f + weapon_weight = WEAPON_HEAVY + burst_size = 1 + recoil = 6 //With good firearm skill it's not an issue + spread = 6 + fire_sound = 'modular_darkpack/modules/ert/sounds/m249fire.ogg' + rack_sound = 'modular_darkpack/modules/ert/sounds/m249rack.ogg' + +/obj/item/gun/ballistic/automatic/l6_saw/darkpack/update_icon_state() + . = ..() + if(item_flags & ACTIVE_WORLD_ICON) + return + inhand_icon_state = "[base_icon_state][magazine ? "mag":"nomag"]" + +/obj/item/gun/ballistic/automatic/l6_saw/darkpack/update_overlays() + . = ..() + if(item_flags & ACTIVE_WORLD_ICON) + return + . += "px249f_door_[cover_open ? "open" : "closed"]" + +/obj/item/gun/ballistic/automatic/l6_saw/darkpack/Initialize(mapload) + . = ..() + AddComponent(/datum/component/automatic_fire, 0.1 SECONDS) + +//------------Medical------------ +//To be done at a later date diff --git a/modular_darkpack/modules/ert/code/items/swat_items.dm b/modular_darkpack/modules/ert/code/items/swat_items.dm new file mode 100644 index 000000000000..8d3d906504ca --- /dev/null +++ b/modular_darkpack/modules/ert/code/items/swat_items.dm @@ -0,0 +1,43 @@ +/obj/item/storage/belt/police/swat + name = "swat belt" + desc = "Can hold SWAT gear like handcuffs." + icon_state = "security" + inhand_icon_state = "security" + worn_icon_state = "security" + content_overlays = TRUE + storage_type = /datum/storage/security_belt + +/obj/item/storage/belt/police/swat/full + +/obj/item/storage/belt/police/swat/full/PopulateContents() + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/melee/baton/vamp(src) + +/obj/item/card/swat + name = "Dogtags" + desc = "The dogtags of an elite law enforcement officer. It prints the officer's name in case they're captured or killed." + icon = 'modular_darkpack/modules/ert/icons/badges.dmi' + icon_state = "dogtags" + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/badges_onfloor.dmi') + worn_icon = 'modular_darkpack/modules/jobs/icons/id_worn.dmi' + worn_icon_state = "police_badge" + +/obj/item/card/lieutenant + name = "Officer Badge" + desc = "The shiny badge of an elite law enforcement officer. It shines with golden authority." + icon = 'modular_darkpack/modules/ert/icons/badges.dmi' + icon_state = "leader" + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/badges_onfloor.dmi') + worn_icon = 'modular_darkpack/modules/jobs/icons/id_worn.dmi' + worn_icon_state = "police_badge" + +/obj/item/card/first_aid + name = "First Aid Officer Card" + desc = "The professional laminated card of a field medic. Did you know it's a war crime to specifically target field medics?" + icon = 'modular_darkpack/modules/ert/icons/badges.dmi' + icon_state = "first_aid" + ONFLOOR_ICON_HELPER('modular_darkpack/modules/ert/icons/badges_onfloor.dmi') + worn_icon = 'modular_darkpack/modules/jobs/icons/id_worn.dmi' + worn_icon_state = "grey_id" diff --git a/modular_darkpack/modules/ert/code/loadout.dm b/modular_darkpack/modules/ert/code/loadout.dm new file mode 100644 index 000000000000..802de83f7004 --- /dev/null +++ b/modular_darkpack/modules/ert/code/loadout.dm @@ -0,0 +1,125 @@ +/datum/outfit/job/vampire/ert/pentex + name = "First Team Leader" + ears = /obj/item/radio/headset/darkpack/pmc + uniform = /obj/item/clothing/under/vampire/darkpack_ert/firstteam_uniform + gloves = /obj/item/clothing/gloves/vampire/darkpack_ert/firstteam + mask = /obj/item/clothing/mask/vampire/balaclava + glasses = /obj/item/clothing/glasses/night/thermal + r_pocket = /obj/item/flashlight + l_pocket = /obj/item/ammo_box/magazine/darkpack/px66f + shoes = /obj/item/clothing/shoes/vampire/darkpack_ert/firstteam + belt = /obj/item/gun/ballistic/automatic/darkpack/px66f + suit = /obj/item/clothing/suit/vampire/darkpack_ert/firstteam_armor + head = /obj/item/clothing/head/vampire/darkpack_ert/firstteam_helmet + back = /obj/item/storage/backpack/satchel + implants = list(/obj/item/implant/explosive) + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack/px66f = 3, + /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom=1, + /obj/item/ammo_box/magazine/darkpack/mk23/silver = 2, + /obj/item/ammo_box/darkpack/c556/bale = 1, + /obj/item/vamp/keys/pentex = 1, + /obj/item/grenade/frag = 3, + /obj/item/storage/medkit/darkpack/ifak = 1, + ) + +/datum/outfit/job/vampire/ert/pentex/exterminator + name = "First Team Exterminator" + belt = /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom + l_pocket = /obj/item/ammo_box/magazine/darkpack/px249f + r_hand = /obj/item/gun/ballistic/automatic/l6_saw/darkpack + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack/mk23/silver = 2, + /obj/item/ammo_box/darkpack/c556/bale = 3, + /obj/item/vamp/keys/pentex = 1, + /obj/item/grenade/frag = 3, + /obj/item/storage/medkit/darkpack/ifak = 1, + ) + +/datum/outfit/job/vampire/ert/pentex/specialist + name = "First Team Specialist" + belt = /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom + r_hand = /obj/item/gun/ballistic/shotgun/darkpack/px12r + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack/px66f = 3, + /obj/item/ammo_box/magazine/darkpack/mk23/silver = 2, + /obj/item/ammo_box/darkpack/f12g = 2, + /obj/item/vamp/keys/pentex = 1, + /obj/item/grenade/frag = 3, + /obj/item/storage/medkit/darkpack/ifak = 1, + ) + +/datum/outfit/job/vampire/ert/pentex/medic + name = "First Team Medic" + back = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack/px66f = 3, + /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom=1, + /obj/item/ammo_box/magazine/darkpack/mk23/silver = 2, + /obj/item/ammo_box/darkpack/c556/bale = 1, + /obj/item/vamp/keys/pentex = 1, + /obj/item/grenade/frag = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/storage/medkit/darkpack/combat = 1 + ) + +//=================BUDGET FIRST TEAM LOADOUTS================= + +/datum/outfit/job/vampire/ert/pentex_budget + name = "First Team Squad Leader" //the singular competent guy on the team + glasses = /obj/item/clothing/glasses/night/thermal + shoes = /obj/item/clothing/shoes/vampire/jackboots + uniform = /obj/item/clothing/under/vampire/pentex_suit + gloves = /obj/item/clothing/gloves/vampire/work + ears = /obj/item/radio/headset/darkpack/pmc + suit = /obj/item/clothing/suit/vampire/vest + head = /obj/item/clothing/head/vampire/pentex_beret + r_pocket = /obj/item/vamp/keys/pentex + mask = /obj/item/clothing/mask/gas/sechailer + belt = /obj/item/gun/ballistic/automatic/darkpack/mp7 + back = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom=1, + /obj/item/ammo_box/magazine/darkpack/mk23/silver = 2, + /obj/item/ammo_box/magazine/darkpack/c46pdw/ext = 4, + ) + +/datum/outfit/job/vampire/ert/pentex_budget/intern + name = "First Team Intern" //"I signed up to the cool add on TV and got thrown out to kill werewolves with a shotgun and silenced pistol" + uniform = /obj/item/clothing/under/vampire/pentex_longleeve + gloves = /obj/item/clothing/gloves/vampire/work + shoes = /obj/item/clothing/shoes/vampire + back = /obj/item/storage/backpack/satchel + glasses = /obj/item/clothing/glasses/sunglasses + head = /obj/item/clothing/head/vampire/pentex_yellowhardhat + r_hand = /obj/item/gun/ballistic/shotgun/vampire + back = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/storage/medkit/darkpack/standard = 1, + /obj/item/ammo_box/darkpack/c12g = 1, + /obj/item/ammo_box/darkpack/c12g/buck = 1, + /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom=1, + /obj/item/ammo_box/magazine/darkpack/mk23/hp = 2, + /obj/item/clipboard = 1, + ) + + +/datum/outfit/job/vampire/ert/pentex_budget/medic + name = "First Team Intern Medic" //"How the fuck am I supposed to heal anyone with this" + uniform = /obj/item/clothing/under/vampire/pentex_shortsleeve + gloves = /obj/item/clothing/gloves/vampire/work + shoes = /obj/item/clothing/shoes/vampire + back = /obj/item/storage/backpack/satchel + glasses = /obj/item/clothing/glasses/sunglasses + head = /obj/item/clothing/head/vampire/pentex_whitehardhat + belt = /obj/item/gun/ballistic/automatic/darkpack/huntrifle + back = /obj/item/storage/backpack/satchel + backpack_contents = list( + /obj/item/storage/medkit/darkpack/advanced = 1, + /obj/item/ammo_box/magazine/darkpack556/hunt = 3, + /obj/item/gun/ballistic/automatic/pistol/darkpack/mk23_socom=1, + /obj/item/ammo_box/magazine/darkpack/mk23/hp = 2, + /obj/item/clipboard = 1, + ) + diff --git a/modular_darkpack/modules/ert/code/national_guard.dm b/modular_darkpack/modules/ert/code/national_guard.dm new file mode 100644 index 000000000000..496dde9cc1a2 --- /dev/null +++ b/modular_darkpack/modules/ert/code/national_guard.dm @@ -0,0 +1,24 @@ +/datum/antagonist/ert/darkpack/national_guard/leader + name = "National Guard Sergeant" + outfit = /datum/outfit/job/vampire/ert/national_guard/sergeant + role = "National Guard Sergeant" + +/datum/antagonist/ert/darkpack/national_guard/medic + name = "National Guard Medic" + outfit = /datum/outfit/job/vampire/ert/national_guard/medic + role = "National Guard Medic" + +/datum/antagonist/ert/darkpack/national_guard/rifleman + name = "National Guard Rifleman" + outfit = /datum/outfit/job/vampire/ert/national_guard/rifleman + role = "National Guard Rifleman" + +/datum/antagonist/ert/darkpack/national_guard/explosives + name = "National Guard Bomb Squad" + outfit = /datum/outfit/job/vampire/ert/national_guard/explosives + role = "National Guard Bomb Squad" + +/datum/antagonist/ert/darkpack/national_guard/marksman + name = "National Guard Marksman" + outfit = /datum/outfit/job/vampire/ert/national_guard/marksman + role = "National Guard Marksman" diff --git a/modular_darkpack/modules/ert/code/outfits/national_guard_outfits.dm b/modular_darkpack/modules/ert/code/outfits/national_guard_outfits.dm new file mode 100644 index 000000000000..b4b477b8e68f --- /dev/null +++ b/modular_darkpack/modules/ert/code/outfits/national_guard_outfits.dm @@ -0,0 +1,104 @@ +/datum/outfit/job/vampire/ert/national_guard/sergeant + name = "National Guard Sergeant" + ears = /obj/item/radio/headset/darkpack/military + glasses = /obj/item/clothing/glasses/vampire/sun + uniform = /obj/item/clothing/under/vampire/military_fatigues + gloves = /obj/item/clothing/gloves/vampire/work + r_pocket = /obj/item/flashlight/seclite + l_pocket = /obj/item/vamp/keys/police/federal + shoes = /obj/item/clothing/shoes/vampire/jackboots + suit = /obj/item/clothing/suit/vampire/vest + id = /obj/item/card/lieutenant + r_hand = /obj/item/gun/ballistic/automatic/darkpack/autoshotgun + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpackautoshot = 1, + /obj/item/ammo_box/darkpack/c12g/buck = 1, + /obj/item/knife/vamp = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/megaphone = 1, + ) + +/datum/outfit/job/vampire/ert/national_guard/medic + name = "National Guard Medic" + ears = /obj/item/radio/headset/darkpack/military + glasses = /obj/item/clothing/glasses/vampire/perception + uniform = /obj/item/clothing/under/vampire/military_fatigues + gloves = /obj/item/clothing/gloves/vampire/latex + r_pocket = /obj/item/flashlight/seclite + l_pocket = /obj/item/vamp/keys/police/federal + shoes = /obj/item/clothing/shoes/vampire/jackboots + belt = /obj/item/defibrillator/compact/loaded + suit = /obj/item/clothing/suit/vampire/labcoat/director + head = /obj/item/clothing/head/beret + id = /obj/item/card/first_aid + r_hand = /obj/item/gun/ballistic/automatic/darkpack/mp5 + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack9mp5 = 1, + /obj/item/storage/medkit/darkpack/doctor = 1, + /obj/item/storage/medkit/darkpack/combat = 1, + /obj/item/storage/medkit/darkpack/burn = 1, + /obj/item/storage/medkit/darkpack/brute = 1, + ) + +/datum/outfit/job/vampire/ert/national_guard/rifleman + name = "National Guard Rifleman" + ears = /obj/item/radio/headset/darkpack/military + uniform = /obj/item/clothing/under/vampire/military_fatigues + mask = /obj/item/clothing/mask/vampire/balaclava + gloves = /obj/item/clothing/gloves/vampire/work + r_pocket = /obj/item/flashlight/seclite + l_pocket = /obj/item/ammo_box/magazine/darkpackaug + shoes = /obj/item/clothing/shoes/vampire/jackboots + belt = /obj/item/gun/ballistic/automatic/darkpack/aug + suit = /obj/item/clothing/suit/vampire/vest/army + head = /obj/item/clothing/head/vampire/army + id = /obj/item/card/swat + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpackaug = 1, + /obj/item/grenade/frag = 1, + /obj/item/grenade/flashbang = 1, + /obj/item/grenade/smokebomb = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + ) + +/datum/outfit/job/vampire/ert/national_guard/explosives + name = "National Guard Explosives Expert" + ears = /obj/item/radio/headset/darkpack/military + uniform = /obj/item/clothing/under/vampire/military_fatigues + gloves = /obj/item/clothing/gloves/vampire/work + r_pocket = /obj/item/lighter + shoes = /obj/item/clothing/shoes/vampire/jackboots + suit = /obj/item/clothing/suit/vampire/eod + head = /obj/item/clothing/head/vampire/eod + id = /obj/item/card/swat + backpack_contents = list( + /obj/item/grenade/frag = 1, + /obj/item/grenade/flashbang = 1, + /obj/item/grenade/smokebomb = 1, + /obj/item/molotov = 1, + /obj/item/gun/ballistic/revolver/grenadelauncher = 1, + /obj/item/gun/ballistic/automatic/darkpack/uzi = 1, + /obj/item/liquid_flamethrower = 1, + ) + +/datum/outfit/job/vampire/ert/national_guard/marksman + name = "National Guard Marksman" + ears = /obj/item/radio/headset/darkpack/military + glasses = /obj/item/clothing/glasses/vampire/sun + uniform = /obj/item/clothing/under/vampire/military_fatigues + gloves = /obj/item/clothing/gloves/vampire/work + r_pocket = /obj/item/flashlight/seclite + l_pocket = /obj/item/ammo_box/magazine/m9mm + shoes = /obj/item/clothing/shoes/vampire/jackboots + suit = /obj/item/clothing/suit/vampire/vest/army + head = /obj/item/clothing/head/beret + id = /obj/item/card/swat + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack556 = 1, + /obj/item/ammo_box/magazine/m9mm = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/binoculars = 1, + /obj/item/gun/ballistic/automatic/darkpack/autosniper = 1, + /obj/item/gun/ballistic/automatic/pistol/darkpack/beretta = 1, + ) + diff --git a/modular_darkpack/modules/ert/code/outfits/swat_outfits.dm b/modular_darkpack/modules/ert/code/outfits/swat_outfits.dm new file mode 100644 index 000000000000..34ca0575474f --- /dev/null +++ b/modular_darkpack/modules/ert/code/outfits/swat_outfits.dm @@ -0,0 +1,84 @@ +/datum/outfit/job/vampire/swat_rifleman + name = "Swat Rifleman" + ears = /obj/item/radio/headset/darkpack/police + uniform = /obj/item/clothing/under/vampire/police/utility + gloves = /obj/item/clothing/gloves/fingerless + l_pocket = /obj/item/vamp/keys/police + suit_store = /obj/item/flashlight/seclite + shoes = /obj/item/clothing/shoes/vampire/jackboots + belt = /obj/item/storage/belt/police/swat/full + suit = /obj/item/clothing/suit/vampire/vest + head = /obj/item/clothing/head/vampire/helmet + id = /obj/item/card/swat + r_hand = /obj/item/gun/ballistic/automatic/darkpack/ar15 + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack556 = 2, + /obj/item/grenade/frag = 1, + /obj/item/grenade/flashbang = 1, + /obj/item/grenade/smokebomb = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/restraints/handcuffs = 1, + ) + +/datum/outfit/job/vampire/swat_lieutenant + name = "Swat Lieutenant" + ears = /obj/item/radio/headset/darkpack/police + glasses = /obj/item/clothing/glasses/vampire/sun + uniform = /obj/item/clothing/under/vampire/police/utility + gloves = /obj/item/clothing/gloves/fingerless + l_pocket = /obj/item/vamp/keys/police/secure/captain + suit_store = /obj/item/flashlight/seclite + shoes = /obj/item/clothing/shoes/vampire/jackboots + belt = /obj/item/storage/belt/police/swat/full + suit = /obj/item/clothing/suit/vampire/trench/armored + head = /obj/item/clothing/head/hats/hos/beret + id = /obj/item/card/lieutenant + r_hand = /obj/item/gun/ballistic/automatic/pistol/darkpack/deagle/c50 + backpack_contents = list( + /obj/item/ammo_box/magazine/m50 = 2, + /obj/item/grenade/frag = 1, + /obj/item/grenade/flashbang = 1, + /obj/item/grenade/smokebomb = 1, + /obj/item/storage/medkit/darkpack/ifak = 1, + /obj/item/storage/box/handcuffs = 1, + ) + +/datum/outfit/job/vampire/swat_medic + name = "Swat Field Medic" + ears = /obj/item/radio/headset/darkpack/police + glasses = /obj/item/clothing/glasses/vampire/perception + uniform = /obj/item/clothing/under/vampire/police/utility + gloves = /obj/item/clothing/gloves/vampire/latex + l_pocket = /obj/item/vamp/keys/police + suit_store = /obj/item/flashlight/seclite + shoes = /obj/item/clothing/shoes/vampire/jackboots + belt = /obj/item/defibrillator/compact/loaded + suit = /obj/item/clothing/suit/vampire/labcoat + head = /obj/item/clothing/head/vampire/helmet + id = /obj/item/card/first_aid + r_hand = /obj/item/gun/ballistic/automatic/darkpack/mp5 + backpack_contents = list( + /obj/item/ammo_box/magazine/darkpack9mp5 = 2, + /obj/item/storage/medkit/darkpack/doctor = 1, + /obj/item/storage/medkit/darkpack/combat = 1, + /obj/item/storage/medkit/darkpack/burn = 1, + /obj/item/storage/medkit/darkpack/brute = 1, + ) + +/datum/outfit/job/vampire/swat_negotiator + name = "SWAT Negotiations Expert" + ears = /obj/item/radio/headset/darkpack/police + uniform = /obj/item/clothing/under/vampire/suit + l_pocket = /obj/item/vamp/keys/police + shoes = /obj/item/clothing/shoes/vampire + id = /obj/item/card/swat + r_hand = /obj/item/gun/ballistic/automatic/darkpack/sniper // for when negotiations go south. + backpack_contents = list( + /obj/item/clothing/under/vampire/suit/female = 1, + /obj/item/stack/dollar/thousand = 1, + /obj/item/ammo_box/darkpack/c50 = 1, + /obj/item/reagent_containers/cup/glass/coffee/vampire = 1, + /obj/item/food/cookie = 1, // cookies but no milk. these are gonna be some hard negotiations. + /obj/item/megaphone = 1, + /obj/item/flashlight/seclite = 1, + ) diff --git a/modular_darkpack/modules/ert/code/pentex_ert_roles.dm b/modular_darkpack/modules/ert/code/pentex_ert_roles.dm new file mode 100644 index 000000000000..02f7d972fb0d --- /dev/null +++ b/modular_darkpack/modules/ert/code/pentex_ert_roles.dm @@ -0,0 +1,34 @@ +/datum/antagonist/ert/darkpack/pentex/leader + name = "Squad Leader" + outfit = /datum/outfit/job/vampire/ert/pentex + role = "Sergeant" + +/datum/antagonist/ert/darkpack/pentex/medic + name = "Field Medic" + outfit = /datum/outfit/job/vampire/ert/pentex/medic + role = "Field Medic" + +/datum/antagonist/ert/darkpack/pentex/exterminator + name = "Exterminator" + outfit = /datum/outfit/job/vampire/ert/pentex/exterminator + role = "Exterminator" + +/datum/antagonist/ert/darkpack/pentex/specialist + name = "Breaching Specialist" + outfit = /datum/outfit/job/vampire/ert/pentex/specialist + role = "Specialist" + +/datum/antagonist/ert/darkpack/pentex/budget_leader + name = "Evaluation Officer" + outfit = /datum/outfit/job/vampire/ert/pentex_budget + role = "Evaluation Officer" + +/datum/antagonist/ert/darkpack/pentex/budget_intern + name = "Unpaid Intern" + outfit = /datum/outfit/job/vampire/ert/pentex_budget/intern + role = "Intern" + +/datum/antagonist/ert/darkpack/pentex/budget_medic + name = "Unpaid Intern Medic" + outfit = /datum/outfit/job/vampire/ert/pentex_budget/medic + role = "Medical Intern" diff --git a/modular_darkpack/modules/ert/code/swat.dm b/modular_darkpack/modules/ert/code/swat.dm new file mode 100644 index 000000000000..d48abfa10df5 --- /dev/null +++ b/modular_darkpack/modules/ert/code/swat.dm @@ -0,0 +1,19 @@ +/datum/antagonist/ert/darkpack/swat/leader + name = "SWAT Lieutenant" + outfit = /datum/outfit/job/vampire/swat_lieutenant + role = "SWAT Lieutenant" + +/datum/antagonist/ert/darkpack/swat/medic + name = "SWAT Field Medic" + outfit = /datum/outfit/job/vampire/swat_medic + role = "SWAT Field Medic" + +/datum/antagonist/ert/darkpack/swat/rifleman + name = "SWAT Rifleman" + outfit = /datum/outfit/job/vampire/swat_rifleman + role = "SWAT Rifleman" + +/datum/antagonist/ert/darkpack/swat/negotiations + name = "SWAT Negotiations Expert" + outfit = /datum/outfit/job/vampire/swat_negotiator + role = "SWAT Negotiations Expert" diff --git a/modular_darkpack/modules/ert/code/team.dm b/modular_darkpack/modules/ert/code/team.dm new file mode 100644 index 000000000000..97df12882279 --- /dev/null +++ b/modular_darkpack/modules/ert/code/team.dm @@ -0,0 +1,30 @@ +/datum/ert/darkpack + abstract_type = /datum/ert/darkpack + +/datum/ert/darkpack/swat + leader_role = /datum/antagonist/ert/darkpack/swat/leader + roles = list(/datum/antagonist/ert/darkpack/swat/medic, /datum/antagonist/ert/darkpack/swat/rifleman, /datum/antagonist/ert/darkpack/swat/negotiations) + rename_team = "SWAT Unit" + mission = "Assess the situation and assist the Police Department. Ensure that law and order is restored in the City." + polldesc = "the City's Special Weapons and Tactics Unit" + +/datum/ert/darkpack/national_guard + leader_role = /datum/antagonist/ert/darkpack/national_guard/leader + roles = list(/datum/antagonist/ert/darkpack/national_guard/medic, /datum/antagonist/ert/darkpack/national_guard/rifleman, /datum/antagonist/ert/darkpack/national_guard/explosives) + rename_team = "National Guard Platoon" + mission = "Stabilize the area. Establish a curfew. Disperse the riots. Ensure safety and restore order immediately - by any means." + polldesc = "the National Guard Emergency Response Unit" + +/datum/ert/darkpack/pentex + leader_role = /datum/antagonist/ert/darkpack/pentex/leader + roles = list(/datum/antagonist/ert/darkpack/pentex/medic, /datum/antagonist/ert/darkpack/pentex/exterminator, /datum/antagonist/ert/darkpack/pentex/specialist) + rename_team = "First Team" + mission = "Liquidate all hostile anomolous entities" + polldesc = "an elite FIRST Team" + +/datum/ert/darkpack/pentex/budget + leader_role = /datum/antagonist/ert/darkpack/pentex/budget_leader + roles = list(/datum/antagonist/ert/darkpack/pentex/budget_intern, /datum/antagonist/ert/darkpack/pentex/budget_medic) + rename_team = "First Team" //For when you need a death-squad on a budget + mission = "Remove all hostile anomolous entities" + polldesc = "an 'elite' FIRST Team" diff --git a/modular_darkpack/modules/ert/icons/48x32weapons.dmi b/modular_darkpack/modules/ert/icons/48x32weapons.dmi new file mode 100644 index 000000000000..e22880c27f1a Binary files /dev/null and b/modular_darkpack/modules/ert/icons/48x32weapons.dmi differ diff --git a/modular_darkpack/modules/ert/icons/ammo.dmi b/modular_darkpack/modules/ert/icons/ammo.dmi new file mode 100644 index 000000000000..f2bf90d8cdfc Binary files /dev/null and b/modular_darkpack/modules/ert/icons/ammo.dmi differ diff --git a/modular_darkpack/modules/ert/icons/badges.dmi b/modular_darkpack/modules/ert/icons/badges.dmi new file mode 100644 index 000000000000..dc6836811e5e Binary files /dev/null and b/modular_darkpack/modules/ert/icons/badges.dmi differ diff --git a/modular_darkpack/modules/ert/icons/badges_onfloor.dmi b/modular_darkpack/modules/ert/icons/badges_onfloor.dmi new file mode 100644 index 000000000000..f06fb56ec1f3 Binary files /dev/null and b/modular_darkpack/modules/ert/icons/badges_onfloor.dmi differ diff --git a/modular_darkpack/modules/ert/icons/clothing.dmi b/modular_darkpack/modules/ert/icons/clothing.dmi new file mode 100644 index 000000000000..13460812df4c Binary files /dev/null and b/modular_darkpack/modules/ert/icons/clothing.dmi differ diff --git a/modular_darkpack/modules/ert/icons/lefthand.dmi b/modular_darkpack/modules/ert/icons/lefthand.dmi new file mode 100644 index 000000000000..702f8a019b75 Binary files /dev/null and b/modular_darkpack/modules/ert/icons/lefthand.dmi differ diff --git a/modular_darkpack/modules/ert/icons/medical.dmi b/modular_darkpack/modules/ert/icons/medical.dmi new file mode 100644 index 000000000000..0451ab4a0e53 Binary files /dev/null and b/modular_darkpack/modules/ert/icons/medical.dmi differ diff --git a/modular_darkpack/modules/ert/icons/onfloor.dmi b/modular_darkpack/modules/ert/icons/onfloor.dmi new file mode 100644 index 000000000000..02f64283c486 Binary files /dev/null and b/modular_darkpack/modules/ert/icons/onfloor.dmi differ diff --git a/modular_darkpack/modules/ert/icons/righthand.dmi b/modular_darkpack/modules/ert/icons/righthand.dmi new file mode 100644 index 000000000000..8ba47e602e5e Binary files /dev/null and b/modular_darkpack/modules/ert/icons/righthand.dmi differ diff --git a/modular_darkpack/modules/ert/icons/worn.dmi b/modular_darkpack/modules/ert/icons/worn.dmi new file mode 100644 index 000000000000..fe186cde68a9 Binary files /dev/null and b/modular_darkpack/modules/ert/icons/worn.dmi differ diff --git a/modular_darkpack/modules/ert/sounds/balefire.ogg b/modular_darkpack/modules/ert/sounds/balefire.ogg new file mode 100644 index 000000000000..fc1611c22404 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/balefire.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/cycling.ogg b/modular_darkpack/modules/ert/sounds/cycling.ogg new file mode 100644 index 000000000000..16431fbf5399 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/cycling.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/m249fire.ogg b/modular_darkpack/modules/ert/sounds/m249fire.ogg new file mode 100644 index 000000000000..136ea4bdbc19 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/m249fire.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/m249rack.ogg b/modular_darkpack/modules/ert/sounds/m249rack.ogg new file mode 100644 index 000000000000..a0fa47a40da8 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/m249rack.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/shell_load.ogg b/modular_darkpack/modules/ert/sounds/shell_load.ogg new file mode 100644 index 000000000000..e157c90404f9 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/shell_load.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/shotgun_firing.ogg b/modular_darkpack/modules/ert/sounds/shotgun_firing.ogg new file mode 100644 index 000000000000..5db94de7de13 Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/shotgun_firing.ogg differ diff --git a/modular_darkpack/modules/ert/sounds/silenced_rifle.ogg b/modular_darkpack/modules/ert/sounds/silenced_rifle.ogg new file mode 100644 index 000000000000..473b22f78ded Binary files /dev/null and b/modular_darkpack/modules/ert/sounds/silenced_rifle.ogg differ diff --git a/modular_darkpack/modules/flavor_text/code/examine.dm b/modular_darkpack/modules/flavor_text/code/examine.dm index 73938bce8372..8cbcbb9ee47d 100644 --- a/modular_darkpack/modules/flavor_text/code/examine.dm +++ b/modular_darkpack/modules/flavor_text/code/examine.dm @@ -11,12 +11,6 @@ /datum/examine_panel/ui_state(mob/user) return GLOB.always_state -/datum/examine_panel/ui_close(mob/user) - // If this is an examine preview dummy, clean it up. - if(istype(holder, /mob/living/carbon/human/dummy)) - qdel(holder) - - /datum/examine_panel/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) @@ -77,15 +71,7 @@ return flavor_text_to_show -/mob/living/carbon/human/dummy/proc/setup_examine_preview(mob/living/carbon/user) - examine_panel_tgui.holder = user - /mob/living/carbon/Topic(href, href_list) if(href_list["view_flavortext"]) - // The examine preview dummy will be cleaned up once the user closes the TGUI window. - var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy() - if(!mannequin.examine_panel_tgui) - mannequin.examine_panel_tgui = new() - mannequin.setup_examine_preview(src) - mannequin.examine_panel_tgui.ui_interact(usr) - ..() + examine_panel_tgui.ui_interact(usr) + . = ..() diff --git a/modular_darkpack/modules/food/code/drinks.dm b/modular_darkpack/modules/food/code/drinks.dm index 91857b98641e..5990fb7c2667 100644 --- a/modular_darkpack/modules/food/code/drinks.dm +++ b/modular_darkpack/modules/food/code/drinks.dm @@ -30,7 +30,7 @@ icon_state = "beer_blue" list_reagents = list(/datum/reagent/consumable/ethanol/beer/light = 25, /datum/reagent/toxin/amatoxin = 5) -// DARKPACK TODO - Typhon's Beer +// DARKPACK TODO - (Typhon's Beer needs an audit of its handling. This looks ass.) /obj/item/reagent_containers/cup/glass/bottle/beer/vampire/typhon name = "Typhon's Beer" desc = "A sanguine drink to sate those of vampiric tastes" @@ -38,6 +38,10 @@ //foodtype = SANGUINE list_reagents = list(/datum/reagent/consumable/ethanol/beer/typhon = 30) +/obj/item/reagent_containers/cup/glass/bottle/beer/vampire/typhon/attack(mob/living/M, mob/user, def_zone) + . = ..() + reagents.trans_to(M, gulp_size, transferred_by = user) + /datum/reagent/consumable/ethanol/beer/typhon name = "Typhon's Beer" description = "An alcoholic beverage brewed with a sicekningly addictive sanguine taste" @@ -55,10 +59,6 @@ M.adjust_blood_pool(1) return ..() -/obj/item/reagent_containers/cup/glass/bottle/beer/vampire/typhon/attack(mob/living/M, mob/user, def_zone) - . = ..() - reagents.trans_to(M, gulp_size, transferred_by = user) - /obj/item/reagent_containers/cup/glass/vampirecola name = "two liter cola bottle" desc = "Coca cola espuma..." diff --git a/modular_darkpack/modules/food/code/recipes.dm b/modular_darkpack/modules/food/code/recipes.dm index 40134234447f..8e7c1df33edd 100644 --- a/modular_darkpack/modules/food/code/recipes.dm +++ b/modular_darkpack/modules/food/code/recipes.dm @@ -8,10 +8,9 @@ /datum/crafting_recipe/typhon_brew/is_recipe_available(mob/user) if(!ishuman(user)) return FALSE - //var/mob/living/carbon/human/H = user - /*for(var/datum/action/A in H.actions) DARKPACK TODO: Serpentis - if(istype(A, /datum/action/discipline/serpentis)) - return TRUE - */ - return FALSE + var/datum/splat/vampire/vampire = does_use_disciplines(user) + if(vampire?.get_discipline_power(/datum/discipline/serpentis)) + return TRUE + + return FALSE diff --git a/modular_darkpack/modules/forensics/code/forensic_gatherer.dm b/modular_darkpack/modules/forensics/code/forensic_gatherer.dm index 3420a7a4436d..a63b2e49357a 100644 --- a/modular_darkpack/modules/forensics/code/forensic_gatherer.dm +++ b/modular_darkpack/modules/forensics/code/forensic_gatherer.dm @@ -65,7 +65,7 @@ // Skill-check for scans var/mob/living/carbon/human/H = user - var/investigation_dice = H.st_get_stat(STAT_PERCEPTION) + H.st_get_stat(STAT_INVESTIGATION) + var/datum/storyteller_roll/investigation/investigate_roll = new() //Minium skill requirement to even use the thing if(H.st_get_stat(STAT_INVESTIGATION) <= 0) @@ -74,8 +74,9 @@ if(ishuman(scanned_atom)) var/mob/living/carbon/human/scanned_human = scanned_atom - var/investigation_roll = SSroll.storyteller_roll(investigation_dice, 3, H, src, TRUE) - if(!investigation_roll > 0) + investigate_roll.difficulty = 3 + var/investigation_roll = investigate_roll.st_roll(user, scanned_human) + if(investigation_roll != ROLL_SUCCESS) log_entry.add_data_entry(DETSCAN_CATEGORY_FINGERS, list("Improper fingerprints; try again.")) else if(!scanned_human.gloves) @@ -86,8 +87,9 @@ else if(!ismob(scanned_atom)) var/list/atom_fingerprints = GET_ATOM_FINGERPRINTS(scanned_atom) - var/investigation_roll = SSroll.storyteller_roll(investigation_dice, 5, H, src, TRUE) - if(!investigation_roll > 0) + investigate_roll.difficulty = 5 + var/investigation_roll = investigate_roll.st_roll(user, scanned_atom) + if(investigation_roll != ROLL_SUCCESS) log_entry.add_data_entry(DETSCAN_CATEGORY_FINGERS, list("Improper gathering; try again.")) else if(length(atom_fingerprints)) @@ -109,9 +111,10 @@ log_entry.add_data_entry(DETSCAN_CATEGORY_BLOOD, list(blood_DNA = blood_type)) if(istype(scanned_atom, /obj/item/ammo_casing)) - var/investigation_roll = SSroll.storyteller_roll(investigation_dice, 7, H, src, TRUE) + investigate_roll.difficulty = 7 + var/investigation_roll = investigate_roll.st_roll(user, scanned_atom) var/obj/item/ammo_casing/casing = scanned_atom - if(!investigation_roll > 0) + if(investigation_roll != ROLL_SUCCESS) log_entry.add_data_entry(DETSCAN_CATEGORY_MICROSTAMP, list("[casing.name] has an incomplete microstamp; you can't make it out.")) else if(casing.serial_type_index) @@ -121,8 +124,9 @@ if(istype(scanned_atom, /obj/item/card/id)) var/obj/item/card/id/user_id = scanned_atom - var/investigation_roll = SSroll.storyteller_roll(investigation_dice, 3, H, src, TRUE) - if(!investigation_roll > 0) + investigate_roll.difficulty = 3 + var/investigation_roll = investigate_roll.st_roll(user, scanned_atom) + if(investigation_roll != ROLL_SUCCESS) log_entry.add_data_entry(DETSCAN_CATEGORY_ACCESS, list("Improper gathering; try again.")) else for(var/region in DETSCAN_ACCESS_ORDER()) diff --git a/modular_darkpack/modules/frenzy/code/frenzy.dm b/modular_darkpack/modules/frenzy/code/frenzy.dm index 0c92e39d15a0..a40a5a9fcc7a 100644 --- a/modular_darkpack/modules/frenzy/code/frenzy.dm +++ b/modular_darkpack/modules/frenzy/code/frenzy.dm @@ -12,7 +12,7 @@ /mob/living/carbon/proc/rollfrenzy() if(client) - if(isgarou(src) || iswerewolf(src)) + if(isgarou(src)) to_chat(src, "I'm full of [span_danger("ANGER")], and I'm about to flare up in [span_danger("RAGE")]. Rolling...") else if(iskindred(src)) to_chat(src, "I need [span_danger("BLOOD")]. The [span_danger("BEAST")] is calling. Rolling...") diff --git a/modular_darkpack/modules/government/code/drivers_license.dm b/modular_darkpack/modules/government/code/drivers_license.dm index f25a6ab547ff..39bf001c543e 100644 --- a/modular_darkpack/modules/government/code/drivers_license.dm +++ b/modular_darkpack/modules/government/code/drivers_license.dm @@ -14,6 +14,7 @@ var/fake = FALSE /// If the NAME does not belong to the person. var/fake_identity = FALSE + var/datum/storyteller_roll/investigation/examine_roll /obj/item/card/drivers_license/Initialize(mapload) . = ..() @@ -43,8 +44,11 @@ /obj/item/card/drivers_license/examine(mob/user) . = ..() - //DARKPACK TODO - STATS - refer to passport.dm, this should be a statcheck to see if its an illegal ID/counterfeit + if(!examine_roll) + examine_roll = new() + examine_roll.reroll_cooldown = 1 SCENES + var/roll_result = examine_roll.st_roll(user, src) if(owner) . += span_notice("It reads as belonging to [owner], issued by the state of [issuing_state].") - if(fake) + if(fake && (roll_result == ROLL_SUCCESS)) . += span_notice("It looks like a crude counterfeit.") diff --git a/modular_darkpack/modules/government/code/passport.dm b/modular_darkpack/modules/government/code/passport.dm index dfac96c3be0f..37bcf6bf42b7 100644 --- a/modular_darkpack/modules/government/code/passport.dm +++ b/modular_darkpack/modules/government/code/passport.dm @@ -1,26 +1,3 @@ -/datum/quirk/illegal_identity - name = "Illegal Identity" - desc = "Illegal immigrant? Died legally? Born a wolf? The cops aren't happy." - value = 0 - quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_HIDE_FROM_SCAN - icon = FA_ICON_PERSON_CIRCLE_QUESTION - mob_trait = TRAIT_ILLEGAL_IDENTITY - gain_text = span_warning("You feel legally unprepared.") - lose_text = span_notice("You feel bureaucratically legitimate.") - medical_record_text = "Patient is not checked in with valid identification." - // excluded_clans = list(CLAN_RAVNOS) // DARKPACK TODO - RAVNOS - (They are forced to take this) - -/datum/quirk/illegal_identity/add() - . = ..() - if(!ishuman(quirk_holder)) - return - var/mob/living/carbon/human/criminal = quirk_holder - var/obj/item/passport/passport = locate() in criminal // In pockets - if(!passport && criminal.back) - passport = locate() in criminal.back // In backpack - if(passport && passport.owner == criminal.real_name) - passport.link_human(criminal) - /datum/loadout_item/pocket_items/passport name = "Identification" item_path = /obj/item/passport @@ -54,6 +31,7 @@ var/fake = FALSE /// If the NAME does not belong to the person. var/fake_identity = FALSE + var/datum/storyteller_roll/investigation/examine_roll /obj/item/passport/Initialize(mapload) . = ..() @@ -87,10 +65,13 @@ /obj/item/passport/examine(mob/user) . = ..() - // DARKPACK TODO - STATS - (Make this a perception+investigation roll when we have retrying check standeridization) + if(!examine_roll) + examine_roll = new() + examine_roll.reroll_cooldown = 1 SCENES + var/roll_result = examine_roll.st_roll(user, src) if(!closed && owner) . += span_notice("It reads as belonging to [owner] from [country_of_origin].") - if(fake) + if(fake && (roll_result == ROLL_SUCCESS)) . += span_notice("It looks like a crude counterfeit.") /obj/item/passport/attack_self(mob/user) diff --git a/modular_darkpack/modules/graveyard/code/graveyard.dm b/modular_darkpack/modules/graveyard/code/graveyard.dm index ceb8f8b997ec..3d28b8b02e32 100644 --- a/modular_darkpack/modules/graveyard/code/graveyard.dm +++ b/modular_darkpack/modules/graveyard/code/graveyard.dm @@ -84,7 +84,7 @@ // is there a nearby vampgate? for(var/obj/structure/vampgate/gate in oview(scan_range, zombie)) - if(!gate.gate_broken) + if(!gate.broken) // theres one, kill it controller.set_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET, gate) controller.clear_blackboard_key(BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) @@ -123,6 +123,7 @@ /obj/vampgrave/Initialize(mapload) . = ..() randomize_appearance() + spawn_interval += rand(-10 SECONDS, 10 SECONDS) // Prevent them from all spawning at the same time. addtimer(CALLBACK(src, PROC_REF(try_spawn_zombie)), spawn_interval, TIMER_STOPPABLE | TIMER_LOOP) //they have the indestructible flag so this should never happen but just in case @@ -167,48 +168,51 @@ icon_state += "-snow" /obj/vampgrave/proc/is_outdoors() - if(!istype(get_area(src), /area/vtm)) - return FALSE - var/area/vtm/V = get_area(src) - return V.outdoors + var/area/my_area = get_area(src) + return my_area.outdoors + +// Adminbus or testing +/obj/vampgrave/rapid + name = "upturned grave" + spawn_interval = 30 SECONDS + max_zombies_per_grave = 1 /obj/structure/vampgate - name = "Graveyard Gate" + name = "graveyard gate" desc = "It opens and closes." icon = 'modular_darkpack/modules/graveyard/icons/gate.dmi' icon_state = "gate" - pixel_w = -32 + pixel_x = -32 + base_pixel_x = -32 anchored = TRUE density = TRUE - opacity = FALSE - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF | INDESTRUCTIBLE max_integrity = 500 + prevent_destruction = TRUE var/repairing = FALSE - var/gate_broken = FALSE /obj/structure/vampgate/Initialize(mapload) . = ..() - atom_integrity = max_integrity + var/turf/right_turf = get_step(src, EAST) + var/turf/left_turf = get_step(src, WEST) + if(right_turf) + right_turf.set_density(TRUE) + if(left_turf) + left_turf.set_density(TRUE) /obj/structure/vampgate/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) - // dont take more damage if its already broken - if(gate_broken) - return - - // manually reduce integrity, if we dont do this and rely only on parent functions, it will qdel at < 0 integrity - atom_integrity = max(0, atom_integrity - damage_amount) - - if(sound_effect) - playsound(get_turf(src), 'modular_darkpack/master_files/sounds/effects/door/get_bent.ogg', 100, FALSE) + . = ..() + if(!broken) + if(sound_effect) + playsound(get_turf(src), 'modular_darkpack/master_files/sounds/effects/door/get_bent.ogg', 100, FALSE) - shake_gate() + shake_gate() - if(atom_integrity <= 0) - break_open() + if(atom_integrity <= 0) + break_open() /obj/structure/vampgate/atom_destruction(damage_flag) - .=..() + . = ..() break_open() /obj/structure/vampgate/proc/shake_gate() @@ -221,41 +225,20 @@ pixel_w = initial(pixel_w) /obj/structure/vampgate/proc/break_open() - if(gate_broken) + if(broken) return - gate_broken = TRUE + broken = TRUE density = FALSE icon_state = "gate-open" - atom_integrity = 0 visible_message(span_boldwarning("[src] breaks open!")) -/obj/structure/vampgate/examine(mob/user) - . = ..() - - if(gate_broken) - . += span_boldwarning("The gate is broken open!") - return - - var/health_percent = round(get_integrity_percentage() * 100) - - switch(health_percent) - if(0 to 25) - . += span_boldwarning("Integrity: [atom_integrity]/[max_integrity] - Critically damaged!") - if(26 to 50) - . += span_warning("Integrity: [atom_integrity]/[max_integrity] - Heavily damaged") - if(51 to 75) - . += span_notice("Integrity: [atom_integrity]/[max_integrity] - Moderately damaged") - if(76 to INFINITY) - . += span_notice("Integrity: [atom_integrity]/[max_integrity] - Good condition") - /obj/structure/vampgate/item_interaction(mob/living/user, obj/item/tool, list/modifiers) - . = ..() if(istype(tool, /obj/item/melee/vamp/tire)) attempt_repair(user) - return TRUE + return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/structure/vampgate/proc/attempt_repair(mob/living/user) if(repairing) @@ -271,8 +254,8 @@ if(do_after(user, 5 SECONDS, src)) repair_damage(50) - if(atom_integrity > 0 && gate_broken) - gate_broken = FALSE + if(atom_integrity > 0 && broken) + broken = FALSE density = TRUE icon_state = "gate" visible_message(span_notice("[src] is repaired and closed!")) diff --git a/modular_darkpack/modules/guestbook/code/adjective_preference.dm b/modular_darkpack/modules/guestbook/code/adjective_preference.dm index 183f81d93dbd..31fec0260dc4 100644 --- a/modular_darkpack/modules/guestbook/code/adjective_preference.dm +++ b/modular_darkpack/modules/guestbook/code/adjective_preference.dm @@ -1,5 +1,5 @@ /datum/preference/choiced/adjective_preference - category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL priority = PREFERENCE_PRIORITY_TABLETOP savefile_key = "adjective_preference" savefile_identifier = PREFERENCE_CHARACTER diff --git a/modular_darkpack/modules/guestbook/code/human_helpers.dm b/modular_darkpack/modules/guestbook/code/human_helpers.dm index da8d60536b37..193fd0198676 100644 --- a/modular_darkpack/modules/guestbook/code/human_helpers.dm +++ b/modular_darkpack/modules/guestbook/code/human_helpers.dm @@ -7,6 +7,9 @@ visible_gender = "Woman" else visible_gender = "Person" + if(dna?.species.visible_gender_override) + visible_gender = dna.species.visible_gender_override + return visible_gender /mob/living/carbon/human/proc/get_age() diff --git a/modular_darkpack/modules/jobs/code/clinic/doctor.dm b/modular_darkpack/modules/jobs/code/clinic/doctor.dm index ba3411275641..175e8f57871f 100644 --- a/modular_darkpack/modules/jobs/code/clinic/doctor.dm +++ b/modular_darkpack/modules/jobs/code/clinic/doctor.dm @@ -16,7 +16,7 @@ ) description = "Help your fellow kindred in all matters medicine related. Sell blood. Keep your human colleagues ignorant." - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_KINFOLK, SPLAT_NONE) allowed_clans = list(VAMPIRE_CLAN_DAUGHTERS_OF_CACOPHONY, VAMPIRE_CLAN_SALUBRI, VAMPIRE_CLAN_BAALI, VAMPIRE_CLAN_BRUJAH, VAMPIRE_CLAN_TREMERE, VAMPIRE_CLAN_VENTRUE, VAMPIRE_CLAN_NOSFERATU, VAMPIRE_CLAN_GANGREL, VAMPIRE_CLAN_CITY_GANGREL, VAMPIRE_CLAN_TOREADOR, VAMPIRE_CLAN_MALKAVIAN, VAMPIRE_CLAN_BANU_HAQIM, VAMPIRE_CLAN_GIOVANNI, VAMPIRE_CLAN_SETITE, VAMPIRE_CLAN_TZIMISCE, VAMPIRE_CLAN_LASOMBRA, VAMPIRE_CLAN_CAITIFF, VAMPIRE_CLAN_KIASYD) known_contacts = list("Clinic Director") diff --git a/modular_darkpack/modules/jobs/code/event/national_guard.dm b/modular_darkpack/modules/jobs/code/event/national_guard.dm deleted file mode 100644 index 30e1a66fe572..000000000000 --- a/modular_darkpack/modules/jobs/code/event/national_guard.dm +++ /dev/null @@ -1,217 +0,0 @@ -/datum/outfit/job/vampire/national_guard - name = "National Guard Soldier" - ears = /obj/item/radio/headset/darkpack/military - uniform = /obj/item/clothing/under/vampire/military_fatigues - mask = /obj/item/clothing/mask/vampire/balaclava - r_pocket = /obj/item/flashlight - l_pocket = /obj/item/ammo_box/magazine/darkpackaug - shoes = /obj/item/clothing/shoes/vampire/jackboots - belt = /obj/item/gun/ballistic/automatic/darkpack/aug - suit = /obj/item/clothing/suit/vampire/vest/army - head = /obj/item/clothing/head/vampire/army - backpack_contents = list( - /obj/item/ammo_box/magazine/darkpackaug = 3, - /obj/item/gun/ballistic/automatic/pistol/darkpack/beretta = 1 - ) - -/datum/antagonist/national_guard/proc/equip_national_guard() - var/list/landmarkslist = list() - for(var/obj/effect/landmark/start/S in GLOB.start_landmarks_list) - if(S.name == name) - landmarkslist += S - var/mob/living/carbon/human/H = new(pick(landmarkslist)) - H.equipOutfit(national_guard_outfit) - H.st_set_stat(5, STAT_STAMINA) - H.st_set_stat(5, STAT_LARCENY) - H.st_set_stat(4, STAT_STRENGTH) - H.ignores_warrant = TRUE - -/datum/antagonist/national_guard/proc/offer_loadout() - var/list/loadouts = list( - "Flamethrower", - "EOD", - "Medic", - "Sniper", - "Ammo Carrier" - ) - var/loadout_type = input(owner.current, "Choose your loadout:", "Loadout Selection") in loadouts - switch(loadout_type) - if("Flamethrower") - owner.current.put_in_r_hand(new /obj/item/liquid_flamethrower(owner.current)) - owner.current.put_in_l_hand(new /obj/item/gas_can/full(owner.current)) - if("EOD") - owner.current.put_in_r_hand(new /obj/item/clothing/suit/vampire/eod(owner.current)) - owner.current.put_in_l_hand(new /obj/item/clothing/head/vampire/eod(owner.current)) - if("Medic") - owner.current.put_in_r_hand(new /obj/item/storage/medkit/darkpack/combat(owner.current)) - if("Sniper") - owner.current.put_in_r_hand(new /obj/item/gun/ballistic/automatic/darkpack/sniper(owner.current)) - owner.current.put_in_l_hand(new /obj/item/ammo_box/darkpack/c556(owner.current)) - if("Ammo Carrier") - owner.current.put_in_r_hand(new /obj/item/ammo_box/darkpack/c556/incendiary(owner.current)) - owner.current.put_in_l_hand(new /obj/item/ammo_box/darkpack/c556/incendiary(owner.current)) - -/datum/antagonist/national_guard - name = "National Guard" - roundend_category = "national guard" - antagpanel_category = "National Guard" - pref_flag = ROLE_NATIONAL_GUARD - antag_hud_name = "synd" - antag_moodlet = /datum/mood_event/focused - show_to_ghosts = TRUE - var/always_new_team = FALSE - var/datum/team/national_guard/national_guard_team - var/national_guard_outfit = /datum/outfit/job/vampire/national_guard - var/custom_objective - -/datum/antagonist/national_guard/sergeant - name = "National Guard Sergeant" - always_new_team = TRUE - var/title - -/datum/antagonist/national_guard/on_gain() - var/mob/living/carbon/human/guard_character = owner.current - guard_character.randomize_human_appearance() - forge_objectives() - equip_national_guard() - give_alias() - offer_loadout() - return ..() - -/datum/antagonist/national_guard/on_removal() - ..() - to_chat(owner.current, span_userdanger("You are no longer in the National Guard!")) - -/datum/antagonist/national_guard/greet() - to_chat(owner.current, span_alertsyndie("You're in the national guard.")) - to_chat(owner, span_notice("You are a [national_guard_team ? national_guard_team.national_guard_name : "national guard"] soldier!")) - owner.announce_objectives() - - -/datum/antagonist/national_guard/proc/give_alias() - var/my_name = "Tyler" - var/list/military_ranks = list("Private", "Private First Class", "Specialist", "Corporal") - var/selected_rank = pick(military_ranks) - if(owner.current.gender == MALE) - my_name = pick(GLOB.first_names_male) - else - my_name = pick(GLOB.first_names_female) - var/my_surname = pick(GLOB.last_names) - owner.current.fully_replace_character_name(null,"[selected_rank] [my_name] [my_surname]") - -/datum/antagonist/national_guard/forge_objectives() - spawn(2 SECONDS) - if(national_guard_team) - objectives |= national_guard_team.objectives - -/datum/antagonist/national_guard/sergeant/give_alias() - var/my_name = "Tyler" - if(owner.current.gender == MALE) - my_name = pick(GLOB.first_names_male) - else - my_name = pick(GLOB.first_names_female) - var/my_surname = pick(GLOB.last_names) - owner.current.fully_replace_character_name(null,"Sergeant [my_name] [my_surname]") - -/datum/team/national_guard/antag_listing_name() - if(national_guard_name) - return "[national_guard_name] Soldiers" - else - return "Soldiers" - - -/datum/antagonist/national_guard/sergeant/greet() - to_chat(owner, "You are the leading sergeant for this mission. You are responsible for guiding your team's operation.") - to_chat(owner, "If you feel you are not up to this task, give your command to another soldier.") - owner.announce_objectives() - addtimer(CALLBACK(src, PROC_REF(national_guardteam_name_assign)), 1) - -/datum/antagonist/national_guard/sergeant/proc/national_guardteam_name_assign() - if(!national_guard_team) - return - national_guard_team.rename_team(ask_name()) - -/datum/antagonist/national_guard/sergeant/proc/ask_name() - var/randomname = pick(GLOB.last_names) - var/newname = stripped_input(owner.current,"You are the sergeant. Please choose a name for your team.", "Name change",randomname) - if (!newname) - newname = randomname - else - newname = reject_bad_name(newname) - if(!newname) - newname = randomname - -/datum/antagonist/national_guard/create_team(datum/team/national_guard/new_team) - if(!new_team) - if(!always_new_team) - for(var/datum/antagonist/national_guard/N in GLOB.antagonists) - if(!N.owner) - stack_trace("Antagonist datum without owner in GLOB.antagonists: [N]") - continue - national_guard_team = new /datum/team/national_guard - national_guard_team.update_objectives() - return - if(!istype(national_guard_team)) - stack_trace("Wrong team type passed to [type] initialization.") - national_guard_team = new_team - -/datum/antagonist/national_guard/admin_add(datum/mind/new_owner,mob/admin) - new_owner.assigned_role = ROLE_NATIONAL_GUARD - new_owner.add_antag_datum(src) - message_admins("[key_name_admin(admin)] has national guard'd [key_name_admin(new_owner)].") - log_admin("[key_name(admin)] has national guard'd [key_name(new_owner)].") - -/datum/team/national_guard/proc/rename_team(new_name) - national_guard_name = new_name - name = "[national_guard_name] Team" - -/datum/objective/national_guard - name = "national_guard" - explanation_text = "Follow the orders of your sergeant." - martyr_compatible = TRUE - -/proc/national_guard_name() - var/name = "" - - // Prefix - name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - - // Suffix - if (prob(80)) - name += " " - - // Full - if(prob(60)) - name += pick("Squad", "Team", "Unit", "Group", "Section", "Element", "Detachment") - // Broken - else - name += pick("-", "*", "") - name += "Ops" - -/datum/team/national_guard - var/national_guard_name - var/core_objective = /datum/objective/national_guard - member_name = "National Guard Operative" - var/memorized_code - var/list/team_discounts - var/obj/item/nuclear_challenge/war_button - -/datum/team/national_guard/New() - ..() - national_guard_name = national_guard_name() - -/datum/team/national_guard/proc/update_objectives() - if(core_objective) - var/datum/objective/O = new core_objective - O.team = src - objectives += O - -/datum/team/national_guard/roundend_report() - var/list/parts = list() - parts += span_header("[national_guard_name] Operatives:") - - var/text = "
[span_header("The national guard were:")]" - text += printplayerlist(members) - parts += text - - return "
[parts.Join("
")]
" diff --git a/modular_darkpack/modules/jobs/code/event/swat.dm b/modular_darkpack/modules/jobs/code/event/swat.dm deleted file mode 100644 index d5bc506a5abb..000000000000 --- a/modular_darkpack/modules/jobs/code/event/swat.dm +++ /dev/null @@ -1,196 +0,0 @@ -/datum/outfit/job/vampire/swat - name = "Swat Officer" - ears = /obj/item/radio/headset/darkpack/police - uniform = /obj/item/clothing/under/vampire/police/utility - mask = /obj/item/clothing/mask/vampire/balaclava - r_pocket = /obj/item/flashlight - l_pocket = /obj/item/vamp/keys/police - shoes = /obj/item/clothing/shoes/vampire/jackboots - belt = /obj/item/storage/belt/security/police/swat - suit = /obj/item/clothing/suit/vampire/vest/police - head = /obj/item/clothing/head/vampire/helmet - id = /obj/item/card/police - backpack_contents = list( - /obj/item/ammo_box/magazine/darkpack556 = 4, - /obj/item/storage/medkit/darkpack/ifak = 1, - /obj/item/vamp/keys/hack=2 - ) - r_hand = /obj/item/gun/ballistic/automatic/darkpack/ar15 - -/datum/antagonist/swat/proc/equip_swat() - var/list/landmarkslist = list() - for(var/obj/effect/landmark/start/S in GLOB.start_landmarks_list) - if(S.name == name) - landmarkslist += S - var/mob/living/carbon/human/H = new(pick(landmarkslist)) - H.equipOutfit(swat_outfit) - H.st_set_stat(5, STAT_LARCENY) - H.st_set_stat(4, STAT_STRENGTH) - H.ignores_warrant = TRUE - -/datum/antagonist/swat - name = "Swat Officer" - roundend_category = "Swat" - antagpanel_category = "Swat" - pref_flag = ROLE_SWAT - antag_hud_name = "synd" - antag_moodlet = /datum/mood_event/focused - show_to_ghosts = TRUE - var/always_new_team = FALSE - var/datum/team/swat/swat_team - var/swat_outfit = /datum/outfit/job/vampire/swat - var/custom_objective - -/datum/antagonist/swat/team_leader - name = "Swat Team Leader" - always_new_team = TRUE - var/title - -/datum/antagonist/swat/on_gain() - var/mob/living/carbon/human/swat_character = owner.current - swat_character.randomize_human_appearance() - forge_objectives() - equip_swat() - give_alias() - return ..() - -/datum/antagonist/swat/on_removal() - ..() - to_chat(owner.current,span_userdanger("You are no longer in the Special Weapons and Tactics squad!")) - -/datum/antagonist/swat/greet() - to_chat(owner.current, span_alertsyndie("You're in the Special Weapons and Tactics squad.")) - to_chat(owner, span_notice("You are a [swat_team ? swat_team.swat_name : "swat"] officer!")) - spawn(3 SECONDS) - owner.announce_objectives() - - -/datum/antagonist/swat/proc/give_alias() - var/my_name = "Tyler" - var/list/swat_ranks = list("Private", "Private First Class", "Lance Corporal", "Corporal") - var/selected_rank = pick(swat_ranks) - if(owner.current.gender == MALE) - my_name = pick(GLOB.first_names_male) - else - my_name = pick(GLOB.first_names_female) - var/my_surname = pick(GLOB.last_names) - owner.current.fully_replace_character_name(null,"[selected_rank] [my_name] [my_surname]") - -/datum/antagonist/swat/forge_objectives() - spawn(2 SECONDS) - if(swat_team) - objectives |= swat_team.objectives - -/datum/antagonist/swat/leader/give_alias() - var/my_name = "Tyler" - if(owner.current.gender == MALE) - my_name = pick(GLOB.first_names_male) - else - my_name = pick(GLOB.first_names_female) - var/my_surname = pick(GLOB.last_names) - owner.current.fully_replace_character_name(null,"Squad Leader [my_name] [my_surname]") - -/datum/team/swat/antag_listing_name() - if(swat_name) - return "[swat_name] Officers" - else - return "Officers" - - -/datum/antagonist/swat/leader/greet() - to_chat(owner, "You are the SWAT Officer in charge of this mission. You are responsible for guiding your team's operation.") - to_chat(owner, "If you feel you are not up to this task, give your command to another officer.") - spawn(3 SECONDS) - owner.announce_objectives() - addtimer(CALLBACK(src, PROC_REF(swatteam_name_assign)), 1) - -/datum/antagonist/swat/leader/proc/swatteam_name_assign() - if(!swat_team) - return - swat_team.rename_team(ask_name()) - -/datum/antagonist/swat/leader/proc/ask_name() - var/randomname = pick(GLOB.last_names) - var/newname = stripped_input(owner.current,"You are the squa leader. Please choose a name for your team.", "Name change",randomname) - if (!newname) - newname = randomname - else - newname = reject_bad_name(newname) - if(!newname) - newname = randomname - -/datum/antagonist/swat/create_team(datum/team/swat/new_team) - if(!new_team) - if(!always_new_team) - for(var/datum/antagonist/swat/N in GLOB.antagonists) - if(!N.owner) - stack_trace("Antagonist datum without owner in GLOB.antagonists: [N]") - continue - swat_team = new /datum/team/swat - swat_team.update_objectives() - return - if(!istype(swat_team)) - stack_trace("Wrong team type passed to [type] initialization.") - swat_team = new_team - -/datum/antagonist/swat/admin_add(datum/mind/new_owner,mob/admin) - new_owner.assigned_role = ROLE_SWAT - new_owner.add_antag_datum(src) - message_admins("[key_name_admin(admin)] has swat'd [key_name_admin(new_owner)].") - log_admin("[key_name(admin)] has swat'd [key_name(new_owner)].") - -/datum/team/swat/proc/rename_team(new_name) - swat_name = new_name - name = "[swat_name] Team" - -/proc/swat_name() - var/name = "" - - // Prefix - name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - - // Suffix - if (prob(80)) - name += " " - - // Full - if(prob(60)) - name += pick("Squad", "Team", "Unit", "Group", "Section", "Element", "Detachment") - // Broken - else - name += pick("-", "*", "") - name += "Ops" - - return name - -/datum/objective/swat - name = "swat" - explanation_text = "Follow the orders of your commander." - martyr_compatible = TRUE - -/datum/team/swat - var/swat_name - var/core_objective = /datum/objective/swat - member_name = "Swat Officer" - -/datum/team/swat/New() - ..() - swat_name = swat_name() - -/datum/team/swat/proc/update_objectives() - if(core_objective) - var/datum/objective/O = new core_objective - O.team = src - objectives += O - - -/datum/team/swat/roundend_report() - var/list/parts = list() - parts += span_header("[swat_name] Operatives:") - - var/text = "
[span_header("The SWAT were:")]" - text += printplayerlist(members) - parts += text - - return "
[parts.Join("
")]
" - diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm index 0f258c5a8cc8..cd42710a762d 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/citizen.dm @@ -10,7 +10,7 @@ display_order = JOB_DISPLAY_ORDER_CITIZEN department_for_prefs = /datum/job_department/citizen job_flags = CITY_JOB_FLAGS - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = SPLAT_ALL minimal_masquerade = 0 alt_titles = list( "Citizen", diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/club_worker.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/club_worker.dm index 1f4bd2e2b0fb..8b164064836d 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/club_worker.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/club_worker.dm @@ -22,7 +22,7 @@ "Club Attendant" ) - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_KINFOLK, SPLAT_NONE) description = "Offer strip club services. Some of your clientele may be... Unusual, but you are either addicted to vampire bites, or bribed to listen little and say even less." minimal_masquerade = 3 diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/janitor.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/janitor.dm index b62c79369605..6a438be49119 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/janitor.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/janitor.dm @@ -7,7 +7,7 @@ config_tag = "JANITOR" outfit = /datum/outfit/job/vampire/janitor job_flags = CITY_JOB_FLAGS - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = SPLAT_ALL department_for_prefs = /datum/job_department/city_services departments_list = list( /datum/job_department/city_services, diff --git a/modular_darkpack/modules/jobs/code/miscelllaneous/taxi.dm b/modular_darkpack/modules/jobs/code/miscelllaneous/taxi.dm index 1263de38b5c9..1c79a17dc28b 100644 --- a/modular_darkpack/modules/jobs/code/miscelllaneous/taxi.dm +++ b/modular_darkpack/modules/jobs/code/miscelllaneous/taxi.dm @@ -13,7 +13,7 @@ departments_list = list( /datum/job_department/city_services, ) - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = SPLAT_ALL description = "Drive people in the city." minimal_masquerade = 0 diff --git a/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm b/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm index f7a3b363d1a0..8866004027d0 100644 --- a/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm +++ b/modular_darkpack/modules/jobs/code/pentex/branch_lead.dm @@ -23,7 +23,7 @@ "Endron Operations Director" ) - allowed_splats = list(SPLAT_GAROU, SPLAT_KINDRED, SPLAT_NONE) + allowed_splats = SPLAT_ALL // allowed_tribes = list(TRIBE_WYRM, TRIBE_RONIN) allowed_clans = VAMPIRE_CLAN_ALL minimal_masquerade = 5 diff --git a/modular_darkpack/modules/jobs/code/pentex/employee.dm b/modular_darkpack/modules/jobs/code/pentex/employee.dm index e4d2e9f84813..bbaa327790b3 100644 --- a/modular_darkpack/modules/jobs/code/pentex/employee.dm +++ b/modular_darkpack/modules/jobs/code/pentex/employee.dm @@ -24,7 +24,7 @@ "Endron Labourer" ) - allowed_splats = list(SPLAT_GAROU, SPLAT_KINDRED, SPLAT_NONE) + allowed_splats = SPLAT_ALL // allowed_tribes = list(TRIBE_WYRM, TRIBE_RONIN) allowed_clans = VAMPIRE_CLAN_ALL minimal_masquerade = 3 diff --git a/modular_darkpack/modules/jobs/code/pentex/sec.dm b/modular_darkpack/modules/jobs/code/pentex/sec.dm index 1c47b7e99420..b4050511b35c 100644 --- a/modular_darkpack/modules/jobs/code/pentex/sec.dm +++ b/modular_darkpack/modules/jobs/code/pentex/sec.dm @@ -16,7 +16,7 @@ job_flags = CITY_JOB_FLAGS outfit = /datum/outfit/job/vampire/pentex_sec - allowed_splats = list(SPLAT_GAROU, SPLAT_KINDRED, SPLAT_NONE) + allowed_splats = SPLAT_ALL // allowed_tribes = list(TRIBE_WYRM, TRIBE_RONIN) allowed_clans = VAMPIRE_CLAN_ALL minimal_masquerade = 3 diff --git a/modular_darkpack/modules/jobs/code/police/dispatcher.dm b/modular_darkpack/modules/jobs/code/police/dispatcher.dm index f70beb09462a..acb7beffbd85 100644 --- a/modular_darkpack/modules/jobs/code/police/dispatcher.dm +++ b/modular_darkpack/modules/jobs/code/police/dispatcher.dm @@ -15,8 +15,8 @@ /datum/job_department/clinic, ) - allowed_splats = list(SPLAT_GHOUL, SPLAT_NONE) - splat_slots = list(SPLAT_GHOUL = 1) + allowed_splats = list(SPLAT_GHOUL, SPLAT_KINFOLK, SPLAT_NONE) + splat_slots = list(SPLAT_GHOUL = 1, SPLAT_KINFOLK = 1) description = "Report emergencies to the correct emergency service." minimal_masquerade = 0 diff --git a/modular_darkpack/modules/jobs/code/police/police.dm b/modular_darkpack/modules/jobs/code/police/police.dm index a4b46d3ed98a..83bf746e995a 100644 --- a/modular_darkpack/modules/jobs/code/police/police.dm +++ b/modular_darkpack/modules/jobs/code/police/police.dm @@ -20,8 +20,8 @@ "Senior Police Officer", ) - allowed_splats = list(SPLAT_GHOUL, SPLAT_NONE) - splat_slots = list(SPLAT_GHOUL = 2) + allowed_splats = list(SPLAT_GHOUL, SPLAT_KINFOLK, SPLAT_NONE) + splat_slots = list(SPLAT_GHOUL = 2, SPLAT_KINFOLK = 2) description = "Enforce the Law." minimal_masquerade = 0 diff --git a/modular_darkpack/modules/jobs/code/supply/supply_tech.dm b/modular_darkpack/modules/jobs/code/supply/supply_tech.dm index c9a0618cb838..be165fdae7e1 100644 --- a/modular_darkpack/modules/jobs/code/supply/supply_tech.dm +++ b/modular_darkpack/modules/jobs/code/supply/supply_tech.dm @@ -15,7 +15,7 @@ /datum/job_department/supply, ) - allowed_splats = list(SPLAT_KINDRED, SPLAT_GHOUL, SPLAT_NONE) + allowed_splats = SPLAT_ALL description = "You work at the warehouse, moving boxes and selling not-quite legal goods to anyone who has the money." minimal_masquerade = 0 diff --git a/modular_darkpack/modules/loadout/code/categories/heads.dm b/modular_darkpack/modules/loadout/code/categories/heads.dm index d299ef0353e4..b4070bf1806f 100644 --- a/modular_darkpack/modules/loadout/code/categories/heads.dm +++ b/modular_darkpack/modules/loadout/code/categories/heads.dm @@ -105,3 +105,6 @@ /datum/loadout_item/head/prayer_veil name = "Prayer Veil" item_path = /obj/item/clothing/head/vampire/prayer_veil + +/datum/loadout_item/head/beret + item_path = /obj/item/clothing/head/beret diff --git a/modular_darkpack/modules/loadout/code/categories/shoes.dm b/modular_darkpack/modules/loadout/code/categories/shoes.dm index c9b8777373c2..c857600e8dd3 100644 --- a/modular_darkpack/modules/loadout/code/categories/shoes.dm +++ b/modular_darkpack/modules/loadout/code/categories/shoes.dm @@ -53,3 +53,12 @@ /datum/loadout_item/shoes/metal_tipped_shoes name = "Shoes (Metal Tipped)" item_path = /obj/item/clothing/shoes/vampire/businesstip + +/datum/loadout_item/shoes/blackfur + item_path = /obj/item/clothing/shoes/vampire/blackfur + +/datum/loadout_item/shoes/brownfur + item_path = /obj/item/clothing/shoes/vampire/brownfur + +/datum/loadout_item/shoes/pumped + item_path = /obj/item/clothing/shoes/vampire/pumped diff --git a/modular_darkpack/modules/loadout/code/categories/suit.dm b/modular_darkpack/modules/loadout/code/categories/suit.dm index 2230f7af7601..b7e5f06cf70d 100644 --- a/modular_darkpack/modules/loadout/code/categories/suit.dm +++ b/modular_darkpack/modules/loadout/code/categories/suit.dm @@ -42,6 +42,9 @@ name = "Coat (Red)" item_path = /obj/item/clothing/suit/vampire/coat/winter/alt +/datum/loadout_item/suit/coat/leopardcoat + item_path = /obj/item/clothing/suit/vampire/coat/leopard + // Jackets /datum/loadout_item/suit/jacket abstract_type = /datum/loadout_item/suit/jacket @@ -86,6 +89,13 @@ name = "Trenchcoat (Burgundy)" item_path = /obj/item/clothing/suit/vampire/trench/archive +// Hoodies +/datum/loadout_item/suit/hoodie + item_path = /obj/item/clothing/suit/hooded/hoodie + +/datum/loadout_item/suit/hoodiezim + item_path = /obj/item/clothing/suit/hooded/hoodie/hoodie_pim + // Misc /datum/loadout_item/suit/kasaya name = "Kasaya" diff --git a/modular_darkpack/modules/loadout/code/categories/uniform.dm b/modular_darkpack/modules/loadout/code/categories/uniform.dm index 027d761190cd..df3ff9bea874 100644 --- a/modular_darkpack/modules/loadout/code/categories/uniform.dm +++ b/modular_darkpack/modules/loadout/code/categories/uniform.dm @@ -188,6 +188,19 @@ name = "Dress Shirt (Brown)" item_path = /obj/item/clothing/under/vampire/biker +//Scene +/datum/loadout_item/uniform/scenepink + item_path = /obj/item/clothing/under/vampire/scenepink + +/datum/loadout_item/uniform/scenemoody + item_path = /obj/item/clothing/under/vampire/scenemoody + +/datum/loadout_item/uniform/sceneleopard + item_path = /obj/item/clothing/under/vampire/sceneleopard + +/datum/loadout_item/uniform/scenezim + item_path = /obj/item/clothing/under/vampire/scenezim + //Other /datum/loadout_item/uniform/baron name = "Bartender Outfit" diff --git a/modular_darkpack/modules/masquerade/code/blood_hunt_skull.dm b/modular_darkpack/modules/masquerade/code/blood_hunt_skull.dm index a5e1b9110b5f..3eb00551dedd 100644 --- a/modular_darkpack/modules/masquerade/code/blood_hunt_skull.dm +++ b/modular_darkpack/modules/masquerade/code/blood_hunt_skull.dm @@ -2,6 +2,7 @@ name = "ominous skull" desc = "A stylized skull, made out of marble." icon = 'modular_darkpack/modules/masquerade/icons/blood_hunt_skull.dmi' + ONFLOOR_ICON_HELPER('modular_darkpack/modules/masquerade/icons/onfloor.dmi') icon_state = "skull" item_flags = NOBLUDGEON w_class = WEIGHT_CLASS_SMALL @@ -70,17 +71,17 @@ target.clear_blood_hunt() // This code is for reinforcing a player's masquerade. -/obj/item/blood_hunt/pre_attack(atom/A, mob/living/user, params) - if(!ishuman(A)) - return - if(!iskindred(A)) - return +/obj/item/blood_hunt/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!ishuman(interacting_with)) + return NONE + if(!iskindred(interacting_with)) + return ITEM_INTERACT_BLOCKING - to_chat(user, span_notice("You hold the [src] up to [A]...")) - if(!do_after(user, 10 SECONDS, A)) - return COMPONENT_CANCEL_ATTACK_CHAIN - if(SSmasquerade.masquerade_reinforce(src, A, MASQUERADE_REASON_PREFERENCES)) - to_chat(user, span_notice("You pardon [A]'s masquerade breach!")) - return COMPONENT_CANCEL_ATTACK_CHAIN - to_chat(user, span_notice("[A]'s masquerade breach isn't worthy enough to be pardoned!")) - return COMPONENT_CANCEL_ATTACK_CHAIN + to_chat(user, span_notice("You hold the [src] up to [interacting_with]...")) + if(!do_after(user, 10 SECONDS, interacting_with)) + return ITEM_INTERACT_BLOCKING + if(SSmasquerade.masquerade_reinforce(src, interacting_with, MASQUERADE_REASON_PREFERENCES)) + to_chat(user, span_notice("You pardon [interacting_with]'s masquerade breach!")) + return ITEM_INTERACT_SUCCESS + to_chat(user, span_notice("[interacting_with]'s masquerade breach isn't worthy enough to be pardoned!")) + return ITEM_INTERACT_BLOCKING diff --git a/modular_darkpack/modules/masquerade/code/components/violation_observer.dm b/modular_darkpack/modules/masquerade/code/components/violation_observer.dm index e131be0a0e52..f82593f8f98e 100644 --- a/modular_darkpack/modules/masquerade/code/components/violation_observer.dm +++ b/modular_darkpack/modules/masquerade/code/components/violation_observer.dm @@ -45,13 +45,18 @@ breached_players += player_breacher SSmasquerade.masquerade_breach(source, player_breacher, (isliving(source) ? MASQUERADE_REASON_NPC : MASQUERADE_REASON_OBJECT)) + return TRUE + /datum/component/violation_observer/proc/on_masquerade_violation_reinforced(atom/source, mob/living/player_breacher) SIGNAL_HANDLER - SEND_SIGNAL(source, COMSIG_MASQUERADE_HUD_DELETE, player_breacher) - SSmasquerade.masquerade_reinforce(source, player_breacher) - source.observe_masquerade_reinforce(player_breacher) - breached_players -= player_breacher + if(player_breacher in breached_players) + SEND_SIGNAL(source, COMSIG_MASQUERADE_HUD_DELETE, player_breacher) + SSmasquerade.masquerade_reinforce(source, player_breacher) + source.observe_masquerade_reinforce(player_breacher) + breached_players -= player_breacher + + return TRUE /datum/component/violation_observer/proc/on_death(atom/source) SIGNAL_HANDLER @@ -63,21 +68,18 @@ breached_players -= player_breacher /atom/proc/observe_masquerade_violation(player_breacher) - /* // DARKPACK TODO - GAROU - if(isgarou(player_breacher) || iswerewolf(player_breacher)) + do_alert_animation() + if(iswerewolfsplat(player_breacher)) to_chat(player_breacher, span_userdanger(span_bold("VEIL VIOLATION"))) - SEND_SOUND(player_breacher, sound('code/modules/wod13/sounds/veil_violation.ogg', volume = 75)) + playsound(player_breacher, 'modular_darkpack/modules/masquerade/sound/veil_violation.ogg', 50, FALSE, -5) return - */ playsound(player_breacher, 'modular_darkpack/modules/masquerade/sound/masquerade_violation.ogg', 50, FALSE, -5) to_chat(player_breacher, span_userdanger(span_bold("MASQUERADE VIOLATION"))) /atom/proc/observe_masquerade_reinforce(player_breacher) - /* // DARKPACK TODO - GAROU - if(isgarou(player_breacher) || iswerewolf(player_breacher)) - SEND_SOUND(player_breacher, sound('code/modules/wod13/sounds/humanity_gain.ogg', volume = 75)) - to_chat(player_breacher, span_big(span_boldnicegreen("VEIL REINFORCEED"))) + if(iswerewolfsplat(player_breacher)) + to_chat(player_breacher, span_big(span_boldnicegreen("VEIL REINFORCED"))) + playsound(player_breacher, 'modular_darkpack/modules/masquerade/sound/humanity_gain.ogg', 50, FALSE, -5) return - */ to_chat(player_breacher, span_big(span_boldnicegreen("MASQUERADE REINFORCED"))) playsound(player_breacher, 'modular_darkpack/modules/masquerade/sound/masquerade_reinforce.ogg', 50, FALSE, -5) diff --git a/modular_darkpack/modules/masquerade/code/masquerade_contract.dm b/modular_darkpack/modules/masquerade/code/masquerade_contract.dm index 9cc5010d2339..48c2106f68eb 100644 --- a/modular_darkpack/modules/masquerade/code/masquerade_contract.dm +++ b/modular_darkpack/modules/masquerade/code/masquerade_contract.dm @@ -13,9 +13,9 @@ fire = 100 acid = 100 -/obj/item/masquerade_contract/attack_self(mob/user) +/obj/item/masquerade_contract/attack_self(mob/user, modifiers) . = ..() - if(!iskindred(user) && !isghoul(user)) + if(!isvampiresplat(user)) return var/turf/current_location = get_turf(user) to_chat(user, "[span_bold("YOU")], [get_area_name(user)] X:[current_location.x] Y:[current_location.y] Z:[current_location.z]") @@ -42,10 +42,9 @@ armor_type = /datum/armor/masquerade_contract resistance_flags = FIRE_PROOF | ACID_PROOF -/* // DARKPACK TODO - GAROU -/obj/item/veil_contract/attack_self(mob/user) +/obj/item/veil_contract/attack_self(mob/user, modifiers) . = ..() - if(!isgarou(user)) + if(!iswerewolfsplat(user)) return var/turf/current_location = get_turf(user) to_chat(user, "[span_bold("YOU")], [get_area_name(user)] X:[current_location.x] Y:[current_location.y] Z:[current_location.z]") @@ -60,4 +59,3 @@ if(!GLOB.veil_breakers_list) to_chat(user, span_info("No available Veil breakers in city...")) -*/ diff --git a/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm b/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm index 57a0a15eb27a..962d799ef944 100644 --- a/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm +++ b/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm @@ -5,7 +5,10 @@ SUBSYSTEM_DEF(masquerade) var/masquerade_level = MASQUERADE_MAX_LEVEL var/list/masquerade_breachers var/static/regex/masquerade_breaching_phrase_regex + + // The round is soon to be declared ending. Scarey sounds during this. var/ending = FALSE + var/roundend_started = FALSE /datum/controller/subsystem/masquerade/Initialize() masquerade_breachers = new() @@ -67,21 +70,15 @@ SUBSYSTEM_DEF(masquerade) . = TRUE break if(player_breacher.masquerade_score == 5) //Doesn't matter if they weren't in one of these lists. - // DARKPACK TODO - GAROU - //GLOB.veil_breakers_list -= player_breacher + GLOB.veil_breakers_list -= player_breacher GLOB.masquerade_breakers_list -= player_breacher - /* // DARKPACK TODO - GAROU - if(isgarou(player_breacher) || iswerewolf(player_breacher)) - var/random_renown = pick("Honor","Wisdom","Glory") - switch(random_renown) - if("Honor") - player_breacher.adjust_renown("honor", -1, vessel = player_breacher) - if("Glory") - player_breacher.adjust_renown("glory", -1, vessel = player_breacher) - if("Wisdom") - player_breacher.adjust_renown("wisdom", -1, vessel = player_breacher) + /* + var/datum/splat/werewolf/werewolf_splat = iswerewolfsplat(player_breacher) + if(istype(werewolf_splat)) + werewolf_splat.adjust_renown(pick(RENOWN_HONOR, RENOWN_GLORY, RENOWN_WISDOM), 1) */ + save_persistent_masquerade(player_breacher) return . @@ -99,14 +96,20 @@ SUBSYSTEM_DEF(masquerade) return player_breacher.masquerade_score = max(0, player_breacher.masquerade_score - 1) masquerade_breachers += list(list(player_breacher, source, reason)) - // DARKPACK TODO - GAROU - //if(isgarou(player_breacher) || iswerewolf(player_breacher)) - // GLOB.veil_breakers_list |= player_breacher - //else - GLOB.masquerade_breakers_list |= player_breacher + if(isvampiresplat(player_breacher)) + GLOB.masquerade_breakers_list |= player_breacher + else if(iswerewolfsplat(player_breacher)) + GLOB.veil_breakers_list |= player_breacher //Only lower the global masq if the player's breach score is actually reduced by 1 if(pre_breach_score > player_breacher.masquerade_score) masquerade_level = max(0, masquerade_level - 1) + + /* + var/datum/splat/werewolf/werewolf_splat = iswerewolfsplat(player_breacher) + if(istype(werewolf_splat)) + werewolf_splat.adjust_renown(pick(RENOWN_HONOR, RENOWN_GLORY, RENOWN_WISDOM), -1) + */ + save_persistent_masquerade(player_breacher) check_roundend_condition() @@ -117,39 +120,32 @@ SUBSYSTEM_DEF(masquerade) // Save the player's masquerade level to their character sheet. /datum/controller/subsystem/masquerade/proc/save_persistent_masquerade(mob/living/player_breacher) - var/datum/preferences/preferences = GLOB.preferences_datums[ckey(player_breacher.key)] - if(preferences) - preferences.write_preference_midround(GLOB.preference_entries[/datum/preference/numeric/masquerade], player_breacher.masquerade_score) - preferences.save_character() + var/mob/living/carbon/human/human_breacher = player_breacher + if(!istype(human_breacher)) + return + human_breacher.write_preference_midround(/datum/preference/numeric/masquerade, player_breacher.masquerade_score) // This is for clearing the round's masquerade because a player matrix'd -/datum/controller/subsystem/masquerade/proc/cryo_masquerade_breacher(mob/living/player_breacher, update_preferences) +/datum/controller/subsystem/masquerade/proc/matrix_masquerade_breacher(mob/living/player_breacher, update_preferences) for(var/masquerade_breach as anything in masquerade_breachers) if((player_breacher in masquerade_breach)) masquerade_breachers -= list(masquerade_breach) masquerade_level = min(MASQUERADE_MAX_LEVEL, masquerade_level + 1) - // DARKPACK TODO - GAROU - //if(isgarou(player_breacher) || iswerewolf(player_breacher)) - // GLOB.veil_breakers_list -= player_breacher - //else GLOB.masquerade_breakers_list -= player_breacher + GLOB.veil_breakers_list -= player_breacher if(update_preferences) save_persistent_masquerade(player_breacher) // This is for checking if a joined player should be on the breachers list. /datum/controller/subsystem/masquerade/proc/masquerade_breacher_check(mob/living/player_breacher) if(player_breacher.masquerade_score < 5) - // DARKPACK TODO - GAROU - //if(isgarou(player_breacher) || iswerewolf(player_breacher)) - // GLOB.veil_breakers_list |= player_breacher - //else - GLOB.masquerade_breakers_list |= player_breacher + if(isvampiresplat(player_breacher)) + GLOB.masquerade_breakers_list |= player_breacher + else if(iswerewolfsplat(player_breacher)) + GLOB.veil_breakers_list |= player_breacher else - // DARKPACK TODO - GAROU - //if(isgarou(player_breacher) || iswerewolf(player_breacher)) - // GLOB.veil_breakers_list -= player_breacher - //else GLOB.masquerade_breakers_list -= player_breacher + GLOB.veil_breakers_list -= player_breacher /datum/controller/subsystem/masquerade/proc/player_masquerade_reinforce(datum/source, mob/living/player_breacher) SIGNAL_HANDLER @@ -184,10 +180,6 @@ SUBSYSTEM_DEF(masquerade) else var/atom/object = masquerade_breach_list[2] SEND_SIGNAL(object, COMSIG_ALL_MASQUERADE_REINFORCE) - SSticker.force_ending = 1 - SSticker.current_state = GAME_STATE_FINISHED + GLOB.canon_event = FALSE - toggle_ooc(TRUE) // Turn it on - toggle_dooc(TRUE) - SSticker.declare_completion(SSticker.force_ending) - Master.SetRunLevel(RUNLEVEL_POSTGAME) + roundend_started = TRUE diff --git a/modular_darkpack/modules/masquerade/icons/onfloor.dmi b/modular_darkpack/modules/masquerade/icons/onfloor.dmi index 569d6c68db66..18212b14a75a 100644 Binary files a/modular_darkpack/modules/masquerade/icons/onfloor.dmi and b/modular_darkpack/modules/masquerade/icons/onfloor.dmi differ diff --git a/modular_darkpack/modules/masquerade/sound/humanity_gain.ogg b/modular_darkpack/modules/masquerade/sound/humanity_gain.ogg new file mode 100644 index 000000000000..7e11eef24cd9 Binary files /dev/null and b/modular_darkpack/modules/masquerade/sound/humanity_gain.ogg differ diff --git a/modular_darkpack/modules/masquerade/sound/veil_violation.ogg b/modular_darkpack/modules/masquerade/sound/veil_violation.ogg new file mode 100644 index 000000000000..b859541f6df9 Binary files /dev/null and b/modular_darkpack/modules/masquerade/sound/veil_violation.ogg differ diff --git a/modular_darkpack/modules/matrix/code/matrix.dm b/modular_darkpack/modules/matrix/code/matrix.dm index 0107ffbe8746..fb5deb642aa8 100644 --- a/modular_darkpack/modules/matrix/code/matrix.dm +++ b/modular_darkpack/modules/matrix/code/matrix.dm @@ -50,11 +50,14 @@ ADMIN_VERB_AND_CONTEXT_MENU(matrix_mob_verb, R_ADMIN, "Matrix Mob", "Matrix (des message_admins("[ADMIN_LOOKUP(despawning_mob)] has exited through the matrix.") log_game("[despawning_mob] has exited through the matrix.") + GLOB.manifest.remove(despawning_mob.real_name) + SSjob.FreeRole(despawning_mob) GLOB.joined_player_list -= despawning_mob.ckey //handle_objectives() + SSmasquerade.matrix_masquerade_breacher(despawning_mob, TRUE) despawning_mob.ghostize(FALSE) QDEL_NULL(despawning_mob) diff --git a/modular_darkpack/modules/merits_flaws/code/_darkpack_quirk.dm b/modular_darkpack/modules/merits_flaws/code/_darkpack_quirk.dm new file mode 100644 index 000000000000..18680a0b25b2 --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/code/_darkpack_quirk.dm @@ -0,0 +1,71 @@ +/datum/quirk/darkpack + abstract_type = /datum/quirk/darkpack + /// List of splats (vampire clans/types) this quirk is allowed for. Null = all allowed + var/list/allowed_splats + /// List of splats this quirk is explicitly forbidden for + var/list/forbidden_splats + /// Excluded clans from this quirk (exclusive to vampire) + var/list/excluded_clans + /// Minimum Generation + var/minimum_generation + /// Unique failure message on joining the round (should probably just mix the clan and generation blocking into the tgui...) + var/failure_message = "One of the quirks you've selected hasn't applied - your character is ineligible to use it!" + +/datum/quirk/darkpack/add_to_holder(mob/living/new_holder, quirk_transfer = FALSE, client/client_source, unique = TRUE, announce = TRUE) + if(forbidden_splats) + for(var/datum/splat/splat as anything in new_holder.splats) + if(splat.id in forbidden_splats) + return FALSE + + if(allowed_splats) + var/has_allowed_splat = FALSE + for(var/datum/splat/splat as anything in new_holder.splats) + if(splat.id in allowed_splats) + has_allowed_splat = TRUE + break + if(!has_allowed_splat) + return FALSE + + if(excluded_clans && iskindred(new_holder)) + var/datum/splat/vampire/kindred/kindred_splat = iskindred(new_holder) + if(kindred_splat.clan && (kindred_splat.clan.id in excluded_clans)) + to_chat(new_holder, span_warning("[failure_message]")) + return FALSE + + if(minimum_generation) + var/datum/splat/vampire/kindred/kindred_splat = iskindred(new_holder) + if(kindred_splat.generation < minimum_generation) + to_chat(new_holder, span_warning("[failure_message]")) + return FALSE + return ..() + +/datum/quirk/darkpack/is_splat_appropriate(datum/splat/mob_splat) + if(!..()) + return FALSE + + if(!forbidden_splats && !allowed_splats && !excluded_clans) + return TRUE + + var/datum/splat/splat_path = GLOB.splat_prototypes[mob_splat] + var/splat_id = splat_path?.id + + if(forbidden_splats && (splat_id in forbidden_splats)) + return FALSE + + if(allowed_splats && !(splat_id in allowed_splats)) + return FALSE + + return TRUE + +/datum/quirk/darkpack/proc/is_clan_appropriate(datum/vampire_clan/clan) + if(!excluded_clans) + return TRUE + + if(!clan) + return TRUE + + if(clan.id in excluded_clans) + return FALSE + + return TRUE + diff --git a/modular_darkpack/modules/merits_flaws/code/illegal_identity.dm b/modular_darkpack/modules/merits_flaws/code/illegal_identity.dm new file mode 100644 index 000000000000..07688d52bdc5 --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/code/illegal_identity.dm @@ -0,0 +1,29 @@ +/datum/quirk/darkpack/illegal_identity + name = "Illegal Identity" + desc = "Illegal immigrant? Died legally? Born a wolf? The cops aren't happy." + value = 0 + quirk_flags = QUIRK_HUMAN_ONLY|QUIRK_HIDE_FROM_SCAN + icon = FA_ICON_PERSON_CIRCLE_QUESTION + mob_trait = TRAIT_ILLEGAL_IDENTITY + gain_text = span_warning("You feel legally unprepared.") + lose_text = span_notice("You feel bureaucratically legitimate.") + medical_record_text = "Patient is not checked in with valid identification." + //excluded_clans = list(VAMPIRE_CLAN_RAVNOS) // They are forced to take this + failure_message = "Oh, there's my actual ID, looks like I misplaced it..." + +/datum/quirk/darkpack/illegal_identity/add() + . = ..() + if(!ishuman(quirk_holder)) + return + var/mob/living/carbon/human/criminal = quirk_holder + var/obj/item/passport/passport = locate() in criminal // In pockets + if(!passport && criminal.back) + passport = locate() in criminal.back // In backpack + if(passport && passport.owner == criminal.real_name) + passport.link_human(criminal) + //drivers license too + var/obj/item/card/drivers_license/license = locate() in criminal // In pockets + if(!license && criminal.back) + license = locate() in criminal.back // In backpack + if(license) + license.link_human(criminal) diff --git a/modular_darkpack/modules/merits_flaws/code/permanent_fangs.dm b/modular_darkpack/modules/merits_flaws/code/permanent_fangs.dm new file mode 100644 index 000000000000..8ee8a3d81f9a --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/code/permanent_fangs.dm @@ -0,0 +1,10 @@ +/datum/quirk/darkpack/permafangs + name = "Permanent Fangs" + desc = "Your fangs do not retract, making it impossible for you to hide your true nature. While some mortals may think you've had your teeth filed or are wearing prosthetics, sooner or later you're going to run into someone who knows what you truly are." + value = -1 + mob_trait = TRAIT_PERMAFANGS + gain_text = span_notice("Your fangs become stuck.") + lose_text = span_notice("You feel your fangs retract again.") + allowed_splats = list(SPLAT_KINDRED) + icon = FA_ICON_TEETH + failure_message = "You feel your fangs retract." diff --git a/modular_darkpack/modules/merits_flaws/merits_flaws.md b/modular_darkpack/modules/merits_flaws/merits_flaws.md new file mode 100644 index 000000000000..6b5a1d0100bf --- /dev/null +++ b/modular_darkpack/modules/merits_flaws/merits_flaws.md @@ -0,0 +1,28 @@ +## + +Module ID: MERITS_FLAWS + +### Description: + +This module implements Darkpack merits/flaws, allowing users to select negative quirks for extra freebie points to allocate on the stat sheet, or positive quirks to diminish their freebie points on the statsheet. Users cannot go below zero freebie points. + +### TG Proc/File Changes: + +- `tgui/packages/tgui/interfaces/PreferencesMenu/CharacterPreferences/QuirksPage.tsx` +- `code/modules/client/preferences/middleware/quirks.dm` -- adds two procs, `get_freebie_points()`, `get_quirk_balance()` + +### Modular Overrides: + +- N/A + +### Defines: + +- N/A + +### Included files that are not contained in this module: + +- N/A + +### Credits: + +chazzyjazzy diff --git a/modular_darkpack/modules/npc/code/human/__npc.dm b/modular_darkpack/modules/npc/code/human/__npc.dm index 1a1d4b4d3b1e..a61b86d0f544 100644 --- a/modular_darkpack/modules/npc/code/human/__npc.dm +++ b/modular_darkpack/modules/npc/code/human/__npc.dm @@ -17,8 +17,6 @@ /// my_backup_weapon = type_path /// This only determines my_weapon, you set my_backup_weapon yourself /// The last entry in the list for a type of NPC should always have 100 as the index - // DARKPACK TODO - reimplement weapons - /* var/static/list/role_weapons_chances = list( BANDIT_TYPE_NPC = list( /obj/item/gun/ballistic/automatic/pistol/darkpack/deagle = 33, @@ -30,7 +28,6 @@ /obj/item/gun/ballistic/automatic/darkpack/ar15 = 100, ) ) - */ var/datum/socialrole/socialrole var/is_talking = FALSE @@ -98,14 +95,11 @@ return INITIALIZE_HINT_LATELOAD /mob/living/carbon/human/npc/LateInitialize(mapload) - // DARKPACK TODO - reimplement weapons - /* if (role_weapons_chances.Find(type)) for(var/weapon in role_weapons_chances[type]) if(prob(role_weapons_chances[type][weapon])) my_weapon = new weapon(src) break - */ if (!my_weapon && my_weapon_type) my_weapon = new my_weapon_type(src) diff --git a/modular_darkpack/modules/npc/code/human/npc_types/bouncers/__bouncer.dm b/modular_darkpack/modules/npc/code/human/npc_types/bouncers/__bouncer.dm index ab7e28d2e9df..185d272f7237 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/bouncers/__bouncer.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/bouncers/__bouncer.dm @@ -149,11 +149,13 @@ /mob/living/carbon/human/npc/bouncer/examine(mob/user) - .=..() + . = ..() if(can_be_reasoned_with() && in_range(src, user)) var/list/interact_options = list( - "Persuade for Entry" = image(icon = 'icons/obj/toys/dice.dmi', icon_state = "d10")) + "Persuade for Entry" = image(icon = 'icons/obj/toys/dice.dmi', icon_state = "d10"), + "Intimidate for Entry" = image(icon = 'icons/obj/toys/dice.dmi', icon_state = "d10") + ) var/obj/item/held_item = user.get_active_held_item() if(held_item && istype(held_item, /obj/item/card/police)) @@ -162,7 +164,10 @@ switch(picked_option) if("Persuade for Entry") to_chat(user, span_notice("You try to talk your way through.")) - linked_perm.notify_barrier_social_bypass(user, src) + linked_perm.notify_barrier_social_bypass(user, src, FALSE, STAT_EMPATHY) + if("Intimidate for Entry") + to_chat(user, span_notice("You try to talk your way through.")) + linked_perm.notify_barrier_social_bypass(user, src, FALSE, STAT_INTIMIDATION) if("Show Badge") to_chat(user, span_notice("You flash your [held_item] as you try to talk your way through.")) linked_perm.notify_barrier_social_bypass(user, src, TRUE) diff --git a/modular_darkpack/modules/npc/code/human/npc_types/police.dm b/modular_darkpack/modules/npc/code/human/npc_types/police.dm index 1ecb69805af0..1bb98623efd0 100644 --- a/modular_darkpack/modules/npc/code/human/npc_types/police.dm +++ b/modular_darkpack/modules/npc/code/human/npc_types/police.dm @@ -25,7 +25,7 @@ Aggro(H, FALSE) -// DARKPACK TODO: create a police npc that just stands there +// There was a todo here for creating a npc that stands still. But it seems like we did implement that behavoir /mob/living/carbon/human/npc/police/static // fights_anyway = TRUE staying = TRUE diff --git a/modular_darkpack/modules/npc/code/nonhuman/friendly/bird.dm b/modular_darkpack/modules/npc/code/nonhuman/friendly/bird.dm index 3799b65922e7..e0f7a6715b46 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/friendly/bird.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/friendly/bird.dm @@ -1,3 +1,4 @@ +// DARKPACK TODO - CORAX - (Corax kinfolk and thus should be grouped into WTA soon.) /mob/living/basic/corvid name = "corvid" desc = "Caw." @@ -6,7 +7,7 @@ icon_state = "black" icon_living = "black" icon_dead = "black" - icon = 'modular_darkpack/modules/npc/icons/corvid.dmi' + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/corvid.dmi' density = FALSE butcher_results = list(/obj/item/food/meat/slab/chicken = 1) response_help_continuous = "pets" @@ -103,6 +104,7 @@ message = "caws!" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE vary = TRUE + // DARKPACK TODO - CORAX - (Move to wta folder) sound = 'modular_darkpack/modules/npc/sound/caw.ogg' /mob/living/basic/corvid/crow diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm index 9a2f9d647770..2483c56101f0 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/abyss_tentacle.dm @@ -1,5 +1,5 @@ -// Global list to track mobs grabbed by any tentacle -var/global/list/global_tentacle_grabs = list() +/// Global list to track mobs grabbed by any tentacle +GLOBAL_LIST_EMPTY(global_tentacle_grabs) /mob/living/basic/abyss_tentacle name = "abyssal tentacle" @@ -79,7 +79,7 @@ var/global/list/global_tentacle_grabs = list() continue if(istype(potential_target, /mob/living/basic/abyss_tentacle)) continue - if(potential_target in global.global_tentacle_grabs) + if(potential_target in GLOB.global_tentacle_grabs) continue if(potential_target in tentacle.recently_released) continue @@ -131,7 +131,7 @@ var/global/list/global_tentacle_grabs = list() if(owner?.tentacle_aggro_mode) aggro_mode = owner.tentacle_aggro_mode -/mob/living/basic/abyss_tentacle/Destroy() +/mob/living/basic/abyss_tentacle/Destroy(force) if(owner) var/datum/splat/vampire/vampire = does_use_disciplines(owner) var/datum/discipline_power/obtenebration/arms_of_the_abyss/power = vampire.get_discipline_power(/datum/discipline_power/obtenebration/arms_of_the_abyss) @@ -143,7 +143,7 @@ var/global/list/global_tentacle_grabs = list() // More checks if(target == owner || istype(target, /mob/living/basic/abyss_tentacle)) return - if(target in global.global_tentacle_grabs) + if(target in GLOB.global_tentacle_grabs) return if(grabbed_mob) return @@ -151,7 +151,7 @@ var/global/list/global_tentacle_grabs = list() to_chat(target, span_userdanger("A shadowy tentacle grabs you!")) visible_message(span_danger("[src] grabs hold of [target]!")) - playsound(/mob/living/basic/abyss_tentacle, 'sound/misc/moist_impact.ogg', 50, FALSE) + playsound(src, 'sound/misc/moist_impact.ogg', 50, FALSE) target.Stun(5) target.forceMove(get_turf(src)) target.set_tentacle_grab(src) @@ -162,14 +162,14 @@ var/global/list/global_tentacle_grabs = list() to_chat(target, span_userdanger("The tentacle forces you to the ground!")) grabbed_mob = target - global.global_tentacle_grabs += target + GLOB.global_tentacle_grabs += target RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_grabbed_mob_move)) /mob/living/basic/abyss_tentacle/proc/release_mob(mob/living/target, add_cooldown = TRUE) if(target == grabbed_mob) grabbed_mob = null - global.global_tentacle_grabs -= target + GLOB.global_tentacle_grabs -= target target.Stun(0) target.clear_tentacle_grab() @@ -199,20 +199,20 @@ var/global/list/global_tentacle_grabs = list() if(get_dist(source, src) > 0) if(world.time >= source.escape_attempt) - source.escape_attempt = world.time + 5 SECONDS - var/rollcheck = SSroll.storyteller_roll(source.st_get_stat(STAT_STRENGTH), 6, list(source), numerical = FALSE) - if(rollcheck == ROLL_SUCCESS) - to_chat(source, span_notice("You break free from the tentacle's grasp!")) - release_mob(source, TRUE) - return - - else if(rollcheck == ROLL_BOTCH || rollcheck == ROLL_FAILURE) - to_chat(source, span_warning("You struggle against the tentacle but can't break free!")) + source.escape_attempt = world.time + 1 TURNS + var/rollcheck = SSroll.storyteller_roll(source.st_get_stat(STAT_STRENGTH), 6, source) + switch(rollcheck) + if(ROLL_SUCCESS) + to_chat(source, span_notice("You break free from the tentacle's grasp!")) + release_mob(source, TRUE) + return + if(ROLL_FAILURE, ROLL_BOTCH) + to_chat(source, span_warning("You struggle against the tentacle but can't break free!")) source.visible_message(span_danger("The tentacle pulls [source] back!")) source.forceMove(get_turf(src)) -/mob/living/basic/abyss_tentacle/death() +/mob/living/basic/abyss_tentacle/death(gibbed) visible_message(span_danger("[src] retracts back into the shadows!")) release_grabbed_mob() . = ..() diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/baali_guard.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/baali_guard.dm index 92d1997e592c..b380e30b888c 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/baali_guard.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/baali_guard.dm @@ -1,5 +1,5 @@ /mob/living/basic/baali_guard - name = "Infernal Creature" + name = "infernal creature" desc = "The peak of abominations armor. Unbelievably undamagable..." icon = 'modular_darkpack/modules/deprecated/icons/32x48.dmi' icon_state = "baali" diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/bear.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/bear.dm index 95c0bfc097d6..cb943693ebd7 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/bear.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/bear.dm @@ -3,8 +3,8 @@ desc = "IS THAT A FUCKING BEAR-" icon = 'modular_darkpack/modules/npc/icons/bear.dmi' bloodquality = BLOOD_QUALITY_LOW - bloodpool = 1 - maxbloodpool = 1 + bloodpool = 10 + maxbloodpool = 10 maxHealth = 850 health = 850 melee_damage_lower = 40 diff --git a/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm b/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm index 870ab0f0055f..070c2a9337e4 100644 --- a/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm +++ b/modular_darkpack/modules/npc/code/nonhuman/hostile/werewolf.dm @@ -40,6 +40,7 @@ icon_dead = "[base_icon_state]_dead-clothing[clothing_type]" . = ..() AddElement(/datum/element/ai_retaliate) + ADD_TRAIT(src, TRAIT_FRENETIC_AURA, INNATE_TRAIT) ///nothing unique, just retaliation. /datum/ai_controller/basic_controller/crinos_beast diff --git a/modular_darkpack/modules/npc/icons/corvid.dmi b/modular_darkpack/modules/npc/icons/corvid.dmi deleted file mode 100644 index 71f5c11099ef..000000000000 Binary files a/modular_darkpack/modules/npc/icons/corvid.dmi and /dev/null differ diff --git a/modular_darkpack/modules/occult_artifacts/code/artifacts/_artifact.dm b/modular_darkpack/modules/occult_artifacts/code/artifacts/_artifact.dm index 47ee132c9ecd..cdb64704524a 100644 --- a/modular_darkpack/modules/occult_artifacts/code/artifacts/_artifact.dm +++ b/modular_darkpack/modules/occult_artifacts/code/artifacts/_artifact.dm @@ -37,6 +37,8 @@ var/research_value = 0 var/can_be_identified_without_ritual = TRUE + var/datum/storyteller_roll/identify_occult/identify_roll + /obj/item/vtm_artifact/proc/identify() if(!identified) name = true_name @@ -61,20 +63,25 @@ return var/mob/living/artifact_identifier = user - if(artifact_identifier.st_get_stat(STAT_OCCULT) < 4) + if(artifact_identifier.st_get_stat(STAT_OCCULT) < 3) to_chat(artifact_identifier, span_warning("What is this thing? Some kind of yard sale item?")) return if(can_be_identified_without_ritual == FALSE) - to_chat(artifact_identifier, span_warning("You've seen countless occult artifacts, trinkets, and powerful relics, but this, you've either never seen it before, or it's power can only be awakened by few...")) + to_chat(artifact_identifier, span_warning("You've seen some occult artifacts, trinkets, and powerful relics, but this, you've either never seen it before, or it's power can only be awakened by few...")) return - to_chat(artifact_identifier, span_cult("You've seen this before in an occult text. You start identifying it...")) - if(do_after(artifact_identifier, 10 SECONDS, src)) - identify() - to_chat(artifact_identifier, span_cult("You successfully identify [src]!")) - else - to_chat(artifact_identifier, span_warning("You stop examining [src].")) + to_chat(artifact_identifier, span_cult("You might have seen this before in an occult text. You start identifying it...")) + if(do_after(artifact_identifier, 1 TURNS, src)) + if(!identify_roll) + identify_roll = new() + identify_roll.difficulty = 8 + var/roll = identify_roll.st_roll(user, src) + if(roll == ROLL_SUCCESS) + identify() + to_chat(artifact_identifier, span_cult("You successfully identify [src]!")) + else + to_chat(artifact_identifier, span_warning("You stop examining [src].")) /obj/effect/spawner/random/occult name = "occult spawner" diff --git a/modular_darkpack/modules/occult_artifacts/code/artifacts/bloodstar.dm b/modular_darkpack/modules/occult_artifacts/code/artifacts/bloodstar.dm index 33b31c28999c..f8f5e58d45ec 100644 --- a/modular_darkpack/modules/occult_artifacts/code/artifacts/bloodstar.dm +++ b/modular_darkpack/modules/occult_artifacts/code/artifacts/bloodstar.dm @@ -1,13 +1,13 @@ /obj/item/vtm_artifact/bloodstar true_name = "Bloodstar" - true_desc = "Increases Bloodpower duration." + true_desc = "Increases Bloodpower efficiency." icon_state = "bloodstar" research_value = 10 /obj/item/vtm_artifact/bloodstar/get_powers() . = ..() - owner.bloodpower_time_plus = 50 + owner.blood_efficiency = 0.8 /obj/item/vtm_artifact/bloodstar/remove_powers() . = ..() - owner.bloodpower_time_plus = 0 + owner.blood_efficiency = 1 diff --git a/modular_darkpack/modules/occult_artifacts/code/artifacts/odious_chalice.dm b/modular_darkpack/modules/occult_artifacts/code/artifacts/odious_chalice.dm index 5861a92886c0..091f8906b4d3 100644 --- a/modular_darkpack/modules/occult_artifacts/code/artifacts/odious_chalice.dm +++ b/modular_darkpack/modules/occult_artifacts/code/artifacts/odious_chalice.dm @@ -21,6 +21,5 @@ M.adjust_fire_loss(-5*stored_blood, TRUE) M.update_damage_overlays() M.update_health_hud() - M.update_blood_hud() playsound(M.loc,'sound/items/drink.ogg', 50, TRUE) return diff --git a/modular_darkpack/modules/onfloor_icons/code/dynamic_item_icon.dm b/modular_darkpack/modules/onfloor_icons/code/dynamic_item_icon.dm index f95918d9cce3..f9052eee82ab 100644 --- a/modular_darkpack/modules/onfloor_icons/code/dynamic_item_icon.dm +++ b/modular_darkpack/modules/onfloor_icons/code/dynamic_item_icon.dm @@ -44,10 +44,10 @@ item.item_flags &= ~ACTIVE_WORLD_ICON item.update_icon() + item.update_greyscale() /datum/element/dynamic_item_icon/proc/apply_onfloor_icon(obj/item/item) item.icon = item.onflooricon - item.worn_icon = initial(item.worn_icon) item.pixel_w = 0 item.cut_overlays() if (item.onflooricon_state) @@ -57,3 +57,4 @@ item.item_flags |= ACTIVE_WORLD_ICON item.update_icon() + //item.update_greyscale() diff --git a/modular_darkpack/modules/onfloor_icons/code/gags_configs.dm b/modular_darkpack/modules/onfloor_icons/code/gags_configs.dm new file mode 100644 index 000000000000..bccde76a4e12 --- /dev/null +++ b/modular_darkpack/modules/onfloor_icons/code/gags_configs.dm @@ -0,0 +1,7 @@ +/datum/greyscale_config/beret/onfloor + name = "Badged (Onfloor)" + icon_file = 'modular_darkpack/modules/clothes/icons/head_onfloor.dmi' + +/datum/greyscale_config/beret_badge/onfloor + name = "Badged Beret (Onfloor)" + icon_file = 'modular_darkpack/modules/clothes/icons/head_onfloor.dmi' diff --git a/modular_darkpack/modules/paths/code/path.dm b/modular_darkpack/modules/paths/code/path.dm index 0568daea7da6..d55cc2950518 100644 --- a/modular_darkpack/modules/paths/code/path.dm +++ b/modular_darkpack/modules/paths/code/path.dm @@ -1,4 +1,5 @@ /datum/discipline/path + abstract_type = /datum/discipline/path action_type = /datum/action/discipline/path var/action_replaced = FALSE selectable = FALSE //cant buy it as a ghoul diff --git a/modular_darkpack/modules/paths/code/spellbooks/spellbooks.dm b/modular_darkpack/modules/paths/code/spellbooks/spellbooks.dm index a6c0cc4474d0..cb5f18bae3df 100644 --- a/modular_darkpack/modules/paths/code/spellbooks/spellbooks.dm +++ b/modular_darkpack/modules/paths/code/spellbooks/spellbooks.dm @@ -1,4 +1,5 @@ /obj/item/path_spellbook + abstract_type = /obj/item/path_spellbook name = "path spellbook" desc = "A default path spellbook. if you're seeing this ingame, please report to coders" icon = 'modular_darkpack/modules/paths/icons/paths.dmi' @@ -16,7 +17,7 @@ var/true_name = "" var/true_desc = "" - COOLDOWN_DECLARE(identify_failure_cooldown) + var/datum/storyteller_roll/identify_occult/identify_roll /obj/item/path_spellbook/Initialize(mapload) . = ..() @@ -30,19 +31,15 @@ . = ..() if(!identified) . += span_notice("You could try to clean off the dust to see what lies beneath.") - if(!COOLDOWN_FINISHED(src, identify_failure_cooldown)) - var/time_left = COOLDOWN_TIMELEFT(src, identify_failure_cooldown) / 10 - . += span_warning("You need to wait [time_left] seconds before trying again.") /obj/item/path_spellbook/attack_self(mob/living/carbon/human/user) if(!identified) - if(!COOLDOWN_FINISHED(src, identify_failure_cooldown)) - var/time_left = COOLDOWN_TIMELEFT(src, identify_failure_cooldown) / 10 - to_chat(user, span_warning("You need to wait [time_left] seconds before trying again.")) - return - if(do_after(user, 5 SECONDS)) - var/roll = SSroll.storyteller_roll(user.st_get_stat(STAT_INTELLIGENCE) + user.st_get_stat(STAT_OCCULT), path_level + 3, user, numerical = FALSE) + if(do_after(user, 1 TURNS)) + if(!identify_roll) + identify_roll = new() + identify_roll.difficulty = path_level + 3 + var/roll = identify_roll.st_roll(user, src) switch(roll) if(ROLL_SUCCESS) to_chat(user, span_cult("You wipe the dust off the previously irrelevant tome. Did someone misplace it from the Library?")) @@ -52,7 +49,6 @@ return else to_chat(user, span_warning("You fail to figure out the real nature of the book and get distracted by more important matters. Maybe its a cookbook?")) - COOLDOWN_START(src, identify_failure_cooldown, 2 MINUTES) return return @@ -121,6 +117,7 @@ /obj/item/occult_book + abstract_type = /obj/item/occult_book name = "occult book" desc = "A default occult book. if you're seeing this ingame, please report to coders" icon = 'modular_darkpack/modules/paths/icons/paths.dmi' diff --git a/modular_darkpack/modules/phones/code/_phone.dm b/modular_darkpack/modules/phones/code/_phone.dm index 1b40a871bec3..059c9a96152f 100644 --- a/modular_darkpack/modules/phones/code/_phone.dm +++ b/modular_darkpack/modules/phones/code/_phone.dm @@ -43,6 +43,8 @@ var/ringer = TRUE // If the phone shows balloon alerts when ringing. var/vibration = TRUE + // Passive particle effect generation for when on call + var/obj/effect/abstract/particle_holder/particle_generator // If the phone's microphone is muted. var/muted = FALSE // ID of the timer that the phone uses for ringing. Deleted once the user denies a phone call or misses it. @@ -115,6 +117,9 @@ if(our_contact.number == sim_card.phone_number) contact_network.contacts -= our_contact + if(particle_generator) + QDEL_NULL(particle_generator) + lose_hearing_sensitivity(ROUNDSTART_TRAIT) UnregisterSignal(src, COMSIG_MOVABLE_HEAR) if(sim_card) @@ -172,18 +177,18 @@ return CLICK_ACTION_SUCCESS return CLICK_ACTION_BLOCKING -/obj/item/smartphone/attackby(obj/item/attacking_item, mob/user, params) - if(istype(attacking_item, /obj/item/sim_card)) +/obj/item/smartphone/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/sim_card)) if(sim_card) balloon_alert(user, "[sim_card] already installed!") - return FALSE - balloon_alert(user, "you insert \the [attacking_item]!") - sim_card = attacking_item - user.transferItemToLoc(attacking_item, src) + return ITEM_INTERACT_BLOCKING + balloon_alert(user, "you insert \the [tool]!") + sim_card = tool + user.transferItemToLoc(tool, src) sim_card.phone_weakref = WEAKREF(src) phone_flags &= ~PHONE_NO_SIM - return TRUE - return ..() + return ITEM_INTERACT_SUCCESS + return NONE /obj/item/smartphone/ui_status(mob/user, datum/ui_state/state) if(!(phone_flags & PHONE_OPEN)) diff --git a/modular_darkpack/modules/phones/code/phone_effects.dm b/modular_darkpack/modules/phones/code/phone_effects.dm new file mode 100644 index 000000000000..0eddabad1ceb --- /dev/null +++ b/modular_darkpack/modules/phones/code/phone_effects.dm @@ -0,0 +1,17 @@ +/particles/phone_ringing + icon = 'modular_darkpack/modules/phones/icons/phone.dmi' + icon_state = list("note" = 1) + width = 32 + height = 48 + count = 5 + spawning = 0.5 + lifespan = 2 SECONDS + fade = 1.5 SECONDS + gravity = list(0, 0.1) + position = generator(GEN_SPHERE, 0, 16, NORMAL_RAND) + spin = generator(GEN_NUM, -1, 1, NORMAL_RAND) + + +/obj/item/smartphone/proc/setup_particles() + if(!particle_generator) + particle_generator = new(src, /particles/phone_ringing, PARTICLE_ATTACH_MOB) diff --git a/modular_darkpack/modules/phones/code/phone_procs.dm b/modular_darkpack/modules/phones/code/phone_procs.dm index 7b4f4d54c4dc..1e1bd7d7f5f2 100644 --- a/modular_darkpack/modules/phones/code/phone_procs.dm +++ b/modular_darkpack/modules/phones/code/phone_procs.dm @@ -57,10 +57,14 @@ incoming_phone_number = null if(current_state == PHONE_RINGING) START_PROCESSING(SSprocessing, src) + if(ringer) + setup_particles() if(current_state == PHONE_IN_CALL || current_state == PHONE_AVAILABLE) if(phone_ringing_timer) deltimer(phone_ringing_timer) + if(particle_generator) + QDEL_NULL(particle_generator) STOP_PROCESSING(SSprocessing, src) /obj/item/smartphone/proc/check_missing_sim_card(mob/user) diff --git a/modular_darkpack/modules/phones/icons/lefthand.dmi b/modular_darkpack/modules/phones/icons/lefthand.dmi index af0ff54ee435..9092e1da6f77 100644 Binary files a/modular_darkpack/modules/phones/icons/lefthand.dmi and b/modular_darkpack/modules/phones/icons/lefthand.dmi differ diff --git a/modular_darkpack/modules/phones/icons/phone.dmi b/modular_darkpack/modules/phones/icons/phone.dmi index cd47b2b544cf..c9028cddfae1 100644 Binary files a/modular_darkpack/modules/phones/icons/phone.dmi and b/modular_darkpack/modules/phones/icons/phone.dmi differ diff --git a/modular_darkpack/modules/phones/icons/phone_onfloor.dmi b/modular_darkpack/modules/phones/icons/phone_onfloor.dmi index 6a3c9c97a0e9..87237de75054 100644 Binary files a/modular_darkpack/modules/phones/icons/phone_onfloor.dmi and b/modular_darkpack/modules/phones/icons/phone_onfloor.dmi differ diff --git a/modular_darkpack/modules/phones/icons/righthand.dmi b/modular_darkpack/modules/phones/icons/righthand.dmi index 4a01bd10225d..2c0594955422 100644 Binary files a/modular_darkpack/modules/phones/icons/righthand.dmi and b/modular_darkpack/modules/phones/icons/righthand.dmi differ diff --git a/modular_darkpack/modules/phones/sounds/phone.ogg b/modular_darkpack/modules/phones/sounds/phone.ogg new file mode 100644 index 000000000000..a874d03b3646 Binary files /dev/null and b/modular_darkpack/modules/phones/sounds/phone.ogg differ diff --git a/modular_darkpack/modules/phones/sounds/phonestop.ogg b/modular_darkpack/modules/phones/sounds/phonestop.ogg new file mode 100644 index 000000000000..d41adb7f5a62 Binary files /dev/null and b/modular_darkpack/modules/phones/sounds/phonestop.ogg differ diff --git a/modular_darkpack/modules/postal/code/postal.dm b/modular_darkpack/modules/postal/code/postal.dm index 28bc591621e7..a3870baa23c4 100644 --- a/modular_darkpack/modules/postal/code/postal.dm +++ b/modular_darkpack/modules/postal/code/postal.dm @@ -21,22 +21,25 @@ else say("Not enough money on [src] balance!") -// DARKPACK TODO - (item_interaction) -/obj/lettermachine/attackby(obj/item/I, mob/user, params) - if(iscash(I)) - money += I.get_item_credit_value() - to_chat(user, span_notice("You insert [I.get_item_credit_value()] dollars into [src].")) - say("[I] inserted.") - qdel(I) - if(istype(I, /obj/item/mark)) +/obj/lettermachine/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(iscash(tool)) + money += tool.get_item_credit_value() + to_chat(user, span_notice("You insert [tool.get_item_credit_value()] [MONEY_NAME] into [src].")) + say("[tool] inserted.") + qdel(tool) + return ITEM_INTERACT_SUCCESS + + if(istype(tool, /obj/item/mark)) new /obj/item/stack/dollar(loc, 30) - say("[I] delivered!") - qdel(I) - return ..() + say("[tool] delivered!") + qdel(tool) + return ITEM_INTERACT_SUCCESS + + return NONE /obj/lettermachine/examine(mob/user) . = ..() - . += span_info("It contains [money] dollars.") + . += span_info("It contains [money] [MONEY_NAME].") /obj/item/letter name = "letter" diff --git a/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm b/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm index 13a0d14b5dd0..e61c6b3da141 100644 --- a/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm +++ b/modular_darkpack/modules/powers/code/discipline/__discipline_power.dm @@ -507,7 +507,7 @@ if (toggled && (duration_length == 0)) return - //REFACTOR ME + // DARKPACK TODO - (REFACTOR ME) var/full_duration_length = duration_length + owner.discipline_time_plus duration_timers.Add(addtimer(CALLBACK(src, PROC_REF(duration_expire), target), full_duration_length, TIMER_STOPPABLE | TIMER_DELETE_ME)) diff --git a/modular_darkpack/modules/powers/code/discipline/auspex/aura_component.dm b/modular_darkpack/modules/powers/code/discipline/auspex/aura_component.dm index dbe6f5ed2ef3..8b7844cd5d0e 100644 --- a/modular_darkpack/modules/powers/code/discipline/auspex/aura_component.dm +++ b/modular_darkpack/modules/powers/code/discipline/auspex/aura_component.dm @@ -72,6 +72,17 @@ aura_image.color = hsv2rgb(hsv_color_value) holder.appearance = aura_image + // DARKPACK TODO - aura still needs real sprites. + if(HAS_TRAIT(parent_mob, TRAIT_FRENETIC_AURA)) + var/icon/temporary_icon_holder = holder.appearance + var/mutable_appearance/aura_image = mutable_appearance(temporary_icon_holder, "old_aura_bright", ABOVE_MOB_LAYER, parent_mob, GAME_PLANE) + + var/list/hsv_color_value = rgb2hsv(holder.color) + hsv_color_value[2] = hsv_color_value[2] * 1.5 // Way brighter for shapeshifters + + aura_image.color = hsv2rgb(hsv_color_value) + holder.appearance = aura_image + /datum/component/aura/proc/update_aura_overlays(mutable_appearance/aura_appearance, image/holder) holder.cut_overlays() diff --git a/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm b/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm index 14e578e6bde4..48a88e37241c 100644 --- a/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm +++ b/modular_darkpack/modules/powers/code/discipline/auspex/auspex.dm @@ -107,12 +107,14 @@ cooldown_length = 1 SCENES vitae_cost = 0 + toggled = TRUE + /datum/discipline_power/auspex/aura_perception/activate() . = ..() var/datum/atom_hud/data/auspex_aura/target_hud = GLOB.huds[DATA_HUD_AUSPEX_AURAS] target_hud.show_to(owner) - var/list/heard = get_hearers_in_range(DEFAULT_MESSAGE_RANGE, owner) + var/list/heard = orange(DEFAULT_MESSAGE_RANGE, owner) for(var/mob/living/hearer in heard) hearer.apply_status_effect(/datum/status_effect/question_emotion) @@ -157,7 +159,6 @@ // Can remotely scan objects and mobs. if((get_dist(scanned_atom, user) > 8) || (!(scanned_atom in view(8, user)))) return TRUE - playsound(owner, SFX_INDUSTRIAL_SCAN, 20, TRUE, -2, TRUE, FALSE) // GATHER INFORMATION @@ -270,8 +271,8 @@ /datum/discipline_power/auspex/psychic_projection/activate() . = ..() - var/roll = SSroll.storyteller_roll(owner.st_get_stat(STAT_PERCEPTION) + owner.st_get_stat(STAT_AWARENESS), 7, owner, owner, TRUE) - if(roll > 0) + var/roll = SSroll.storyteller_roll(owner.st_get_stat(STAT_PERCEPTION) + owner.st_get_stat(STAT_AWARENESS), 7, owner) + if(roll == ROLL_SUCCESS) owner.enter_avatar() else to_chat(owner, span_warning("Your mind fails to leave your body.")) diff --git a/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm b/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm index c85c717a1785..967697b69ff2 100644 --- a/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm +++ b/modular_darkpack/modules/powers/code/discipline/bloodheal/bloodheal.dm @@ -12,7 +12,7 @@ name = "Bloodheal power name" desc = "Bloodheal power description" - activate_sound = 'modular_darkpack/modules/deprecated/sounds/bloodhealing.ogg' + activate_sound = 'modular_darkpack/modules/vampire_the_masquerade/sounds/bloodhealing.ogg' level = 1 check_flags = DISC_CHECK_TORPORED diff --git a/modular_darkpack/modules/powers/code/discipline/dementation.dm b/modular_darkpack/modules/powers/code/discipline/dementation.dm index 258c1126f48a..017a2a0b8638 100644 --- a/modular_darkpack/modules/powers/code/discipline/dementation.dm +++ b/modular_darkpack/modules/powers/code/discipline/dementation.dm @@ -377,7 +377,7 @@ frenzy or Rötschreck response is automatic. owner.say(attack_text, spans = list("bold", "singing")) var/list/potential_targets = list() for(var/mob/living/carbon/human/hearer in (get_hearers_in_view(8, owner) - owner)) - if(!hearer.can_hear() || hearer.stat > CONSCIOUS) + if(!HAS_TRAIT(hearer, TRAIT_DEAF) || hearer.stat > CONSCIOUS) continue potential_targets += hearer var/targets_affected = 0 diff --git a/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm b/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm index d0b0bceadca4..56d76f718711 100644 --- a/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm +++ b/modular_darkpack/modules/powers/code/discipline/dominate/dominate.dm @@ -100,14 +100,14 @@ return TRUE var/theirpower = target.st_get_stat(STAT_TEMPORARY_WILLPOWER) - var/mypower = SSroll.storyteller_roll(owner_stat, difficulty = theirpower, mobs_to_show_output = owner, numerical = TRUE) + var/mypower = SSroll.storyteller_roll(owner_stat, difficulty = theirpower, roller = owner, numerical = TRUE) //tremere have built-in safeguards to easily dominate their stone servitors var/mob/living/carbon/human/human_target = target if(HAS_TRAIT(target, TRAIT_WEAK_TO_DOMINATE)) theirpower -= 2 - //wearing dark sunglasses makes it harder for the Dominator to capture the victim's gaze and raises difficulty -- v20 'Dominate' section titled 'Eye Contact' + //wearing dark sunglasses makes it harder for the Dominator to capture the victim's gaze and raises difficulty -- V20 'Dominate' section titled 'Eye Contact' var/total_tint = 0 for(var/obj/item/clothing/worn_item in human_target.get_equipped_items(INCLUDE_ABSTRACT)) total_tint += worn_item.tint @@ -204,7 +204,7 @@ custom_command = tgui_input_text(owner, "Dominate Command", "What is your command?", encode = FALSE) var/mob/living/carbon/human/conditioner = target.conditioner?.resolve() if(owner != conditioner) - //v20 Dominate 'Command' section + //V20 Dominate 'Command' section if(length(splittext(custom_command, " ")) > 1) to_chat(owner, span_warning("Commands must be only ONE word!")) return FALSE diff --git a/modular_darkpack/modules/powers/code/discipline/melpominee.dm b/modular_darkpack/modules/powers/code/discipline/melpominee.dm index f766537b3959..dc072a67ba7a 100644 --- a/modular_darkpack/modules/powers/code/discipline/melpominee.dm +++ b/modular_darkpack/modules/powers/code/discipline/melpominee.dm @@ -63,7 +63,7 @@ difficulty_malus = 0 if (get_dist(hearer, target) > 3) difficulty_malus += 1 - if (SSroll.storyteller_roll(hearer.st_get_stat(STAT_PERCEPTION), base_difficulty + difficulty_malus, mobs_to_show_output = hearer) == ROLL_SUCCESS) + if (SSroll.storyteller_roll(hearer.st_get_stat(STAT_PERCEPTION), base_difficulty + difficulty_malus, roller = hearer) == ROLL_SUCCESS) if (masked) to_chat(hearer, span_warning("[target]'s jaw isn't moving to match [target.p_their()] words.")) else diff --git a/modular_darkpack/modules/powers/code/discipline/obtenebration.dm b/modular_darkpack/modules/powers/code/discipline/obtenebration.dm index 538e00bbbc40..30572b4a214f 100644 --- a/modular_darkpack/modules/powers/code/discipline/obtenebration.dm +++ b/modular_darkpack/modules/powers/code/discipline/obtenebration.dm @@ -192,7 +192,7 @@ /datum/discipline_power/obtenebration/black_metamorphosis/activate() . = ..() activating = FALSE - var/roll = SSroll.storyteller_roll(owner.st_get_stat(STAT_MANIPULATION)/* + owner.st_get_stat(STAT_COURAGE)*/, 7, owner) + var/roll = SSroll.storyteller_roll(owner.st_get_stat(STAT_MANIPULATION) + owner.st_get_stat(STAT_COURAGE), 7, owner) switch(roll) if(ROLL_SUCCESS) successful = TRUE diff --git a/modular_darkpack/modules/powers/code/discipline/presence/presence.dm b/modular_darkpack/modules/powers/code/discipline/presence/presence.dm index 8399f172926c..aed2b15ab46b 100644 --- a/modular_darkpack/modules/powers/code/discipline/presence/presence.dm +++ b/modular_darkpack/modules/powers/code/discipline/presence/presence.dm @@ -30,7 +30,7 @@ //is the difficulty pre-defined? if not, its probably their willpower. var/theirpower = difficulty || target.st_get_stat(STAT_TEMPORARY_WILLPOWER) - var/successes = SSroll.storyteller_roll(owner_stat, difficulty = theirpower, mobs_to_show_output = owner, numerical = TRUE) + var/successes = SSroll.storyteller_roll(owner_stat, difficulty = theirpower, roller = owner, numerical = TRUE) //botch if(successes < 0) @@ -49,14 +49,14 @@ target.apply_overlay(MUTATIONS_LAYER) SEND_SOUND(target, sound('modular_darkpack/modules/powers/sounds/presence_activate.ogg')) - // resist presence button - note to self, in the future, v20 states that the resister must continue to spend willpower if in the presence of the vamp + // resist presence button - note to self, in the future, V20 states that the resister must continue to spend willpower if in the presence of the vamp var/datum/action/resist_presence/resist_action = new(target) resist_action.Grant(target) // remove the action after 20 seconds addtimer(CALLBACK(resist_action, TYPE_PROC_REF(/datum/action, Remove), target), resist_timer) -//used in awe - v20 book states that awe affects the targets of lowest willpower first if affecting multiple targets. +//used in awe - V20 book states that awe affects the targets of lowest willpower first if affecting multiple targets. /datum/discipline_power/presence/proc/sort_targets_by_willpower(list/targets) var/list/sorted = list() for(var/mob/living/carbon/target in targets) @@ -98,7 +98,7 @@ user.st_set_stat(STAT_TEMPORARY_WILLPOWER, max((user.st_get_stat(STAT_TEMPORARY_WILLPOWER) - 1),0)) to_chat(user, span_warning("You burn a point of willpower to resist the supernatural influence...")) - var/roll_success = SSroll.storyteller_roll(user.st_get_stat(STAT_TEMPORARY_WILLPOWER), difficulty = 8, mobs_to_show_output = user) + var/roll_success = SSroll.storyteller_roll(user.st_get_stat(STAT_TEMPORARY_WILLPOWER), difficulty = 8, roller = user) if(roll_success) user.remove_overlay(MUTATIONS_LAYER) @@ -129,7 +129,7 @@ .=..() //charisma + performance - successes = SSroll.storyteller_roll(owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_PERFORMANCE), difficulty = 7, mobs_to_show_output = owner, numerical = TRUE) + successes = SSroll.storyteller_roll(owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_PERFORMANCE), difficulty = 7, roller = owner, numerical = TRUE) if(successes > 0) return TRUE @@ -149,7 +149,7 @@ to_chat(owner, span_warning("There is no one around to be awed by your presence.")) return - var/list/target_counts = list(1, 2, 6, 20, length(potential_targets)) //v20 core rulebook presence -> awe + var/list/target_counts = list(1, 2, 6, 20, length(potential_targets)) //V20 core rulebook presence -> awe var/targets_to_affect = target_counts[clamp(successes, 1, 5)] potential_targets = sort_targets_by_willpower(potential_targets) @@ -186,14 +186,14 @@ multi_activate = TRUE cooldown_length = 15 SECONDS duration_length = 10 SECONDS - vitae_cost = 1 //no mention of literally any cost for using this in v20 + vitae_cost = 1 //no mention of literally any cost for using this in V20 var/successes = 0 /datum/discipline_power/presence/dread_gaze/pre_activation_checks(mob/living/target) //charisma + intimidation, difficulty equal to the victims wits + courage - successes = presence_check(owner, target, owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_INTIMIDATION), difficulty = (target.st_get_stat(STAT_WITS))) //+ target.st_get_stat(STAT_COURAGE))) + successes = presence_check(owner, target, owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_INTIMIDATION), difficulty = (target.st_get_stat(STAT_WITS) + target.st_get_stat(STAT_COURAGE))) if(successes > 0) return TRUE @@ -211,7 +211,7 @@ to_chat(target, span_userdanger("Overwhelming dread fills you! You must get away from [owner]!")) to_chat(owner, span_warning("Your terrifying presence sends [target] fleeing in terror!")) - //v20's 'dread gaze' section states that with 3 or more successes targets will find themselves scratching at the walls or fleeing against their will because they are so terrified. + //V20's 'dread gaze' section states that with 3 or more successes targets will find themselves scratching at the walls or fleeing against their will because they are so terrified. //var/datum/cb = CALLBACK(target, TYPE_PROC_REF(/mob/living/carbon/human, step_away_caster), owner) //for(var/i in 1 to 30) //addtimer(cb, (i - 1) * target.total_multiplicative_slowdown()) @@ -314,7 +314,7 @@ var/location_info = "[get_area_name(owner_turf)], X:[owner_turf.x] Y:[owner_turf.y] Z:[owner_turf.z]" to_chat(summon_target, span_yellowteamradio("[owner.real_name] is summoning you to their location. [owner.real_name] is currently at [location_info]")) - //v20 presence -> 'summon' section for this flavortext + //V20 presence -> 'summon' section for this flavortext var/list/flavor_texts = list( "You feel a faint pull towards [owner.real_name], approaching slowly and hesitantly.", "You feel reluctantly compelled to seek out [owner.real_name], though obstacles easily deter you.", @@ -357,7 +357,7 @@ continue //'the victim must make a courage roll with a difficulty equal to the caster's charisma + intimidation to a maximum of 10' - var/hearer_successes = SSroll.storyteller_roll(hearer.st_get_stat(STAT_COURAGE), difficulty = owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_INTIMIDATION), mobs_to_show_output = hearer, numerical = TRUE) + var/hearer_successes = SSroll.storyteller_roll(hearer.st_get_stat(STAT_COURAGE), difficulty = owner.st_get_stat(STAT_CHARISMA) + owner.st_get_stat(STAT_INTIMIDATION), roller = hearer, numerical = TRUE) hearer_successes = max(0, hearer_successes) apply_presence_overlay(hearer, 3 MINUTES) @@ -369,7 +369,10 @@ to_chat(hearer, span_info("Despite the overwhelming presence, your will allows you to make [hearer_successes] contradictory action\s until youre allowed to leave [owner]'s company.")) var/total_affected = length(affected_targets) - to_chat(owner, span_warning(total_affected > 0 ? "Your Majesty overwhelms [total_affected] individual[total_affected == 1 ? "" : "s"] in your presence!" : "No one is present to witness your Majesty.")) + if(total_affected > 0) + to_chat(owner, span_warning("Your Majesty overwhelms [total_affected] individual[total_affected == 1 ? "" : "s"] in your presence!")) + else + to_chat(owner, span_warning("No one is present to witness your Majesty.")) /datum/discipline_power/presence/majesty/deactivate(mob/living/carbon/human/target) . = ..() diff --git a/modular_darkpack/modules/powers/code/discipline/quietus/components/scorptions_touch.dm b/modular_darkpack/modules/powers/code/discipline/quietus/components/scorptions_touch.dm index 5e1796d65576..52fb5e62940a 100644 --- a/modular_darkpack/modules/powers/code/discipline/quietus/components/scorptions_touch.dm +++ b/modular_darkpack/modules/powers/code/discipline/quietus/components/scorptions_touch.dm @@ -42,7 +42,7 @@ var/mob/living/carbon/human/victim = target // victim resists the posion with stamina + fortitude - var/resistance = SSroll.storyteller_roll(dice = (victim.st_get_stat(STAT_STAMINA)/* + victim.st_get_stat(STAT_FORTITUDE)*/), difficulty = 6, numerical = TRUE, mobs_to_show_output = victim) + var/resistance = SSroll.storyteller_roll(dice = (victim.st_get_stat(STAT_STAMINA)/* + victim.st_get_stat(STAT_FORTITUDE)*/), difficulty = 6, numerical = TRUE, roller = victim) // each resistance success subtracts from the duration var/effective_duration = max(0, poison_duration - resistance) diff --git a/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm b/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm index 890d3e897e55..277f43cd7d71 100644 --- a/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm +++ b/modular_darkpack/modules/powers/code/discipline/quietus/quietus.dm @@ -48,7 +48,7 @@ /datum/discipline_power/quietus/scorpions_touch/pre_activation_checks(atom/target) . = ..() - var/success_count = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = 6, mobs_to_show_output = owner, alert_atom = owner, numerical = TRUE) + var/success_count = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = 6, roller = owner, numerical = TRUE) if(success_count <= 0) to_chat(owner, span_warning("Your blood fails to transform into poison!")) @@ -191,9 +191,9 @@ var/victim_stamina = victim.st_get_stat(STAT_STAMINA) var/victim_willpower = victim.st_get_stat(STAT_PERMANENT_WILLPOWER) - var/attacker_successes = SSroll.storyteller_roll(attacker_stamina, victim_willpower, numerical = TRUE, mobs_to_show_output = owner) + var/attacker_successes = SSroll.storyteller_roll(attacker_stamina, victim_willpower, numerical = TRUE, roller = owner) - var/victim_successes = SSroll.storyteller_roll(victim_stamina, victim_willpower, numerical = TRUE, mobs_to_show_output = victim) + var/victim_successes = SSroll.storyteller_roll(victim_stamina, victim_willpower, numerical = TRUE, roller = victim) var/net_successes = attacker_successes - victim_successes @@ -258,7 +258,7 @@ var/layers = tgui_input_number(owner, "How many blood points do you want to use?", "Baal's Caress", 1, max_layers, 1) if(!layers) return - user.bloodpool -= layers + user.adjust_blood_pool(-layers) target.AddComponent(/datum/component/baals_caress, owner, layers) to_chat(owner, span_notice("You imbue [target] with [layers] layer\s of your toxic vitae!")) diff --git a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm index ad1e06ff9a12..207df790feff 100644 --- a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm +++ b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/path_of_blood.dm @@ -31,7 +31,7 @@ /datum/discipline_power/thaumaturgy/activate(atom/target) . = ..() //Thaumaturgy powers have different effects based off the amount of successes. I dont want to copy paste the code, so this is being put here. - success_count = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = (level + 3), numerical = TRUE, mobs_to_show_output = owner) + success_count = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = (level + 3), numerical = TRUE, roller = owner) if(success_count < 0) thaumaturgy_botch_effect() return TRUE @@ -153,7 +153,7 @@ /datum/discipline_power/thaumaturgy/cauldron_of_blood ) -// "Each success forces the subject to spend one blood point immediately in the way the caster desires" -v20 Core Rulebook +// "Each success forces the subject to spend one blood point immediately in the way the caster desires" -V20 Core Rulebook /datum/discipline_power/thaumaturgy/blood_rage/activate(mob/living/carbon/human/target) if(..()) return diff --git a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/levinbolt.dm b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/levinbolt.dm index 4db40e6ac7af..edb5f7e8bbfa 100644 --- a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/levinbolt.dm +++ b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/levinbolt.dm @@ -84,8 +84,7 @@ disabled_any = TRUE if(disabled_any) - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(5, 1, get_turf(human_target)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(human_target), 5, 1) spark_system.start() return TRUE @@ -95,8 +94,7 @@ cargo_comp.locked = !cargo_comp.locked // sparks - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(3, 1, get_turf(target)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(target), 3, 1) spark_system.start() playsound(target, 'sound/effects/sparks/sparks4.ogg', 50, TRUE) @@ -111,8 +109,7 @@ fuse.damaged += 101 fuse.check_damage(owner, TRUE) - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(5, 1, get_turf(target)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(target), 5, 1) spark_system.start() playsound(target, 'sound/effects/sparks/sparks2.ogg', 75, TRUE) @@ -434,8 +431,7 @@ if(!owner) return - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(rand(3,7), 1, get_turf(owner)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(owner), rand(3,7), 1) spark_system.start() if(prob(50)) @@ -467,8 +463,7 @@ target.Stun(1 SECONDS) target.visible_message(span_warning("[target] convulses from the electrical shock!")) - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(8, 1, get_turf(target)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(target), 8, 1) spark_system.start() owner.visible_message(span_danger("Lightning arcs from [owner] to [target]!")) @@ -486,8 +481,7 @@ addtimer(CALLBACK(attacker, TYPE_PROC_REF(/mob, emote), "scream"), 1) attacker.Stun(4 SECONDS) attacker.electrocute_act(rand(10,20), owner, siemens_coeff = 1, flags = NONE) - var/datum/effect_system/spark_spread/spark_system = new - spark_system.set_up(5, 1, get_turf(attacker)) + var/datum/effect_system/basic/spark_spread/spark_system = new(get_turf(attacker), 5, 1) spark_system.start() playsound(attacker, 'sound/effects/sparks/sparks4.ogg', 60, TRUE) diff --git a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/lure_of_flames.dm b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/lure_of_flames.dm index 41640ba1665d..229b155b58c6 100644 --- a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/lure_of_flames.dm +++ b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/paths/lure_of_flames.dm @@ -14,12 +14,12 @@ var/range_successes var/flames_range -//v20 core rulebook states that lure of flames can only conjure flames so far depending on successes. +//V20 core rulebook states that lure of flames can only conjure flames so far depending on successes. /datum/discipline_power/thaumaturgy/path/flames/pre_activation_checks(atom/target, ranged) . = ..() if(src.ranged == FALSE) return TRUE - range_successes = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = (level + 3), numerical = TRUE, mobs_to_show_output = owner) + range_successes = SSroll.storyteller_roll(dice = owner.st_get_stat(STAT_PERMANENT_WILLPOWER), difficulty = (level + 3), numerical = TRUE, roller = owner) switch(range_successes) if(-INFINITY to 0) to_chat(owner, "You fail to conjure flames anywhere further than your own hand.") diff --git a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/status_effects/blood_of_potency_effect.dm b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/status_effects/blood_of_potency_effect.dm index 23fdca6b8b8c..ea2f2ebaa662 100644 --- a/modular_darkpack/modules/powers/code/discipline/thaumaturgy/status_effects/blood_of_potency_effect.dm +++ b/modular_darkpack/modules/powers/code/discipline/thaumaturgy/status_effects/blood_of_potency_effect.dm @@ -19,10 +19,8 @@ iskindred(owner)?.set_generation(stored_generation) stored_generation = null - owner.update_blood_hud() - if(owner.bloodpool > owner.maxbloodpool) - owner.bloodpool = owner.maxbloodpool + owner.set_blood_pool(owner.maxbloodpool) /atom/movable/screen/alert/status_effect/blood_of_potency name = "Blood of Potency" diff --git a/modular_darkpack/modules/powers/code/discipline_actions.dm b/modular_darkpack/modules/powers/code/discipline_actions.dm index 4e35d3a3b104..d94ef451dc99 100644 --- a/modular_darkpack/modules/powers/code/discipline_actions.dm +++ b/modular_darkpack/modules/powers/code/discipline_actions.dm @@ -1,6 +1,5 @@ /datum/action/discipline check_flags = NONE - background_icon = 'modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi' background_icon_state = "bg_discipline" button_icon = 'modular_darkpack/modules/deprecated/icons/ui/actions.dmi' button_icon_state = "bloodheal" diff --git a/modular_darkpack/modules/powers/icons/auras.dmi b/modular_darkpack/modules/powers/icons/auras.dmi index 2ef96f58195e..136a1e6ad2b6 100644 Binary files a/modular_darkpack/modules/powers/icons/auras.dmi and b/modular_darkpack/modules/powers/icons/auras.dmi differ diff --git a/modular_darkpack/modules/preferences/height_preference.dm b/modular_darkpack/modules/preferences/height_preference.dm new file mode 100644 index 000000000000..ea39fd09f5ff --- /dev/null +++ b/modular_darkpack/modules/preferences/height_preference.dm @@ -0,0 +1,25 @@ +/datum/preference/numeric/height + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL + savefile_key = "height" + savefile_identifier = PREFERENCE_CHARACTER + priority = PREFERENCE_PRIORITY_WORLD_OF_DARKNESS + minimum = HUMAN_HEIGHT_DWARF // 6 would be like. 4'10 + maximum = HUMAN_HEIGHT_TALLEST // 18 would be like... 6'8 or 6'10? + step = 2 + +/datum/preference/numeric/height/create_default_value() + return HUMAN_HEIGHT_MEDIUM // 12 or 5'10 or 5'11 idk + +/datum/preference/numeric/height/apply_to_human(mob/living/carbon/human/target, value) + target.set_mob_height(value) + +/datum/preference/numeric/height/compile_constant_data() + var/list/data = ..() + var/list/height_labels = list() + for(var/i in minimum to maximum) + var/total_inches = 58 + ((i - 6) * 2) // min is 58 inches (4'10) and each step is 2 inches so each step should increase by 2, 7 for example is 60 + var/feet = round(total_inches / 12) + var/inches = total_inches % 12 + height_labels["[i]"] = "[feet]'[inches]\"" + data["height_labels"] = height_labels + return data diff --git a/modular_darkpack/modules/quirks/code/negative_quirks/derangement.dm b/modular_darkpack/modules/quirks/code/negative_quirks/derangement.dm index 28598145d46a..494bea3a29eb 100644 --- a/modular_darkpack/modules/quirks/code/negative_quirks/derangement.dm +++ b/modular_darkpack/modules/quirks/code/negative_quirks/derangement.dm @@ -85,7 +85,7 @@ var/list/objects = list() for(var/obj/object in view(hallucinator)) - if((object.invisibility > hallucinator.see_invisible) || !object.loc || !object.name) + if((object.invisibility > hallucinator.see_invisible) || !object.loc || !object.name || (object in hallucinator.contents)) continue var/weight = 1 if(isitem(object)) @@ -95,9 +95,8 @@ else if(ismachinery(object)) weight = 2 objects[object] = weight - if(!objects.len) + if(!length(objects)) return - objects -= hallucinator.contents var/obj/speaker = pick_weight(objects) var/speech = pick(audible_hallucinations) @@ -106,7 +105,7 @@ hallucinator.playsound_local(hallucinator, audible_hallucinations[speech], vol = 20, vary = TRUE) if(hallucinator.client.prefs.read_preference(/datum/preference/toggle/see_rc_emotes)) hallucinator.create_chat_message(speaker, language, speech, spans = list(hallucinator.speech_span)) - to_chat(hallucinator, message) + to_chat(target = hallucinator, text = message) return TRUE diff --git a/modular_darkpack/modules/quirks/code/quirks.dm b/modular_darkpack/modules/quirks/code/quirks.dm index 9a58b31c35c4..932adc99a81a 100644 --- a/modular_darkpack/modules/quirks/code/quirks.dm +++ b/modular_darkpack/modules/quirks/code/quirks.dm @@ -24,18 +24,17 @@ Dancer lose_text = "You don't feel rich anymore." /datum/quirk/broker/on_spawn() - if(!iswerewolf(quirk_holder)) - var/mob/living/carbon/human/H = quirk_holder - var/obj/item/stocks_license/pills = new() - pills.whose = H.real_name - pills.name = "[H.real_name]'s stocks trading license" - var/list/slots = list( - LOCATION_LPOCKET = ITEM_SLOT_LPOCKET, - LOCATION_RPOCKET = ITEM_SLOT_RPOCKET, - LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, - LOCATION_HANDS = ITEM_SLOT_HANDS - ) - H.equip_in_one_of_slots(pills, slots, FALSE) + var/mob/living/carbon/human/H = quirk_holder + var/obj/item/stocks_license/pills = new() + pills.whose = H.real_name + pills.name = "[H.real_name]'s stocks trading license" + var/list/slots = list( + LOCATION_LPOCKET = ITEM_SLOT_LPOCKET, + LOCATION_RPOCKET = ITEM_SLOT_RPOCKET, + LOCATION_BACKPACK = ITEM_SLOT_BACKPACK, + LOCATION_HANDS = ITEM_SLOT_HANDS + ) + H.equip_in_one_of_slots(pills, slots, FALSE) /datum/quirk/annonymus name = "Anonymous" @@ -45,9 +44,8 @@ Dancer lose_text = "You don't feel anonymous anymore." /datum/quirk/annonymus/on_spawn() - if(!iswerewolf(quirk_holder)) - var/mob/living/carbon/human/H = quirk_holder - H.equip_to_slot_or_del(new /obj/item/clothing/mask/vampire/balaclava(H), ITEM_SLOT_MASK) + var/mob/living/carbon/human/H = quirk_holder + H.equip_to_slot_or_del(new /obj/item/clothing/mask/vampire/balaclava(H), ITEM_SLOT_MASK) /datum/quirk/bloody_lover name = "Bloody Lover" @@ -105,16 +103,15 @@ Dancer lose_text = "You feel both of your arms again." /datum/quirk/one_hand/on_spawn() - if(!iswerewolf(quirk_holder)) - var/mob/living/carbon/human/H = quirk_holder - var/obj/item/bodypart/B1 = H.get_bodypart(BODY_ZONE_R_ARM) - var/obj/item/bodypart/B2 = H.get_bodypart(BODY_ZONE_L_ARM) - if(prob(50)) - B1.drop_limb() - qdel(B1) - else - B2.drop_limb() - qdel(B2) + var/mob/living/carbon/human/H = quirk_holder + var/obj/item/bodypart/B1 = H.get_bodypart(BODY_ZONE_R_ARM) + var/obj/item/bodypart/B2 = H.get_bodypart(BODY_ZONE_L_ARM) + if(prob(50)) + B1.drop_limb() + qdel(B1) + else + B2.drop_limb() + qdel(B2) /datum/quirk/non_int name = "Non Intellectual" @@ -203,8 +200,6 @@ Dancer lose_text = "You don't feel short anymore." /datum/quirk/dwarf/add() - if(iswerewolf(quirk_holder)) - return quirk_holder.AddElement(/datum/element/dwarfism, COMSIG_PARENT_PREQDELETED, src) /datum/quirk/dwarf/remove() @@ -288,8 +283,6 @@ Dancer allowed_splats = list("Vampire", "Ghoul") /datum/quirk/hunted/on_spawn() - if(iswerewolf(quirk_holder) || isgarou(quirk_holder)) - return if(isturf(quirk_holder.loc)) SSbloodhunt.announce_hunted(quirk_holder, "Camarilla Wanted List") @@ -305,8 +298,6 @@ Dancer quirk_holder.become_nearsighted(ROUNDSTART_TRAIT) /datum/quirk/badvision/on_spawn() - if(iswerewolf(quirk_holder)) - return var/mob/living/carbon/human/H = quirk_holder var/obj/item/clothing/glasses/vampire/perception/glasses = new(get_turf(H)) if(!H.equip_to_slot_if_possible(glasses, ITEM_SLOT_EYES, bypass_equip_delay_self = TRUE)) @@ -345,8 +336,6 @@ Dancer lose_text = "You don't feel tall anymore." /datum/quirk/tower/add() - if(iswerewolf(quirk_holder)) - return quirk_holder.AddElement(/datum/element/giantism, COMSIG_PARENT_PREQDELETED, src) /datum/quirk/tower/remove() diff --git a/modular_darkpack/modules/radios/code/radio.dm b/modular_darkpack/modules/radios/code/radio.dm index a69e5ef9d386..d9f99acfdb46 100644 --- a/modular_darkpack/modules/radios/code/radio.dm +++ b/modular_darkpack/modules/radios/code/radio.dm @@ -17,6 +17,17 @@ set_frequency(FREQ_MILITARY) radio_id = rand(1, 999) // Since we wont have a tranceiver for these, we're just auto-assigning a random ID. This isn't foolproof. +/obj/item/radio/headset/darkpack/pmc + name = "military radio" + radio_network = NETWORK_ENDRON + +/obj/item/radio/headset/darkpack/pmc/Initialize() + . = ..() + set_frequency(FREQ_ENDRON) + AddElement(/datum/element/earhealing) + AddComponent(/datum/component/wearertargeting/earprotection) //Allows for explosive fuckups to not fuck up communication + radio_id = rand(1, 999) // Since we wont have a tranceiver for these, we're just auto-assigning a random ID. This isn't foolproof. + // Police Radio get a special button to call for backup. /obj/item/radio/headset/darkpack/police name = "police radio" diff --git a/modular_darkpack/modules/radios/code/transceiver.dm b/modular_darkpack/modules/radios/code/transceiver.dm index 81dd77d36dde..14d836426413 100644 --- a/modular_darkpack/modules/radios/code/transceiver.dm +++ b/modular_darkpack/modules/radios/code/transceiver.dm @@ -30,7 +30,7 @@ if(CONFIG_GET(flag/punishing_zero_dots) && user.st_get_stat(STAT_TECHNOLOGY) < 1) to_chat(user, span_warning("You don't know how to operate this!")) - return ITEM_INTERACT_FAILURE + return ITEM_INTERACT_BLOCKING var/obj/item/radio/headset/darkpack/radio = tool if(radio.radio_id) @@ -45,10 +45,10 @@ else var/input_number = tgui_input_number(user = user, message = "Set Radio ID", title = "Enter a numerical ID to use for this network.", max_value = 999, min_value = 1, round_value = TRUE) if(!input_number) - return ITEM_INTERACT_FAILURE + return ITEM_INTERACT_BLOCKING if("[input_number]" in connected_radios) to_chat(user, span_warning("A radio with that ID is already connected to this network!")) - return ITEM_INTERACT_FAILURE + return ITEM_INTERACT_BLOCKING join_network(radio, input_number) playsound(src, 'modular_darkpack/modules/radios/sounds/radio_on.ogg', 60, TRUE) to_chat(user, span_notice("You link the [radio] to the [radio_network].")) diff --git a/modular_darkpack/modules/retail/code/stores/hot_ishu.dm b/modular_darkpack/modules/retail/code/stores/hot_ishu.dm index 9dc9793f197d..6b53ad5418ce 100644 --- a/modular_darkpack/modules/retail/code/stores/hot_ishu.dm +++ b/modular_darkpack/modules/retail/code/stores/hot_ishu.dm @@ -2,21 +2,29 @@ products_list = list( new /datum/data/vending_product("\"Vase\"", /obj/item/bong, 50), new /datum/data/vending_product("cross", /obj/item/card/hunter, 25), - new /datum/data/vending_product("backpack", /obj/item/storage/backpack, 100), - new /datum/data/vending_product("black grunge outfit", /obj/item/clothing/under/vampire/black, 20), - new /datum/data/vending_product("burlesque outfit", /obj/item/clothing/under/vampire/burlesque, 15), - new /datum/data/vending_product("female gimp outfit", /obj/item/clothing/under/vampire/nosferatu/female, 15), - new /datum/data/vending_product("female punk attire ", /obj/item/clothing/under/vampire/brujah/female, 20), - new /datum/data/vending_product("gimp outfit", /obj/item/clothing/under/vampire/nosferatu, 15), - new /datum/data/vending_product("gothic clothes", /obj/item/clothing/under/vampire/gothic, 20), - new /datum/data/vending_product("gothic attire", /obj/item/clothing/under/vampire/brujah, 20), - new /datum/data/vending_product("grimey pants", /obj/item/clothing/under/vampire/malkavian, 20), - //new /datum/data/vending_product("hoodie", /obj/item/clothing/suit/hooded/hoodie, 15), - //new /datum/data/vending_product("intruder pim hoodie", /obj/item/clothing/suit/hooded/hoodie/hoodie_pim, 20), - new /datum/data/vending_product("jacket", /obj/item/clothing/suit/vampire/jacket, 15), - new /datum/data/vending_product("odd Goth schoolgirl attire", /obj/item/clothing/under/vampire/malkavian/female, 20), - new /datum/data/vending_product("pentagram shirt", /obj/item/clothing/under/vampire/baali, 20), - new /datum/data/vending_product("pentagram skirt", /obj/item/clothing/under/vampire/baali/female, 20), - new /datum/data/vending_product("punk outfit", /obj/item/clothing/under/vampire/emo, 20), - new /datum/data/vending_product("red hipster outfit", /obj/item/clothing/under/vampire/red, 20), + new /datum/data/vending_product("backpack", /obj/item/storage/backpack, 100), + new /datum/data/vending_product("black grunge outfit", /obj/item/clothing/under/vampire/black, 20), + new /datum/data/vending_product("burlesque outfit", /obj/item/clothing/under/vampire/burlesque, 15), + new /datum/data/vending_product("female gimp outfit", /obj/item/clothing/under/vampire/nosferatu/female, 15), + new /datum/data/vending_product("female punk attire ", /obj/item/clothing/under/vampire/brujah/female, 20), + new /datum/data/vending_product("gimp outfit", /obj/item/clothing/under/vampire/nosferatu, 15), + new /datum/data/vending_product("gothic clothes", /obj/item/clothing/under/vampire/gothic, 20), + new /datum/data/vending_product("gothic attire", /obj/item/clothing/under/vampire/brujah, 20), + new /datum/data/vending_product("grimey pants", /obj/item/clothing/under/vampire/malkavian, 20), + new /datum/data/vending_product("popular outfit", /obj/item/clothing/under/vampire/scenepink, 20), + new /datum/data/vending_product("moody attire", /obj/item/clothing/under/vampire/scenemoody, 20), + new /datum/data/vending_product("pim attire", /obj/item/clothing/under/vampire/scenezim, 20), + new /datum/data/vending_product("revealing outfit", /obj/item/clothing/under/vampire/sceneleopard, 20), + new /datum/data/vending_product("hoodie", /obj/item/clothing/suit/hooded/hoodie, 15), + new /datum/data/vending_product("intruder pim hoodie", /obj/item/clothing/suit/hooded/hoodie/hoodie_pim, 20), + new /datum/data/vending_product("leopard coat", /obj/item/clothing/suit/vampire/coat/leopard, 35), + new /datum/data/vending_product("jacket", /obj/item/clothing/suit/vampire/jacket, 15), + new /datum/data/vending_product("odd Goth schoolgirl attire", /obj/item/clothing/under/vampire/malkavian/female, 20), + new /datum/data/vending_product("pentagram shirt", /obj/item/clothing/under/vampire/baali, 20), + new /datum/data/vending_product("pentagram skirt", /obj/item/clothing/under/vampire/baali/female, 20), + new /datum/data/vending_product("punk outfit", /obj/item/clothing/under/vampire/emo, 20), + new /datum/data/vending_product("red hipster outfit", /obj/item/clothing/under/vampire/red, 20), + new /datum/data/vending_product("black fur boots", /obj/item/clothing/shoes/vampire/blackfur, 35), + new /datum/data/vending_product("brown fur boots", /obj/item/clothing/shoes/vampire/brownfur, 35), + new /datum/data/vending_product("knee-high sneakers", /obj/item/clothing/shoes/vampire/pumped, 35), ) diff --git a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/comforting_darkness.dm b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/comforting_darkness.dm index 13ca050141f7..4e530e0e4d85 100644 --- a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/comforting_darkness.dm +++ b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/comforting_darkness.dm @@ -43,19 +43,19 @@ spent_points = tgui_input_list(invoker, "How many blood points would you like to spend? (60 healing per)", "Blood Points", bpoptions, null) if(!spent_points) return - invoker.bloodpool = max(invoker.bloodpool - spent_points, 0) + invoker.adjust_blood_pool(-spent_points) invoker.apply_status_effect(/datum/status_effect/blood_debt, 2 * spent_points) // Apply debuff with debt amount for(var/mob/living/carbon/human/target in heal_targets) target.heal_ordered_damage(60 * spent_points, list(BRUTE, TOX, OXY, STAMINA)) // Heals 2 levels of lethal/bashing per point spent target.heal_ordered_damage(30 * spent_points, list(BURN, AGGRAVATED)) // Heals aggravated at half effectiveness, TTRPG-inaccurate implementation but necessary else if(roll == 0) - invoker.bloodpool = max(invoker.bloodpool - 1, 0) + invoker.adjust_blood_pool(-1) qdel(src) else if(roll <= -1) to_chat(invoker, span_warning("You lose focus, failing to control the darkness as it burns you!")) - invoker.bloodpool = max(invoker.bloodpool - 1, 0) + invoker.adjust_blood_pool(-1) invoker.apply_damage(30, AGGRAVATED) qdel(src) diff --git a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/reflections_of_hollow_revelation.dm b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/reflections_of_hollow_revelation.dm index b19ce18fa9ed..131fd3f22189 100644 --- a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/reflections_of_hollow_revelation.dm +++ b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/reflections_of_hollow_revelation.dm @@ -65,7 +65,7 @@ var/datum/splat/vampire/vampire = does_use_disciplines(target) if(vampire?.get_discipline(/datum/discipline/obtenebration) || vampire?.get_discipline(/datum/discipline/auspex)) var/theirpower = (user.st_get_stat(STAT_PERCEPTION) + user.st_get_stat(STAT_OCCULT)) - if(SSroll.storyteller_roll(theirpower, 8, target, numerical = FALSE) == ROLL_SUCCESS) + if(SSroll.storyteller_roll(theirpower, 8, target) == ROLL_SUCCESS) to_chat(target, span_warning("You notice the nearby shadows flicker... something is watching you.")) shadowview(target, user) diff --git a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/shadow_guardian.dm b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/shadow_guardian.dm index b4a9e09da9ce..c4d10c611a62 100644 --- a/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/shadow_guardian.dm +++ b/modular_darkpack/modules/ritual_abyss_mysticism/code/rituals/shadow_guardian.dm @@ -9,7 +9,7 @@ /obj/ritual_rune/abyss/heart_that_beats_in_silence/complete() var/mob/living/carbon/human/H = last_activator - var/roll = SSroll.storyteller_roll(last_activator.st_get_stat(STAT_INTELLIGENCE) + last_activator.st_get_stat(STAT_OCCULT), 6, last_activator, numerical = FALSE) + var/roll = SSroll.storyteller_roll(last_activator.st_get_stat(STAT_INTELLIGENCE) + last_activator.st_get_stat(STAT_OCCULT), 6, last_activator) last_activator.apply_damage(30, AGGRAVATED) switch(roll) diff --git a/modular_darkpack/modules/ritual_abyss_mysticism/code/status_effects/blood_debt.dm b/modular_darkpack/modules/ritual_abyss_mysticism/code/status_effects/blood_debt.dm index d5ba071ee812..7fbce92fd314 100644 --- a/modular_darkpack/modules/ritual_abyss_mysticism/code/status_effects/blood_debt.dm +++ b/modular_darkpack/modules/ritual_abyss_mysticism/code/status_effects/blood_debt.dm @@ -19,7 +19,7 @@ if(blood_gained > 0) var/payment = min(blood_gained, debt_amount) - owner.bloodpool -= payment + owner.adjust_blood_pool(-payment) debt_amount -= payment initial_bloodpool = owner.bloodpool diff --git a/modular_darkpack/modules/round_status/code/pref_code.dm b/modular_darkpack/modules/round_status/code/pref_code.dm index 3a8dbf209171..4abe484e5e14 100644 --- a/modular_darkpack/modules/round_status/code/pref_code.dm +++ b/modular_darkpack/modules/round_status/code/pref_code.dm @@ -1,8 +1,31 @@ +/// Preliminary wrapper for the prefs write_preference_midround that ensures your acctually on the same character as the one you spawned in as. +/mob/living/carbon/human/proc/write_preference_midround(datum/preference/preference, preference_value) + if(!(client?.prefs)) + return FALSE + if(!(mind?.original_character_slot_index)) + return FALSE + + var/mob/living/carbon/human/original_human = mind.original_character.resolve() + + if(!original_human || (original_human != src)) + return FALSE + + return client.prefs.write_preference_midround(GLOB.preference_entries[preference], preference_value, src) + /// Wrapper for write_preference to prevent writing for non-canon events like EORG /// Returns TRUE for a successful preference application. /// Returns FALSE if it is invalid or the round was not canon. -/datum/preferences/proc/write_preference_midround(datum/preference/preference, preference_value) +/datum/preferences/proc/write_preference_midround(datum/preference/preference, preference_value, mob/user) + var/reason if(!GLOB.canon_event) - to_chat(parent, span_warning("Cannot save preference: [preference.savefile_key]; current round is not canon.")) + reason = "current round is not canon." + else if(!("[default_slot]" in parent.persistent_client.joined_as_slots)) + reason = "selected character sheet not spawned in." + + if(reason) + to_chat(parent, span_warning("Cannot save preference: [preference.savefile_key]; [reason]")) + user.log_message("failed to write pref: [preference.savefile_key] due to; [reason]", LOG_STATS) return FALSE + + user.log_message("wrote to pref '[preference.savefile_key]' midround. set to '[preference_value]'", LOG_STATS) return write_preference(preference, preference_value) diff --git a/modular_darkpack/modules/sabbat/code/sabbat_blood_bath.dm b/modular_darkpack/modules/sabbat/code/sabbat_blood_bath.dm index e8c5d7737ead..81c0189f293e 100644 --- a/modular_darkpack/modules/sabbat/code/sabbat_blood_bath.dm +++ b/modular_darkpack/modules/sabbat/code/sabbat_blood_bath.dm @@ -34,8 +34,8 @@ icon = 'modular_darkpack/modules/decor/icons/bathroom.dmi' icon_state = "tub" -/obj/structure/bath/sabbatbath/attackby(obj/item/W, mob/living/carbon/user, params) - if(istype(W, /obj/item/sabbat_priest_tome)) +/obj/structure/bath/sabbatbath/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if(istype(tool, /obj/item/sabbat_priest_tome)) if(user.mind && is_sabbat_priest(user) && has_buckled_mobs()) var/mob/living/buckled_mob = buckled_mobs[1] if(buckled_mob.mind) @@ -60,19 +60,19 @@ to_chat(sabbat_member, span_cult("[buckled_mob] has been anointed as the new Ductus of the pack!")) to_chat(buckled_mob, span_cult("You have been anointed as the new Ductus of the pack!")) - return - if(istype(W, /obj/item/knife/vamp)) + return ITEM_INTERACT_SUCCESS + if(istype(tool, /obj/item/knife/vamp)) playsound(loc,'sound/items/weapons/bladeslice.ogg', 50, FALSE) if(do_after(user, 100)) if(user.bloodpool <= 0) to_chat(user, span_warning("You have no blood to donate!")) - return + return ITEM_INTERACT_BLOCKING user.visible_message(span_notice("[user] cuts [user.p_their()] wrist and lets blood flow into the bath."), span_notice("You cut your wrist and let blood flow into the bath.")) var/amount_to_donate = min(user.bloodpool, 3) - user.bloodpool -= amount_to_donate + user.adjust_blood_pool(-amount_to_donate) blood_level = min(blood_level + amount_to_donate, max_blood) reagents.add_reagent(/datum/reagent/blood, amount_to_donate) @@ -82,21 +82,22 @@ update_icon() - return TRUE + return ITEM_INTERACT_SUCCESS else to_chat(user, span_warning("You decide not to add your blood to the bathtub...")) + return ITEM_INTERACT_BLOCKING // Handle vaulderie goblet specifically so that the Priest can use the tub's blood for vaulderie (part of the blood bath rite) - if(istype(W, /obj/item/reagent_containers/cup/silver_goblet/vaulderie_goblet)) - var/obj/item/reagent_containers/cup/silver_goblet/vaulderie_goblet/goblet = W + if(istype(tool, /obj/item/reagent_containers/cup/silver_goblet/vaulderie_goblet)) + var/obj/item/reagent_containers/cup/silver_goblet/vaulderie_goblet/goblet = tool if(blood_level <= 0) to_chat(user, span_warning("The bath is empty.")) - return + return ITEM_INTERACT_BLOCKING var/transfer_amount = min(goblet.volume - goblet.reagents.total_volume, blood_level) if(transfer_amount <= 0) to_chat(user, span_warning("The goblet is already full.")) - return + return ITEM_INTERACT_BLOCKING user.visible_message(span_notice("[user] scoops blood from the bath into [goblet]."), span_notice("You scoop blood from the bath into [goblet].")) @@ -109,9 +110,7 @@ if(blood_level <= 0) update_icon() - return TRUE - - return ..() + return ITEM_INTERACT_SUCCESS /obj/structure/bath/sabbatbath/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) . = ..() diff --git a/modular_darkpack/modules/splats/code/gaining_splats.dm b/modular_darkpack/modules/splats/code/gaining_splats.dm index f97ddda6bb89..ef1318f31f2c 100644 --- a/modular_darkpack/modules/splats/code/gaining_splats.dm +++ b/modular_darkpack/modules/splats/code/gaining_splats.dm @@ -40,6 +40,9 @@ on_gain() + if(owner.hud_used) + add_relevent_huds(owner.hud_used) + return src /** diff --git a/modular_darkpack/modules/splats/code/hud_managment.dm b/modular_darkpack/modules/splats/code/hud_managment.dm new file mode 100644 index 000000000000..fc10092f0515 --- /dev/null +++ b/modular_darkpack/modules/splats/code/hud_managment.dm @@ -0,0 +1,7 @@ +// Called in both the inital hud init and when gaining a splat. To try and ensure its always added. +// Make sure to verify a hud element doesnt already exist as a result. +/datum/splat/proc/add_relevent_huds(datum/hud/hud_used) + return + +/datum/splat/proc/update_relevent_huds() + return diff --git a/modular_darkpack/modules/splats/code/powers/_power.dm b/modular_darkpack/modules/splats/code/powers/_power.dm new file mode 100644 index 000000000000..10480f589040 --- /dev/null +++ b/modular_darkpack/modules/splats/code/powers/_power.dm @@ -0,0 +1,8 @@ +/datum/action/cooldown/power + cooldown_time = 1 TURNS // Good default. + + /// The level/rank at which this power is taken or can be taken at. + var/rank = 0 + // Not used presently but good future proofing incase behavoirs care. + /// Means that this action is not a real power, but some sort of innate ability we represent as a power/disc/gift mechnaicly. + var/innate_ability = FALSE diff --git a/modular_darkpack/modules/splats/code/st_power_management.dm b/modular_darkpack/modules/splats/code/powers/st_power_management.dm similarity index 100% rename from modular_darkpack/modules/splats/code/st_power_management.dm rename to modular_darkpack/modules/splats/code/powers/st_power_management.dm diff --git a/modular_darkpack/modules/splats/code/splat_life.dm b/modular_darkpack/modules/splats/code/splat_life.dm new file mode 100644 index 000000000000..2dfadf83a40c --- /dev/null +++ b/modular_darkpack/modules/splats/code/splat_life.dm @@ -0,0 +1,2 @@ +/datum/splat/proc/splat_life(seconds_per_tick) + return diff --git a/modular_darkpack/modules/splats/code/splat_management.dm b/modular_darkpack/modules/splats/code/splat_management.dm index da555294f827..5120616ed64f 100644 --- a/modular_darkpack/modules/splats/code/splat_management.dm +++ b/modular_darkpack/modules/splats/code/splat_management.dm @@ -39,6 +39,15 @@ return FALSE +/** + * Clears all the splats from the mob/living + * Mostly used in unit tests + */ +/mob/living/proc/clear_splats() + if(splats) + for(var/datum/splat/splat_in_list in splats) + remove_splat(splat_in_list) + /** * Returns if the given splat type can be added to the mob or not. * Incompatibilities are due to an existing splat clashing with it or the given diff --git a/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm b/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm new file mode 100644 index 000000000000..cabfe7a58c23 --- /dev/null +++ b/modular_darkpack/modules/splats/code/subsplat/_subsplat.dm @@ -0,0 +1,28 @@ +// DARKPACK TODO - This should include clans. + +/** + * # Subsplat + * + * Represents the "splats" described in splatbooks, e.g. clans, tribes, factions and kiths. + * These are the "real" splats but we call them subsplats as we used that type path for what WW calls supernaturals + * A more official term sometimes used, especially for games in the Chronicles of Darkness, is "character axis". + * + * These at present are only stored on splats as a var for a given subtype and requires mostly implementing its own handling for everything. + */ +/datum/subsplat + abstract_type = /datum/subsplat + + /// Name of the splat + var/name + /// Description of what the splat is and what it does + var/desc + /// ID for trait sources and whatnot + var/id + +/datum/subsplat/proc/on_gain(datum/splat/gaining, joining_round) + SHOULD_CALL_PARENT(TRUE) + return + +/datum/subsplat/proc/on_lose() + SHOULD_CALL_PARENT(TRUE) + return diff --git a/modular_darkpack/modules/storyteller_dice/code/roll_datum.dm b/modular_darkpack/modules/storyteller_dice/code/roll_datum.dm new file mode 100644 index 000000000000..1d2620324593 --- /dev/null +++ b/modular_darkpack/modules/storyteller_dice/code/roll_datum.dm @@ -0,0 +1,219 @@ +/datum/storyteller_roll + var/bumper_text = "roll" + + var/difficulty = 6 + var/successes_needed = 1 + + // By default uses the highest attribute and ability // Not acctually true yet, it just used all of them. But it should be that. + var/list/applicable_stats = list() + var/numerical = FALSE + + var/roll_output_type = ROLL_PUBLIC + /// This is a roll that can proc multiple times in rapid sucession and thus has weaker or less notible outputs (forced runechat and quieter dice rolls) + var/spammy_roll = FALSE + + /// A lazy list of times indexed by a weakref to a mob + var/list/mobs_last_rolled + var/reroll_cooldown + + // Mutable vars to store the outputs of any given roll. Expect everything past here to be mutated between each roll. + var/last_sucess_amount + var/list/last_output_text = list() + + +/** + * Rolls a number of dice according to Storyteller system rules to find + * success or number of successes. + * + * Rolls a number of 10-sided dice, counting them as a "success" if + * they land on a number equal to or greater than the difficulty. Dice + * that land on 1 subtract a success from the total, and the minimum + * difficulty is 2. The number of successes is returned if numerical + * is true, or the roll outcome (botch, failure, success) as a defined + * number if false. + * + * Arguments: + + * * roller - the mob who is making the role and owns the dice + * * target - who this dice is being rolled against, can be the roller, determines if its considered "important" to the mob to display. + * * dice - bonus dice that are added to the roll. + * + * Returns: The sucess of the roll, either a define or the raw amount of sucesses if `numerical = TRUE` + */ +/datum/storyteller_roll/proc/st_roll(mob/living/roller, atom/target, bonus = 0) + last_sucess_amount = 0 + last_output_text = list() + + if(!can_roll(roller)) + return ROLL_FAILURE + + var/dice_amount = calculate_used_dice(roller, bonus) + + var/list/rolled_dice = roll_dice(dice_amount) + + var/first_line = "[span_tooltip(show_rolling_with(roller, bonus), "[dice_amount] dice")] vs. difficulty [difficulty]." + if(successes_needed > 1) + first_line += " [successes_needed] successes needed." + last_output_text += span_notice(first_line) + + last_sucess_amount = count_success(rolled_dice, difficulty, last_output_text) + var/output = roll_result(last_sucess_amount) + + var/title + if(roll_output_type in list(ROLL_PRIVATE_ADMIN, ROLL_ADMIN)) + title = "[ADMIN_LOOKUPFLW(roller)]" + else + title = "[roller]" + title += " - [bumper_text] [span_tinynoticeital(roll_output_type)]" + + var/output_combined = fieldset_block(title, jointext(last_output_text, "
"), "boxed_message") + for(var/mob/player_mob in get_mobs_to_show(roller)) + var/roll_important_to_me = FALSE + if(!spammy_roll && (player_mob == roller || target)) + roll_important_to_me = TRUE + + var/output_pref = player_mob.client?.prefs.read_preference(/datum/preference/choiced/dice_output) + + if(!spammy_roll && output_pref == DICE_OUTPUT_CHAT) + to_chat(player_mob, output_combined, MESSAGE_TYPE_INFO, trailing_newline = FALSE) + SEND_SOUND(player_mob, sound('sound/items/dice_roll.ogg', volume = roll_important_to_me ? 5 : 20)) + else if(spammy_roll || (output_pref == DICE_OUTPUT_BALLOON)) + if(last_sucess_amount > 0) + roller.balloon_alert(player_mob, "[last_sucess_amount]", TRUE) + else + roller.balloon_alert(player_mob, "[last_sucess_amount]", TRUE) + + LAZYADDASSOC(mobs_last_rolled, WEAKREF(roller), list(world.time, output)) + + return output + + +/datum/storyteller_roll/proc/get_mobs_to_show(mob/living/roller) + switch(roll_output_type) + if(ROLL_PUBLIC) + return viewers(DEFAULT_MESSAGE_RANGE, roller) + if(ROLL_PRIVATE) + return list(roller) + if(ROLL_PRIVATE_ADMIN) + return GLOB.admins + roller + if(ROLL_ADMIN) + return GLOB.admins + if(ROLL_NONE) + return // Not even important enough to be admin visable. + +/datum/storyteller_roll/proc/calculate_used_dice(mob/living/roller, bonus = 0) + var/dice_amount = 0 + for(var/stat_type in using_stats(roller)) + dice_amount += roller.st_get_stat(stat_type) + return dice_amount + bonus + +// Unused rn but can be used for overides of `using_stats()` +/datum/storyteller_roll/proc/return_higher_stat(mob/living/roller, list/stats) + var/stat_to_use + var/highest_stat + for(var/stat in stats) + var/stat_dots = roller.st_get_stat(stat) + if(isnull(highest_stat) || stat_dots > highest_stat) + stat_to_use = stat + highest_stat = stat_dots + return stat_to_use + +/datum/storyteller_roll/proc/using_stats(mob/living/roller) + return applicable_stats + +/datum/storyteller_roll/proc/show_rolling_with(mob/living/roller, bonus = 0) + var/output = "" + var/stuff = list() + for(var/datum/st_stat/stat_type as anything in using_stats(roller)) + stuff += "[LOWER_TEXT(stat_type::name)]:[roller.st_get_stat(stat_type)]" + output += jointext(stuff, "+") + if(bonus) + output += "+[bonus]" + return "Rolling [output]" + +/datum/storyteller_roll/proc/roll_dice(dice, sides = 10) + dice = max(dice, 1) + var/list/rolled_dice = list() + for(var/i in 1 to dice) + rolled_dice += rand(1, sides) + if(SSroll.on_crit_extra_die_enabled) + var/extra_dice = 0 + for(var/roll in rolled_dice) + if(roll == 10) + extra_dice++ + for(var/i in 1 to extra_dice) + rolled_dice += rand(1, sides) + return rolled_dice + +//Count the number of successes. +/datum/storyteller_roll/proc/count_success(list/rolled_dice, difficulty = 6, last_output_text) + var/sucess_amount = 0 + var/dice_text = "" + for(var/roll in rolled_dice) + if(roll >= difficulty) + dice_text += span_nicegreen("[get_dice_char(roll)]") + sucess_amount++ + if(SSroll.on_crit_extra_success_enabled && roll == 10) + sucess_amount++ + else if(roll == 1) + dice_text += span_bold(span_danger("[get_dice_char(roll)]")) + sucess_amount-- + else + dice_text += span_danger("[get_dice_char(roll)]") + last_output_text += "[roll_result_text(roll_result(sucess_amount))] [dice_text]" + return sucess_amount + +/datum/storyteller_roll/proc/roll_result(sucess_amount) + if(numerical) + return sucess_amount + else + if(sucess_amount < 0) + return ROLL_BOTCH + else if(sucess_amount < successes_needed) + return ROLL_FAILURE + else + return ROLL_SUCCESS + +/datum/storyteller_roll/proc/roll_result_text(success_result) + if(numerical) + return "[success_result] successes -" + else + switch(success_result) + if(ROLL_SUCCESS) + return span_nicegreen("Success -") + if(ROLL_FAILURE) + return span_danger("Failure -") + if(ROLL_BOTCH) + return span_bold(span_danger(("Botch -"))) + +/datum/storyteller_roll/proc/get_dice_char(input) + var/static/list/dice_output = list("❶", "❷", "❸", "❹", "❺", "❻", "❼", "❽", "❾", "❿") + return dice_output[input] + /* // This would require making it an assoc list and we dont every expect outside our given range. + // So if someone faces a runtime because of this just make it an actual assoc and deal with the micro preformace hit + var/static/alist/dice_output = alist(1 = "❶", 2 = "❷", 3 = "❸" ,4 = "❹", 5 = "❺", 6 = "❻", 7 = "❼", 8 = "❽", 9 = "❾", 10 = "❿") + if(!dice_output[input]) + return "⓿" + else + return dice_output[input] + */ + +/datum/storyteller_roll/proc/can_roll(mob/living/roller, feedback = TRUE) + if(reroll_cooldown && mobs_last_rolled) + for(var/datum/weakref/guy_ref, roll_info in mobs_last_rolled) + var/mob/living/guy = guy_ref.resolve() + if(!guy) + mobs_last_rolled.Remove(guy_ref) + continue + if(guy != roller) + continue + if(roll_info[1] + reroll_cooldown > world.time) + if(roll_info[2] > 0) + return TRUE + //return roll_info[2] // We really should support directly returning the output..? + if(feedback) + to_chat(roller, span_warning("You cannot reroll [bumper_text] yet. [round((roll_info[1] + reroll_cooldown - world.time)/10)]s left.")) + return FALSE + + return TRUE + diff --git a/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm b/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm index 01f80f17d3fb..9abca7539c78 100644 --- a/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm +++ b/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm @@ -23,150 +23,14 @@ SUBSYSTEM_DEF(roll) * Arguments: * * dice - number of 10-sided dice to roll. * * difficulty - the number that a dice must come up as to count as a success. - * * mobs_to_show_output - mobs shown the result - * * alert_atom - the atom over which balloon alerts should appear + * * roller - the mob who is making the role and owns the dice * * numerical - whether the proc returns number of successes or outcome (botch, failure, success) */ -/datum/controller/subsystem/roll/proc/storyteller_roll(dice = 1, difficulty = 6, list/mobs_to_show_output = list(), atom/alert_atom = null, numerical = FALSE) - var/list/rolled_dice = roll_dice(dice) - if(!islist(mobs_to_show_output)) - mobs_to_show_output = list(mobs_to_show_output) - var/list/output_text = list() - output_text += span_notice("Rolling [length(rolled_dice)] dice against difficulty [difficulty].\n") - var/success_count = count_success(rolled_dice, difficulty, output_text) - - var/output = roll_answer(success_count, numerical, output_text) - for(var/mob/player_mob as anything in mobs_to_show_output) - var/output_pref = player_mob.client?.prefs.read_preference(/datum/preference/choiced/dice_output) - - if(output_pref == DICE_OUTPUT_CHAT) - to_chat(player_mob, jointext(output_text, ""), trailing_newline = FALSE) - else if((output_pref == DICE_OUTPUT_BALLOON) && alert_atom) - if(success_count > 0) - alert_atom.balloon_alert(player_mob, "[success_count]", TRUE) - else - alert_atom.balloon_alert(player_mob, "[success_count]", TRUE) - - if(numerical) - return success_count - - return output - -/datum/controller/subsystem/roll/proc/opposed_roll(mob/player_a, mob/player_b, dice_a = 1, dice_b = 1, show_player_a=TRUE, show_player_b=TRUE, atom/alert_atom = null, draw_goes_to_b=TRUE, numerical=FALSE) - var/list/roll_a = roll_dice(dice_a) - var/list/roll_b = roll_dice(dice_b) - var/success_count_a = count_success_no_output(roll_a) - var/success_count_b = count_success_no_output(roll_b) - - var/player_a_succeeded = FALSE - if(success_count_a > success_count_b || (success_count_a == success_count_b && !draw_goes_to_b)) - player_a_succeeded = TRUE - - if(alert_atom) - var/is_zero = ((success_count_a - success_count_b) == 0) - if(show_player_a) - var/alert_text - if(player_a_succeeded) - alert_text = "[is_zero ?"":"+"][success_count_a - success_count_b]" - else - alert_text = "[success_count_a - success_count_b]" - alert_atom.balloon_alert(player_a, alert_text, TRUE) - if(show_player_b) - var/alert_text - if(!player_a_succeeded) - alert_text = "[is_zero ?"":"+"][success_count_b - success_count_a]" - else - alert_text = "[success_count_b - success_count_a]" - alert_atom.balloon_alert(player_b, alert_text, TRUE) - - if(numerical) - return success_count_a - success_count_b - else - return player_a_succeeded - -/datum/controller/subsystem/roll/proc/roll_dice(dice, sides = 10) - dice = max(dice, 1) - var/list/rolled_dice = list() - for(var/i in 1 to dice) - rolled_dice += rand(1, sides) - if(on_crit_extra_die_enabled) - var/extra_dice = 0 - for(var/roll in rolled_dice) - if(roll == 10) - extra_dice++ - for(var/i in 1 to extra_dice) - rolled_dice += rand(1, sides) - return rolled_dice - -//Count the number of successes. -/datum/controller/subsystem/roll/proc/count_success(list/rolled_dice, difficulty = 6, output_text) - var/success_count = 0 - for(var/roll in rolled_dice) - if(roll >= difficulty) - output_text += span_nicegreen("[get_dice_char(roll)]") - success_count++ - if(on_crit_extra_success_enabled) - if(roll == 10) - success_count++ - else if(roll == 1) - output_text += span_bold(span_danger("[get_dice_char(roll)]")) - success_count-- - else - output_text += span_danger("[get_dice_char(roll)]") - output_text += " " - return success_count - -//Count the number of successes. -/datum/controller/subsystem/roll/proc/count_success_no_output(list/rolled_dice, difficulty = 6) - var/success_count = 0 - for(var/roll in rolled_dice) - if(roll >= difficulty) - success_count++ - if(on_crit_extra_success_enabled) - if(roll == 10) - success_count++ - else if(roll == 1) - success_count-- - return success_count - -/datum/controller/subsystem/roll/proc/roll_answer(success_count, numerical, output_text) - if(numerical) - return success_count - else - if(success_count < 0) - output_text += span_bold(span_danger(("\n Botch!"))) - return ROLL_BOTCH - else if(success_count == 0) - output_text += span_danger("\n Failure!") - return ROLL_FAILURE - else - output_text += span_nicegreen("\n Success!") - return ROLL_SUCCESS - -/datum/controller/subsystem/roll/proc/get_dice_char(input) - switch(input) - if(1) - return "❶" - if(2) - return "❷" - if(3) - return "❸" - if(4) - return "❹" - if(5) - return "❺" - if(6) - return "❻" - if(7) - return "❼" - if(8) - return "❽" - if(9) - return "❾" - if(10) - return "❿" - else - return "⓿" +/datum/controller/subsystem/roll/proc/storyteller_roll(dice = 1, difficulty = 6, mob/living/roller = null, numerical = FALSE) + var/datum/storyteller_roll/dice_roll = new() + dice_roll.difficulty = difficulty + dice_roll.numerical = numerical + return dice_roll.st_roll(roller, roller, dice) //Config datums for exploding dice /datum/config_entry/flag/on_crit_additional_success diff --git a/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm b/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm new file mode 100644 index 000000000000..1bfa854c810d --- /dev/null +++ b/modular_darkpack/modules/storyteller_dice/code/roll_subtypes.dm @@ -0,0 +1,69 @@ +// Pretty generic ones for reuse if you dont really want/need a subtype +/datum/storyteller_roll/turn_cooldown + reroll_cooldown = 1 TURNS + +/datum/storyteller_roll/scene_cooldown + reroll_cooldown = 1 SCENES + +/datum/storyteller_roll/spammy + spammy_roll = TRUE + +// Mostly TTRPG accurate rolls + +// Physical Feats +/datum/storyteller_roll/lockpick + bumper_text = "lockpicking" + reroll_cooldown = 1 SCENES + applicable_stats = list(STAT_DEXTERITY, STAT_LARCENY) + +/datum/storyteller_roll/bash_door + bumper_text = "bash door" + reroll_cooldown = 1 SCENES + applicable_stats = list(STAT_STRENGTH) + numerical = TRUE + +/datum/storyteller_roll/grappling + bumper_text = "grappling" + applicable_stats = list(STAT_STRENGTH, STAT_BRAWL) + numerical = TRUE + spammy_roll = TRUE + +/datum/storyteller_roll/grappled + bumper_text = "resisting" + applicable_stats = list(STAT_STRENGTH, STAT_BRAWL) + numerical = TRUE + spammy_roll = TRUE + +/datum/storyteller_roll/climbing + bumper_text = "climbing" + applicable_stats = list(STAT_DEXTERITY, STAT_ATHLETICS) + +/datum/storyteller_roll/shooting + bumper_text = "shooting" + applicable_stats = list(STAT_DEXTERITY, STAT_FIREARMS) + reroll_cooldown = 1 TURNS + numerical = TRUE + +// DARKPACK TODO - (Attacks need a rework on how they calcuate landing a hit.) +/datum/storyteller_roll/punch + bumper_text = "punch" + applicable_stats = list(STAT_DEXTERITY, STAT_BRAWL) + spammy_roll = TRUE + +/datum/storyteller_roll/damage + bumper_text = "damage" + applicable_stats = list(STAT_STRENGTH) + numerical = TRUE + spammy_roll = TRUE + +// Mental Feats +/datum/storyteller_roll/investigation + bumper_text = "investigation" + applicable_stats = list(STAT_PERCEPTION, STAT_INVESTIGATION) + + +// Made up shittttt +/datum/storyteller_roll/identify_occult + bumper_text = "identify" + applicable_stats = list(STAT_INTELLIGENCE, STAT_OCCULT) + reroll_cooldown = 1 SCENES diff --git a/modular_darkpack/modules/storyteller_dice/code/verbs.dm b/modular_darkpack/modules/storyteller_dice/code/verbs.dm index c2cbcccf6600..bba04c26d80d 100644 --- a/modular_darkpack/modules/storyteller_dice/code/verbs.dm +++ b/modular_darkpack/modules/storyteller_dice/code/verbs.dm @@ -1,6 +1,50 @@ -ADMIN_VERB(roll_storyteller_dice, R_NONE, "Roll storyteller dice", "Roll storyteller dice at yourself.", ADMIN_CATEGORY_FUN) - var/dice_count = tgui_input_number(usr, "Input amount of dice to roll:", "Dice", 5, 100, 1) - var/difficulty = tgui_input_number(usr, "Input roll difficulty:", "Difficulty", 6, 10, 1) - - SSroll.storyteller_roll(dice_count, difficulty, usr, usr) +ADMIN_VERB_ONLY_CONTEXT_MENU(roll_storyteller_dice, R_FUN, "Roll storyteller dice", mob/living/M in world) + M.roll_dice_custom() BLACKBOX_LOG_ADMIN_VERB("Storyteller dice") + +/mob/living/verb/roll_dice_custom() + set name = "Roll custom dice" + set category = "IC" + set desc = "Roll dice!" + + var/list/allowed_stats = list() + // Blame Xeon im pretty sure for the mobs storing this as a string. + for(var/stat_path_as_a_fucked_up_evil_string, dots_in in storyteller_stats) + var/datum/st_stat/stat = text2path(stat_path_as_a_fucked_up_evil_string) + if(!ispath(stat)) + continue + if(stat == stat::abstract_type) + continue + allowed_stats += stat + //allowed_stats[stat] = "[stat::name]: [dots_in]" + var/list/stats_to_use = tgui_input_checkboxes(usr, "Select stats to use for the roll.", "Choose Stats", allowed_stats, min_checked = 0, max_checked = 5) + var/list/output_stats = list() + if(length(stats_to_use)) + for(var/list/stat as anything in stats_to_use) + output_stats += text2path(stat[1]) + + var/bonus_dice = tgui_input_number(usr, "Input amount of bonus dice to roll.", "Dice", 0, 20, -20) + if(isnull(bonus_dice)) + return + + var/difficulty = tgui_input_number(usr, "Input roll difficulty.", "Difficulty", 6, 10, 1) + if(isnull(difficulty)) + return + + var/successes_needed = tgui_input_number(usr, "Input successes required to pass.", "Successes Needed", 1, 20, 1) + if(isnull(successes_needed)) + return + + var/roll_type = tgui_input_list(usr, "Who do you want to roll to.", "Roll Type", list(ROLL_PUBLIC, ROLL_PRIVATE, ROLL_PRIVATE_ADMIN, ROLL_ADMIN), ROLL_PUBLIC) + if(isnull(roll_type)) + return + + var/datum/storyteller_roll/custom_roll/custom_roll = new() + custom_roll.applicable_stats = output_stats + custom_roll.difficulty = difficulty + custom_roll.successes_needed = successes_needed + custom_roll.roll_output_type = roll_type + return custom_roll.st_roll(src, src, bonus_dice) + +/datum/storyteller_roll/custom_roll + bumper_text = "custom roll" diff --git a/modular_darkpack/modules/storyteller_stats/code/logging.dm b/modular_darkpack/modules/storyteller_stats/code/logging.dm index cbb0f8b63597..6022c693cd5b 100644 --- a/modular_darkpack/modules/storyteller_stats/code/logging.dm +++ b/modular_darkpack/modules/storyteller_stats/code/logging.dm @@ -1,2 +1,8 @@ /proc/log_stats(text, list/data) logger.Log(LOG_CATEGORY_STATS, text, data) + +/datum/log_category/stats + category = LOG_CATEGORY_STATS + config_flag = /datum/config_entry/flag/log_stats + +/datum/config_entry/flag/log_stats diff --git a/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm b/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm index 1c0ea38d3bd1..4bf56f68f8b8 100644 --- a/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm +++ b/modular_darkpack/modules/storyteller_stats/code/mob_affecting_adjustments/mob_procs.dm @@ -1,9 +1,17 @@ -// Get a specific mob's stat from its stats list. +/// Get a specific mob's stat from its stats list. /mob/living/proc/st_get_stat(stat_path, include_bonus) var/datum/st_stat/given_stat = storyteller_stats["[stat_path]"] return given_stat?.get_score(include_bonus) -// Set a specific mob's stat from its stats list. +/// Wrapper for st_get_stat to reduce copypaste. Get a specific mob's stat from its stats list. +/mob/living/proc/st_get_stats(list/stat_list, include_bonus) + var/total_score = 0 + for(var/stat_path in stat_list) + var/datum/st_stat/given_stat = storyteller_stats["[stat_path]"] + total_score += given_stat?.get_score(include_bonus) + return total_score + +/// Set a specific mob's stat from its stats list. /mob/living/proc/st_set_stat(stat_path, amount) var/datum/st_stat/given_stat = storyteller_stats["[stat_path]"] return given_stat?.set_score(amount) diff --git a/modular_darkpack/modules/storyteller_stats/code/st_stats/default_abilities.dm b/modular_darkpack/modules/storyteller_stats/code/st_stats/default_abilities.dm index 501fca922bc3..458393434c59 100644 --- a/modular_darkpack/modules/storyteller_stats/code/st_stats/default_abilities.dm +++ b/modular_darkpack/modules/storyteller_stats/code/st_stats/default_abilities.dm @@ -109,7 +109,17 @@ /datum/st_stat/ability/computer subcategory = "Knowledges" name = "Computer" - description = "Affects your ability to use and interact with computerized devices. At 0 points, you won't be able to use a phone. At 1 point, you can use a phone, but not a computer." + /* V20 p. 108 + This Knowledge represents the ability to operate and program computers, including mobile devices. + Most Computer use also imparts a degree of Internet awareness (if not savvy). + */ + description = "Affects your ability to use and interact with computerized devices." + +// This kinda sucks dick to do for every stat. +/datum/st_stat/ability/computer/New() + . = ..() + if(CONFIG_GET(flag/punishing_zero_dots)) + description += " At 0 points, you won't be able to use a computer." /datum/st_stat/ability/finance subcategory = "Knowledges" @@ -149,4 +159,10 @@ /datum/st_stat/ability/technology subcategory = "Knowledges" name = "Technology" + /* V20 p. 110 + The Technology Knowledge represents a broad acumen with electronics, computer hardware, and devices more elaborate than “machines,” which fall under the Crafts Skill. + If it has a processor, a transistor, or an integrated circuit — if it’s electronic rather than electrical manipulating it uses the Technology Knowledge. + This is the wide-ranging Ability used to build one’s own computer, install (or subvert) a security system, repair a mobile phone, or kitbash a shortwave radio. + You must always choose a specialization in Technology, even though you possess some skill in multiple fields. + */ description = "Affects your character's familiarity with machines, devices, and electrical systems." diff --git a/modular_darkpack/modules/storyteller_stats/code/stat_pref_middleware.dm b/modular_darkpack/modules/storyteller_stats/code/stat_pref_middleware.dm index 135a2ae9c299..94421f8635bf 100644 --- a/modular_darkpack/modules/storyteller_stats/code/stat_pref_middleware.dm +++ b/modular_darkpack/modules/storyteller_stats/code/stat_pref_middleware.dm @@ -58,8 +58,8 @@ var/new_value = stat_path.get_score(include_bonus = FALSE) - var/log_text = "[key_name(user, TRUE, TRUE)] increased stat '[stat_path.name]' from [old_value] to [new_value]" - log_stats(log_text) + var/real_name = user.client.prefs.read_preference(/datum/preference/name/real_name) + user.log_message("increased stat '[stat_path.name]' from [old_value] to [new_value] on [real_name]", LOG_STATS) return TRUE /datum/preference_middleware/stats/proc/decrease_stat(list/params, mob/user) @@ -89,8 +89,8 @@ update_middleware_stats(preferences.preference_storyteller_stats) var/new_value = stat_path.get_score(include_bonus = FALSE) - var/log_text = "[key_name(user, TRUE, TRUE)] decreased stat '[stat_path.name]' from [old_value] to [new_value]" - log_stats(log_text) + var/real_name = user.client.prefs.read_preference(/datum/preference/name/real_name) + user.log_message("decreased stat '[stat_path.name]' from [old_value] to [new_value] on '[real_name]'", LOG_STATS) return TRUE /datum/preference_middleware/stats/proc/reset_stats(list/params, mob/user) @@ -100,8 +100,9 @@ to_chat(user, span_warning("You have to be in the main menu to adjust your stats.")) return FALSE - var/log_text = "[key_name(user, TRUE, TRUE)] reset all stats to default values" - log_stats(log_text) + var/real_name = user.client.prefs.read_preference(/datum/preference/name/real_name) + user.log_message("reset all stats to default values on '[real_name]'", LOG_STATS) + preferences.preference_storyteller_stats = null preferences.preference_storyteller_stats = create_new_stat_prefs(preferences.preference_storyteller_stats) return TRUE diff --git a/modular_darkpack/modules/umbra/code/minesweeper.dm b/modular_darkpack/modules/umbra/code/minesweeper.dm index d8b2102efa1a..4a6934c81698 100644 --- a/modular_darkpack/modules/umbra/code/minesweeper.dm +++ b/modular_darkpack/modules/umbra/code/minesweeper.dm @@ -1,3 +1,6 @@ +/datum/storyteller_roll/mindsweeper + applicable_stats = list(STAT_PERCEPTION, STAT_OCCULT) + /obj/minespot name = "safe umbral tether" desc = "Connects the parts of Penumbra together." @@ -35,7 +38,8 @@ icon_state = "boom" if(!dangerous) return - var/roll_result = SSroll.storyteller_roll(user.st_get_stat(STAT_PERCEPTION) + user.st_get_stat(STAT_OCCULT), 6, list(user), user) + var/datum/storyteller_roll/mindsweeper/perc_roll = new() + var/roll_result = perc_roll.st_roll(user, src) switch(roll_result) if(ROLL_SUCCESS) to_chat(user, span_revenwarning("Close... but the spirits do not punish you for this one.")) diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/blood_power.dm b/modular_darkpack/modules/vampire_the_masquerade/code/blood_power.dm index eccf09a22f21..b49b8a0ad234 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/blood_power.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/blood_power.dm @@ -6,9 +6,15 @@ background_icon = 'modular_darkpack/master_files/icons/mob/actions/backgrounds.dmi' background_icon_state = "bg_discipline" check_flags = AB_CHECK_HANDS_BLOCKED | AB_CHECK_IMMOBILE | AB_CHECK_LYING | AB_CHECK_CONSCIOUS - cooldown_time = 10 SECONDS + cooldown_time = 1 TURNS vampiric = TRUE + /// How much the ability costs to activate per "turn" + var/bp_per_turns = 1 + var/stat_buff_amount = 1 + // Activated for two "turns" as 5 seconds is acctually pretty short. Opens to door to let players set how long they are declaring it active for. + /// How many "turns" its activated for. Multiplies the blood cost. + var/turns_activated = 2 var/datum/armor/old_armor var/list/obj/item/bodypart/strengthened_limbs @@ -26,42 +32,45 @@ return FALSE var/mob/living/carbon/human/human_owner = owner - var/cost = HAS_TRAIT(human_owner, TRAIT_HUNGRY) ? 3 : 2 - if (human_owner.bloodpool < cost) + if (human_owner.bloodpool < current_bp_cost(human_owner)) if (feedback) SEND_SOUND(human_owner, sound('modular_darkpack/modules/deprecated/sounds/need_blood.ogg', volume = 75)) owner.balloon_alert(owner, "not enough BLOOD!") return FALSE /datum/action/cooldown/blood_power/Activate(mob/living/target) - cooldown_time = 10 SECONDS + target.discipline_time_plus + target.bloodpower_time_plus + cooldown_time = turns_activated TURNS . = ..() + if(!ishuman(owner)) + return var/mob/living/carbon/human/human_owner = owner - playsound(usr, 'modular_darkpack/modules/deprecated/sounds/bloodhealing.ogg', 50, FALSE) + playsound(usr, 'modular_darkpack/modules/vampire_the_masquerade/sounds/bloodhealing.ogg', 50, FALSE) to_chat(human_owner, span_notice("You use blood to become more powerful.")) - for (var/obj/item/bodypart/limb in human_owner.bodyparts) - limb.unarmed_damage_low += 5 - limb.unarmed_damage_high += 5 - LAZYADD(strengthened_limbs, limb) - + // DARKPACK TODO - This can be represented by having stam do anything old_armor = human_owner.physiology.armor human_owner.physiology.armor = old_armor.generate_new_with_modifiers(list(MELEE = 15, BULLET = 15)) - human_owner.st_add_stat_mod(STAT_STRENGTH, 2, "blood_power") - human_owner.st_add_stat_mod(STAT_DEXTERITY, 2, "blood_power") - human_owner.st_add_stat_mod(STAT_STAMINA, 2, "blood_power") + human_owner.st_add_stat_mod(STAT_STRENGTH, stat_buff_amount, "blood_power") + human_owner.st_add_stat_mod(STAT_DEXTERITY, stat_buff_amount, "blood_power") + human_owner.st_add_stat_mod(STAT_STAMINA, stat_buff_amount, "blood_power") - var/cost = HAS_TRAIT(owner, TRAIT_HUNGRY) ? 3 : 2 - human_owner.adjust_blood_pool(-cost) + human_owner.adjust_blood_pool(-current_bp_cost(human_owner)) ADD_TRAIT(human_owner, TRAIT_IGNORESLOWDOWN, MAGIC_TRAIT) addtimer(CALLBACK(src, PROC_REF(end_bloodpower)), cooldown_time) +/datum/action/cooldown/blood_power/proc/current_bp_cost(mob/living/carbon/human/human_owner) + var/cost = bp_per_turns * turns_activated * stat_buff_amount + if(HAS_TRAIT(human_owner, TRAIT_HUNGRY)) + cost = round(cost * 1.5) + cost = round(cost * human_owner.blood_efficiency) + return cost + /datum/action/cooldown/blood_power/proc/end_bloodpower() if (!owner || !ishuman(owner)) return @@ -69,11 +78,6 @@ var/mob/living/carbon/human/human_owner = owner to_chat(human_owner, span_warning("You feel like your BLOOD power slowly decreases.")) - for (var/obj/item/bodypart/limb in strengthened_limbs) - limb.unarmed_damage_low -= 5 - limb.unarmed_damage_high -= 5 - strengthened_limbs = null - human_owner.physiology.armor = old_armor human_owner.st_remove_stat_mod(STAT_STRENGTH, "blood_power") @@ -81,3 +85,32 @@ human_owner.st_remove_stat_mod(STAT_STAMINA, "blood_power") REMOVE_TRAIT(human_owner, TRAIT_IGNORESLOWDOWN, MAGIC_TRAIT) + +/datum/action/cooldown/blood_power/proc/set_usage() + var/turns = tgui_input_number(owner, "Set turns ([1 TURNS / 10] seconds per turn) to use blood for.", "Set Bloodpower Turns", turns_activated, TURNS_PER_SCENE, 1) + if(turns) + turns_activated = turns + var/datum/splat/vampire/kindred/kindred_splat = iskindred(owner) + if(!kindred_splat) + return + // Realising this is reimplenting very similar behavior to discs and could possibly just be typed under it. + var/max_buff_amount = kindred_splat.vitae_spending_rate + if(max_buff_amount <= 1) // No reason to prompt them if they only have one option + return + stat_buff_amount = clamp(stat_buff_amount, 0, max_buff_amount) + var/stat_buff_input = tgui_input_number(owner, "Set amount of dice to add for usage. (Max based on your generation)", "Set Buff Bonus", stat_buff_amount, max_buff_amount, 1) + if(stat_buff_input) + stat_buff_amount = stat_buff_input + +// DARKPACK TODO - (Refactor. Both this and discs should prob just have a subtype for /action_button) +/atom/movable/screen/movable/action_button/Click(location, control, params) + if(istype(linked_action, /datum/action/cooldown/blood_power)) + var/list/modifiers = params2list(params) + + //increase on right click, decrease on shift right click + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + var/datum/action/cooldown/blood_power/bp_action = linked_action + bp_action.set_usage() + return + //TODO: middle click to swap loadout + . = ..() diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/humanity.dm b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/humanity.dm index 3671ab7f0951..2adee483e833 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/humanity.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/humanity.dm @@ -34,9 +34,12 @@ //before going any further, roll either conscience or conviction to determine if we actually lose path/humanity if(humanity_change < 0) var/stat_to_roll = is_enlightenment ? STAT_CONVICTION : STAT_CONSCIENCE - var/degeneration_roll = SSroll.storyteller_roll(owner.st_get_stat(stat_to_roll), difficulty, owner, numerical = FALSE) + var/datum/storyteller_roll/degeneration_roll = new() + degeneration_roll.applicable_stats = list(stat_to_roll) + degeneration_roll.difficulty = difficulty + var/roll_result = degeneration_roll.st_roll(owner) - if(degeneration_roll == ROLL_SUCCESS) + if(roll_result == ROLL_SUCCESS) to_chat(owner, span_green("Your [is_enlightenment ? "conviction" : "conscience"] prevents you from losing [path] as you successfully justify your actions!")) return else diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/torpor.dm b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/torpor.dm index 64ba868c0fa3..861299439d40 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/kindred/torpor.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/kindred/torpor.dm @@ -14,6 +14,7 @@ var/torpor_time = (14 - morality_score) MINUTES COOLDOWN_START(vampirism, torpor_timer, torpor_time) // RegisterSignal(new_kindred, COMSIG_PATH_HIT, PROC_REF(adjust_morality)) + /mob/living/proc/cure_torpor(source, force) if(!HAS_TRAIT_FROM(src, TRAIT_TORPOR, source)) return diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/preferences/immortal_age.dm b/modular_darkpack/modules/vampire_the_masquerade/code/preferences/immortal_age.dm index 5d2a78d29029..c4f944b3be1d 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/preferences/immortal_age.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/preferences/immortal_age.dm @@ -1,5 +1,5 @@ /datum/preference/numeric/immortal_age - category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + category = PREFERENCE_CATEGORY_NON_CONTEXTUAL savefile_key = "immortal_age" savefile_identifier = PREFERENCE_CHARACTER priority = PREFERENCE_PRIORITY_TABLETOP diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm b/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm index 52aa8d79da7a..9569e1c51c06 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/splats/kindred_splat/kindred_splat.dm @@ -78,6 +78,8 @@ // Morality loss RegisterSignal(owner, COMSIG_PATH_HIT, PROC_REF(adjust_morality)) + RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(on_kindred_death)) + // Make all food except raw meat repulsive var/obj/item/organ/tongue/tongue = owner.get_organ_by_type(/obj/item/organ/tongue) tongue?.liked_foodtypes = NONE @@ -94,11 +96,14 @@ /datum/splat/vampire/kindred/on_lose() owner.set_clan(null) - UnregisterSignal(owner, COMSIG_CARBON_LOSE_ORGAN) - UnregisterSignal(owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION)) - UnregisterSignal(owner, COMSIG_MOB_VAMPIRE_SUCKED) - UnregisterSignal(owner, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS) - UnregisterSignal(owner, COMSIG_HUMAN_ON_HANDLE_BLOOD) + UnregisterSignal(owner, list( + COMSIG_CARBON_LOSE_ORGAN, + SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), + COMSIG_MOB_VAMPIRE_SUCKED, + COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, + COMSIG_HUMAN_ON_HANDLE_BLOOD, + COMSIG_LIVING_DEATH + )) // Reset tongue var/obj/item/organ/tongue/tongue = owner.get_organ_by_type(/obj/item/organ/tongue) @@ -185,6 +190,40 @@ return HANDLE_BLOOD_NO_NUTRITION_DRAIN|HANDLE_BLOOD_NO_OXYLOSS +/datum/splat/vampire/kindred/proc/on_kindred_death(mob/living/carbon/human/kindred, gibbed) + if(gibbed) + return + + kindred.can_be_embraced = FALSE + var/obj/item/organ/brain/brain = kindred.get_organ_slot(ORGAN_SLOT_BRAIN) //NO REVIVAL EVER + if(brain) + brain.organ_flags |= ORGAN_FAILING + + /* + if(HAS_TRAIT(src, TRAIT_IN_FRENZY)) + exit_frenzymod() + */ + SEND_SOUND(kindred, sound('modular_darkpack/modules/vampire_the_masquerade/sounds/final_death.ogg', volume = 50)) + + switch(kindred.chronological_age) + if(-INFINITY to 10) //normal corpse + return + if(10 to 50) + kindred.rot_body(1) //skin takes on a weird colouration + kindred.visible_message(span_notice("[kindred]'s skin loses some of its colour.")) + if(50 to 100) + kindred.rot_body(2) //looks slightly decayed + kindred.visible_message(span_notice("[kindred]'s skin rapidly decays.")) + if(100 to 150) + kindred.rot_body(3) //looks very decayed + kindred.visible_message(span_warning("[kindred]'s body rapidly decomposes!")) + if(150 to 200) + kindred.rot_body(4) //mummified skeletonised corpse + kindred.visible_message(span_warning("[kindred]'s body rapidly skeletonises!")) + if(200 to INFINITY) //turn to ash + playsound(kindred, 'modular_darkpack/modules/vampire_the_masquerade/sounds/burning_death.ogg', 80, TRUE) + kindred.dust(just_ash = TRUE, drop_items = TRUE, force = TRUE) + /datum/splat/vampire/kindred/vv_edit_var(var_name, var_value) switch (var_name) if (NAMEOF(src, generation)) diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_globals.dm b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_globals.dm index 864a5eb1dbbb..220d9e72d5f4 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_globals.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_globals.dm @@ -4,19 +4,12 @@ GLOBAL_LIST_INIT(vampire_clan_list, init_vampire_clan_list()) /proc/init_vampire_clan_list() var/list/clan_list = list() for (var/datum/vampire_clan/clan_type as anything in valid_subtypesof(/datum/vampire_clan)) - clan_list[initial(clan_type.name)] = clan_type + clan_list[clan_type::name] = clan_type clan_list = sort_list(clan_list) return clan_list /// Associative list of Clan typepaths to singletons -GLOBAL_LIST_INIT_TYPED(vampire_clans, /datum/vampire_clan, init_vampire_clans()) - -/proc/init_vampire_clans() - var/list/clan_list = list() - for (var/datum/vampire_clan/clan_type as anything in valid_subtypesof(/datum/vampire_clan)) - clan_list[clan_type] = new clan_type - clan_list = sort_list(clan_list) - return clan_list +GLOBAL_LIST_INIT_TYPED(vampire_clans, /datum/vampire_clan, init_subtypes_w_path_keys(/datum/vampire_clan, list())) /// All frenzied players GLOBAL_LIST_EMPTY(frenzy_list) diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_mark_pref.dm b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_mark_pref.dm index d18dbcf2eb41..90b12e5b1ca9 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_mark_pref.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clan_mark_pref.dm @@ -1,7 +1,7 @@ /datum/preference/external_choiced/clan_mark savefile_key = "clan_mark" savefile_identifier = PREFERENCE_CHARACTER - priority = PREFERENCE_PRIORITY_REQUIRES_CLAN + priority = PREFERENCE_PRIORITY_REQUIRES_SUBSPLAT category = PREFERENCE_CATEGORY_SECONDARY_FEATURES relevant_inherent_trait = TRAIT_VTM_CLANS diff --git a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clans/lasombra/lasombra.dm b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clans/lasombra/lasombra.dm index b454fddcf9bf..4a4465184637 100644 --- a/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clans/lasombra/lasombra.dm +++ b/modular_darkpack/modules/vampire_the_masquerade/code/vampire_clan/clans/lasombra/lasombra.dm @@ -23,3 +23,27 @@ H.vis_flags |= VIS_HIDE H.add_faction(VAMPIRE_CLAN_LASOMBRA) + +// Not TTRPG accurate and is pending a rework to use real rolls after #633 +/proc/scramble_lasombra_message(message, mob/living/lasombra) + var/static/list/zalgo_letters = list( + "̨a", "̡b", "̢c", "̷d", "̶e", "̸f", "̹g", "̺h", "̻i", + "̼j", "̽k", "̾l", "̿m", "͈n", "͍o", "͎p", "q", "͕r", + "͓s", "͒t", "̱u", "̲v", "̳w", "͡x", "̨y", "̨z", "̨A", + "̨B", "̨C", "̨D", "̨E", "̨F", "̨G", "̨H", "̨I", "̨J", + "̨K", "̨L", "̨M", "̨N", "̨O", "̨P", "̨Q", "̨R", "̨S", + "̨T", "̨U", "̨V", "̨W", "̨X", "̨Y", "̨Z" + ) + var/gibberish_message = "" + var/total_stats = 0 + if(istype(lasombra)) + total_stats = lasombra.st_get_stat(STAT_TECHNOLOGY) * 3 // +3% chance per tech. 15 max, 18 avg, 24 beauty.9 + for(var/i = 1 to length(message)) + var/char = message[i] + // Randomize or replace characters with gibberish + var/chance = 70 + total_stats // 70% + total_stats chance per point of social to keep intact. + if(prob(chance)) + gibberish_message += char + else + gibberish_message += pick(zalgo_letters) // Replace with random gibberish letters + return gibberish_message diff --git a/modular_darkpack/modules/deprecated/sounds/bloodhealing.ogg b/modular_darkpack/modules/vampire_the_masquerade/sounds/bloodhealing.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/bloodhealing.ogg rename to modular_darkpack/modules/vampire_the_masquerade/sounds/bloodhealing.ogg diff --git a/modular_darkpack/modules/vip_areas/code/vip_barrier_perm.dm b/modular_darkpack/modules/vip_areas/code/vip_barrier_perm.dm index 90e82b337528..f69962123032 100644 --- a/modular_darkpack/modules/vip_areas/code/vip_barrier_perm.dm +++ b/modular_darkpack/modules/vip_areas/code/vip_barrier_perm.dm @@ -88,11 +88,11 @@ var/mob/living/carbon/human/npc/bouncer/target_bouncer = pick(linked_bouncers) target_bouncer.speak_seldom(pick(target_bouncer.block_phrases), target_mob) -/datum/vip_barrier_perm/proc/notify_barrier_social_bypass(mob/user, mob/bouncer, used_badge) - if(!linked_barriers.len) +/datum/vip_barrier_perm/proc/notify_barrier_social_bypass(mob/user, mob/bouncer, used_badge, used_stat) + if(!length(linked_barriers) || !isliving(user)) return var/obj/effect/vip_barrier/target_barrier = linked_barriers[1] - target_barrier.handle_social_bypass(user, bouncer, used_badge) + target_barrier.handle_social_bypass(user, bouncer, used_badge, used_stat) //============================================================================= diff --git a/modular_darkpack/modules/vip_areas/code/vip_barriers/__vip_barrier.dm b/modular_darkpack/modules/vip_areas/code/vip_barriers/__vip_barrier.dm index d43c01dce6b8..dedecb303cea 100644 --- a/modular_darkpack/modules/vip_areas/code/vip_barriers/__vip_barrier.dm +++ b/modular_darkpack/modules/vip_areas/code/vip_barriers/__vip_barrier.dm @@ -3,11 +3,13 @@ desc = "Not a real checkpoint." icon = 'modular_darkpack/modules/vip_areas/icons/barrier.dmi' icon_state = "camarilla_blocking" + + anchored = TRUE + var/block_sound = "modular_darkpack/modules/deprecated/sounds/bouncer_blocked.ogg" //Social bypass numbers var/social_bypass_allowed = TRUE - var/social_bypass_time = 20 SECONDS var/can_use_badge = TRUE var/mean_to_cops = TRUE var/social_roll_difficulty = 7 @@ -15,9 +17,7 @@ //Display settings var/always_invisible = FALSE - density = FALSE - anchored = TRUE - + var/datum/storyteller_roll/scene_cooldown/bypass_roll //Assigns an ID to NPCs that guard certain doors, must match a barrier's ID @@ -101,7 +101,7 @@ /obj/effect/vip_barrier/proc/check_entry_permission_custom(mob/living/carbon/human/entering_mob) return TRUE -/obj/effect/vip_barrier/proc/handle_social_bypass(mob/living/carbon/human/user, mob/bouncer, used_badge = FALSE) +/obj/effect/vip_barrier/proc/handle_social_bypass(mob/living/carbon/human/user, mob/bouncer, used_badge = FALSE, used_stat = STAT_EMPATHY) if(user.get_face_name() == "Unknown") to_chat(user, span_notice("They won't talk to someone they can't look in the eye.")) @@ -119,18 +119,29 @@ linked_perm.notify_guard_blocked_denial(user) return - - if(!do_after(user, max(5 SECONDS, social_bypass_time - (user.st_get_stat(STAT_CHARISMA) * 2 SECONDS)), bouncer)) + if(!do_after(user, 1 TURNS, bouncer)) return - - var/involved_social_roll = social_roll_difficulty if(used_badge) involved_social_roll -= 1 - if(SSroll.storyteller_roll(user.st_get_stat(STAT_CHARISMA), involved_social_roll, mobs_to_show_output = user) == ROLL_SUCCESS) - to_chat(user, span_notice("You manage to persuade your way past the guards.")) + if(!bypass_roll) + bypass_roll = new() + bypass_roll.bumper_text = "persuade guard" + + var/verbage + bypass_roll.difficulty = involved_social_roll + bypass_roll.applicable_stats = list(STAT_CHARISMA) + if(used_stat == STAT_INTIMIDATION) + verbage = "intimidate" + bypass_roll.applicable_stats += used_stat + else + verbage = "persuade" + bypass_roll.applicable_stats += used_stat + + if(bypass_roll.st_roll(user, src) == ROLL_SUCCESS) + to_chat(user, span_notice("You manage to [verbage] your way past the guards.")) linked_perm.allow_list += user.get_face_name() return diff --git a/modular_darkpack/modules/vitae/code/blood_pack.dm b/modular_darkpack/modules/vitae/code/blood_pack.dm index f82f58723bba..f53ece7db5fc 100644 --- a/modular_darkpack/modules/vitae/code/blood_pack.dm +++ b/modular_darkpack/modules/vitae/code/blood_pack.dm @@ -31,16 +31,18 @@ inhand_icon_state = icon_state onflooricon_state = icon_state -/obj/item/reagent_containers/blood/attack(mob/living/M, mob/living/user) - . = ..() - if(!canconsume(M, user)) - return - if(!do_after(user, 3 SECONDS, M)) - return - reagents.trans_to(M, reagents.total_volume, transferred_by = user, methods = INGEST, show_message = FALSE) - playsound(M.loc, 'sound/items/drink.ogg', 50, TRUE) +/obj/item/reagent_containers/blood/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) + if(!ismob(interacting_with)) + return NONE + if(!canconsume(interacting_with, user)) + return ITEM_INTERACT_BLOCKING + if(!do_after(user, 3 SECONDS, interacting_with)) + return ITEM_INTERACT_BLOCKING + reagents.trans_to(interacting_with, reagents.total_volume, transferred_by = user, methods = INGEST, show_message = FALSE) + playsound(interacting_with.loc, 'sound/items/drink.ogg', 50, TRUE) update_appearance() - //SEND_SIGNAL(M, COMSIG_MASQUERADE_VIOLATION) + // SEND_SIGNAL(interacting_with, COMSIG_MASQUERADE_VIOLATION) - warning, interacting_with is the person who will be breaching, griefing potential here if a player walks up and feeds someone a bloodbag in public causing them to breach. resolve this prior to uncommenting + return ITEM_INTERACT_SUCCESS /obj/item/reagent_containers/blood/empty blood_type = null diff --git a/modular_darkpack/modules/vitae/code/embracing.dm b/modular_darkpack/modules/vitae/code/embracing.dm index 742487125462..28452f65ff07 100644 --- a/modular_darkpack/modules/vitae/code/embracing.dm +++ b/modular_darkpack/modules/vitae/code/embracing.dm @@ -37,7 +37,7 @@ //addtimer(CALLBACK(childe, PROC_REF(embrace_persistence_confirmation)), 1 SECONDS) -/* // DARKPACK TODO - GAROU +/* // DARKPACK TODO - WEREWOLF /mob/living/carbon/human/proc/attempt_abomination_embrace(mob/living/carbon/human/childe, second_party_embrace) if(!(childe.auspice?.level)) //here be Abominations return diff --git a/modular_darkpack/modules/vitae/code/vitae.dm b/modular_darkpack/modules/vitae/code/vitae.dm index a89949c17021..6dbfc41182d8 100644 --- a/modular_darkpack/modules/vitae/code/vitae.dm +++ b/modular_darkpack/modules/vitae/code/vitae.dm @@ -14,7 +14,7 @@ var/datum/weakref/embracer_weakref = data["donor"] var/mob/living/carbon/human/embracer = embracer_weakref?.resolve() - //if(isgarou(victim)) //Are we a garou species? DARKPACK TODO - GAROU + //if(isgarou(victim)) //Are we a garou species? DARKPACK TODO - WEREWOLF // attempt_abomination_embrace(childe) // victim.rollfrenzy() // return diff --git a/modular_darkpack/modules/walls/code/walls.dm b/modular_darkpack/modules/walls/code/walls.dm index 4aa641980faf..7a2513495fe0 100644 --- a/modular_darkpack/modules/walls/code/walls.dm +++ b/modular_darkpack/modules/walls/code/walls.dm @@ -76,18 +76,18 @@ if(user.body_position != STANDING_UP) return if(above_turf && istype(above_turf, /turf/open/openspace)) - var/total_dexterity = user.st_get_stat(STAT_DEXTERITY) - var/total_athletics = user.st_get_stat(STAT_ATHLETICS) to_chat(user, span_notice("You start climbing up...")) add_fingerprint(user) - var/result = do_after(user, (11 - total_dexterity - total_athletics) SECONDS, src) + var/result = do_after(user, 1 TURNS, src) if(!result || HAS_TRAIT(user, LEANING_TRAIT)) to_chat(user, span_notice("You were interrupted and failed to climb up.")) return //(Botch, slip and take damage), (Fail, fail to climb), (Success, climb up successfully) - var/roll = SSroll.storyteller_roll(total_dexterity+total_athletics, 6, user) + + var/datum/storyteller_roll/climbing/climb_roll = new() + var/roll = climb_roll.st_roll(user, src) switch(roll) if(ROLL_BOTCH) user.ZImpactDamage(loc, 1) diff --git a/modular_darkpack/modules/weapons/code/melee.dm b/modular_darkpack/modules/weapons/code/melee.dm index 3c2d839afd22..d4e078f62808 100644 --- a/modular_darkpack/modules/weapons/code/melee.dm +++ b/modular_darkpack/modules/weapons/code/melee.dm @@ -20,6 +20,7 @@ righthand_file = 'modular_darkpack/modules/deprecated/icons/righthand.dmi' worn_icon = 'modular_darkpack/modules/weapons/icons/worn_melee.dmi' ONFLOOR_ICON_HELPER('modular_darkpack/modules/weapons/icons/weapons_onfloor.dmi') + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT // Should really be suit storage pixel_w = -8 custom_price = 1800 @@ -32,6 +33,7 @@ worn_icon = 'modular_darkpack/modules/weapons/icons/worn_melee.dmi' ONFLOOR_ICON_HELPER('modular_darkpack/modules/weapons/icons/weapons_onfloor.dmi') pixel_w = -8 + custom_price = 1300 /obj/item/katana/vamp/Initialize(mapload) . = ..() @@ -148,6 +150,7 @@ . = ..() AddComponent(/datum/component/selling, 600, "longsword", FALSE) +// "Keepers" derived from "my brother's keeper" are an epithet for Lasombra but this seems to be a wholly unqiue item not found in any book. /obj/item/claymore/longsword/keeper name = "The Brother's Keeper" desc = "The ancient yet classic weapon of times gone, this is a longsword. This exemplar is surprisingly well taken care of, despite its age, to the point that whatever blood or vitae it may have drawn in the past is not visible at all, while still functioning as well as it first did however long ago. Upon the flat side of this blade, a simple well-worn inscription is engraved in Latin. 'In Death, I Rise.'" @@ -156,27 +159,15 @@ force = 50 block_chance = 45 armour_penetration = 40 - sharpness = SHARP_EDGED attack_verb_continuous = list("slashes", "cuts") attack_verb_simple = list("slash", "cut") hitsound = 'sound/items/weapons/rapierhit.ogg' wound_bonus = 5 - resistance_flags = FIRE_PROOF - masquerade_violating = FALSE //is_iron = FALSE DARKPACK TODO - Kiasyd -/obj/item/claymore/longsword/keeper/afterattack(atom/target, mob/living/carbon/user, proximity) +/obj/item/claymore/longsword/keeper/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) . = ..() - /* DARKPACK TODO - WEREWOLF - (this is a silver longsword) - if(iswerewolf(target) || isgarou(target) && proximity) - var/mob/living/carbon/M = target - if(M.auspice.gnosis) - if(prob(50)) - adjust_gnosis(-1, M) - - M.apply_damage(25, CLONE) - M.apply_status_effect(STATUS_EFFECT_SILVER_SLOWDOWN) - */ + fera_silver_damage(target, 5, 1) /obj/item/melee/baseball_bat/vamp name = "baseball bat" @@ -188,6 +179,7 @@ ONFLOOR_ICON_HELPER('modular_darkpack/modules/weapons/icons/weapons_onfloor.dmi') icon_state = "baseball" inhand_icon_state = "baseball" + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT // Should really be suit storage custom_price = 50 /obj/item/melee/baseball_bat/vamp/Initialize(mapload) @@ -328,6 +320,7 @@ righthand_file = 'modular_darkpack/modules/deprecated/icons/righthand.dmi' worn_icon = 'modular_darkpack/modules/weapons/icons/worn_melee.dmi' ONFLOOR_ICON_HELPER('modular_darkpack/modules/weapons/icons/weapons_onfloor.dmi') + slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT icon_state = "rock0" inhand_icon_state = "rock0" diff --git a/modular_darkpack/modules/weapons/code/pliers.dm b/modular_darkpack/modules/weapons/code/pliers.dm index d31ad116ecf3..2d07bb87fc3a 100644 --- a/modular_darkpack/modules/weapons/code/pliers.dm +++ b/modular_darkpack/modules/weapons/code/pliers.dm @@ -5,25 +5,15 @@ icon_state = "neat_ripper" lefthand_file = 'modular_darkpack/modules/weapons/icons/melee_lefthand.dmi' righthand_file = 'modular_darkpack/modules/weapons/icons/melee_righthand.dmi' - onflooricon_state = "neat_ripper" inhand_icon_state = "neat_ripper" toolspeed = 2 //isn't meant for cutting wires + slot_flags = NONE /// If pulling fangs lasts for the entire ROUND or not. var/permanent = TRUE - slot_flags = NONE - -/obj/item/wirecutters/pliers/bad_pliers - name = "pliers" - desc = "Meant for pulling wires but you could definetly crush something with these." - icon_state = "ripper" - onflooricon_state = "ripper" - inhand_icon_state = "ripper" - toolspeed = 1.2 //is an actual tool but can't actually cut - permanent = FALSE +/* DARKPACK TODO - MERITS/FLAWS related to feeding /obj/item/wirecutters/pliers/attack(mob/living/target, mob/living/user) . = ..() - /* DARKPACK TODO - Merits/Flaws related to feeding if(HAS_TRAIT(user, TRAIT_PACIFISM)) return if(HAS_TRAIT(target, TRAIT_BABY_TEETH)) @@ -42,4 +32,12 @@ visible_message(span_warning("[user] stuff's in Bone putty into [target] to stop their canines from regrowing!")) else target.apply_status_effect(STATUS_EFFECT_BABY_TEETH) - */ +*/ + +/obj/item/wirecutters/pliers/bad_pliers + name = "pliers" + desc = "Meant for pulling wires but you could definetly crush something with these." + icon_state = "ripper" + inhand_icon_state = "ripper" + toolspeed = 1.2 //is an actual tool but can't actually cut + permanent = FALSE diff --git a/modular_darkpack/modules/weapons/code/projectiles.dm b/modular_darkpack/modules/weapons/code/projectiles.dm index 49761bffd098..a80e7527a32f 100644 --- a/modular_darkpack/modules/weapons/code/projectiles.dm +++ b/modular_darkpack/modules/weapons/code/projectiles.dm @@ -15,18 +15,10 @@ /obj/projectile/bullet/darkpack/vamp9mm/silver name = "9mm silver bullet" -/* /obj/projectile/bullet/darkpack/vamp9mm/silver/on_hit(atom/target, blocked = 0, pierce_hit) . = ..() - if(iswerewolf(target) || isgarou(target)) - var/mob/living/carbon/M = target - if(M.auspice.gnosis) - if(prob(50)) - adjust_gnosis(-1, M) + fera_silver_damage(target, 2) - M.apply_damage(10, AGGRAVATED) - M.apply_status_effect(STATUS_EFFECT_SILVER_SLOWDOWN) -*/ // .45 ACP /obj/projectile/bullet/darkpack/vamp45acp name = ".45 ACP bullet" @@ -43,18 +35,10 @@ /obj/projectile/bullet/darkpack/vamp45acp/silver name = ".45 ACP silver bullet" -/* /obj/projectile/bullet/darkpack/vamp45acp/silver/on_hit(atom/target, blocked = 0, pierce_hit) . = ..() - if(iswerewolf(target) || isgarou(target)) - var/mob/living/carbon/M = target - if(M.auspice.gnosis) - if(prob(50)) - adjust_gnosis(-1, M) + fera_silver_damage(target, 3) - M.apply_damage(15, AGGRAVATED) - M.apply_status_effect(STATUS_EFFECT_SILVER_SLOWDOWN) -*/ // .44 Magnum /obj/projectile/bullet/darkpack/vamp44 name = ".44 bullet" @@ -67,18 +51,10 @@ name = ".44 silver bullet" //icon_state = "s44" -/* /obj/projectile/bullet/darkpack/vamp44/silver/on_hit(atom/target, blocked = 0, pierce_hit) . = ..() - if(iswerewolf(target) || isgarou(target)) - var/mob/living/carbon/M = target - if(M.auspice.gnosis) - if(prob(50)) - adjust_gnosis(-1, M) + fera_silver_damage(target, 4) - M.apply_damage(20, AGGRAVATED) - M.apply_status_effect(STATUS_EFFECT_SILVER_SLOWDOWN) -*/ // .50 BMG/AE /obj/projectile/bullet/darkpack/vamp50 name = ".50 BMG bullet" @@ -168,11 +144,10 @@ . = ..() if(iscarbon(target)) var/mob/living/carbon/hit_person = target - if(SSroll.storyteller_roll( - dice = hit_person.st_get_stat(STAT_STRENGTH) + min(hit_person.st_get_stat(STAT_DEXTERITY) + hit_person.st_get_stat(STAT_ATHLETICS)), - difficulty = 3 + (!isnull(firer) ? rand(1,2) : 0), - mobs_to_show_output = target - ) == ROLL_FAILURE) + var/datum/storyteller_roll/knockdown_roll = new() + knockdown_roll.applicable_stats = list(STAT_STRENGTH, STAT_DEXTERITY, STAT_ATHLETICS) + knockdown_roll.difficulty = 3 + (!isnull(firer) ? rand(1,2) : 0) + if(knockdown_roll.st_roll(target, firer ? firer : src) == ROLL_FAILURE) hit_person.Knockdown(20) to_chat(hit_person, span_danger("The force of a projectile sends you sprawling!")) diff --git a/modular_darkpack/modules/weapons/icons/worn_guns.dmi b/modular_darkpack/modules/weapons/icons/worn_guns.dmi index 7aee5d789094..911da95ca2bf 100644 Binary files a/modular_darkpack/modules/weapons/icons/worn_guns.dmi and b/modular_darkpack/modules/weapons/icons/worn_guns.dmi differ diff --git a/modular_darkpack/modules/weapons/icons/worn_melee.dmi b/modular_darkpack/modules/weapons/icons/worn_melee.dmi index 12c8f1edb932..feadb9fe5d05 100644 Binary files a/modular_darkpack/modules/weapons/icons/worn_melee.dmi and b/modular_darkpack/modules/weapons/icons/worn_melee.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/emotes.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/emotes.dm new file mode 100644 index 000000000000..24795cf5a1fa --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/emotes.dm @@ -0,0 +1,70 @@ +/datum/emote/living/growl + key = "growl" + key_third_person = "growls" + message = "growls!" + emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE + +/datum/emote/living/growl/get_sound(mob/living/carbon/human/user) + if(!istype(user)) + return + return user.dna.species.get_growl_sound(user) + +/// Returns the species' growl sound +/datum/species/proc/get_growl_sound(mob/living/carbon/human/human) + if(human.physique == FEMALE) + return 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/female_growl.ogg' + return 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/male_growl.ogg' + +/datum/species/human/shifter/war/get_growl_sound(mob/living/carbon/human/human) + return 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/crinos_growl.ogg' +/datum/species/human/shifter/dire/get_growl_sound(mob/living/carbon/human/human) + return 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/crinos_growl.ogg' +/datum/species/human/shifter/feral/get_growl_sound(mob/living/carbon/human/human) + return 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/lupus_growl.ogg' + +/* // DARKPACK TODO - CORAX +/datum/emote/living/caw + key = "caw" + key_third_person = "caws" +message = "caws!" + emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE + +/datum/emote/living/caw/get_sound(mob/living/carbon/human/user) + if(!istype(user)) + return + return user.dna.species.get_caw_sound(user) +*/ + +/datum/emote/living/howl + key = "howl" + key_third_person = "howls" + message = "howls!" + message_param = "howls for %t!" + emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE + +/datum/emote/living/howl/get_sound(mob/living/user) + var/static/list/howl_sounds = list( + 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo1.ogg', + 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo2.ogg', + ) + if(isdog(user) || istype(user, /mob/living/basic/mining/wolf)) + return pick(howl_sounds) + + if(isgarou(user)) + return pick(howl_sounds) + + if(user.is_clan(/datum/vampire_clan/gangrel)) + return pick(howl_sounds) + +/datum/emote/living/howl/get_range(mob/living/user) + if(HAS_TRAIT(user, TRAIT_LOUD_HOWLER)) + return 60 + + if(isdog(user) || istype(user, /mob/living/basic/mining/wolf)) + return 7 + + if(isgarou(user)) + return 15 + + if(user.is_clan(/datum/vampire_clan/gangrel)) + return 7 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/auspice.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/auspice.dm deleted file mode 100644 index 87462dda2a03..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/auspice.dm +++ /dev/null @@ -1,105 +0,0 @@ -/datum/auspice - var/name = "Loh" - var/desc = "Furry ebaka" - var/level = 1 - var/start_rage = 1 - var/rage = 1 - var/start_gnosis = 1 - var/gnosis = 1 - var/base_breed = "Homid" - var/tribe = "Wendigo" - var/list/gifts = list() - var/force_abomination = FALSE - - var/list/wendigo = list( - /datum/action/gift/stoic_pose = 1, - /datum/action/gift/freezing_wind = 2, - /datum/action/gift/bloody_feast = 3 - ) - - var/list/glasswalker = list( - /datum/action/gift/smooth_move = 1, - /datum/action/gift/digital_feelings = 2, - /datum/action/gift/elemental_improvement = 3 - ) - - var/list/spiral = list( - /datum/action/gift/stinky_fur = 1, - /datum/action/gift/venom_claws = 2, - /datum/action/gift/burning_scars = 3 - ) - -/datum/auspice/proc/on_gain(mob/living/carbon/C) - C.update_rage_hud() - C.transformator.lupus_form.auspice = src - C.transformator.lupus_form.dna = C.dna - C.transformator.crinos_form.auspice = src - C.transformator.crinos_form.dna = C.dna - rage = start_rage - if(length(gifts)) - for(var/i in gifts) - var/datum/action/A1 = new i() - A1.Grant(C) - var/datum/action/A2 = new i() - A2.Grant(C.transformator.lupus_form) - var/datum/action/A3 = new i() - A3.Grant(C.transformator.crinos_form) - - switch(tribe) - if("Glasswalkers") - for(var/i in 1 to level) - var/zalupa = glasswalker[i] - var/datum/action/A = new zalupa() - A.Grant(C) - var/datum/action/A1 = new zalupa() - A1.Grant(C.transformator.lupus_form) - var/datum/action/A2 = new zalupa() - A2.Grant(C.transformator.crinos_form) - if("Wendigo") - for(var/i in 1 to level) - var/zalupa = wendigo[i] - var/datum/action/A = new zalupa() - A.Grant(C) - var/datum/action/A1 = new zalupa() - A1.Grant(C.transformator.lupus_form) - var/datum/action/A2 = new zalupa() - A2.Grant(C.transformator.crinos_form) - if("Black Spiral Dancers") - for(var/i in 1 to level) - var/zalupa = spiral[i] - var/datum/action/A = new zalupa() - A.Grant(C) - var/datum/action/A1 = new zalupa() - A1.Grant(C.transformator.lupus_form) - var/datum/action/A2 = new zalupa() - A2.Grant(C.transformator.crinos_form) - -/datum/auspice/ahroun - name = "Ahroun" - desc = "The Ahroun is the archetype of the werewolf as murderous beast, though they range from unapologetic berserkers to hardened veterans tempering their Rage with discipline. Their high levels of Rage put them on the edge at all times - the Full Moon's blessing is a hair trigger, among other things. Those closer to the waxing moon tend to exult in the glory of the war, while those closer to the waning moon are more viciously pragmatic, ruthless in their bloodthirst. Every Ahroun is a dangerous individual to be around, but when the forces of the Wyrm attack, their packmates are glad to have a Full Moon warrior at the front of the charge." - start_rage = 5 - gifts = list(/datum/action/gift/falling_touch, /datum/action/gift/inspiration, /datum/action/gift/razor_claws) - -/datum/auspice/galliard - name = "Galliard" - desc = "Where the Philodox is stoic, the Galliard is a creature of unbridled passion. The Gibbous Moon is a fiery muse, and stirs its children into great heights and depths of emotion. While all Galliards are prone to immense mirth and immense melancholy, those born under a waning moon fall more readily into dark, consuming passions; they are the tragedians of the Garou, mastering tales of doom, ruin, sacrifice and loss. Conversely, their waxing-moon cousins sing of triumph and conquest, of the pounding heart and the love of life. They tend to be the soul of their pack's morale - when the Galliard is willing to go on, so too are all the others." - start_rage = 4 - gifts = list(/datum/action/gift/beast_speech, /datum/action/gift/call_of_the_wyld, /datum/action/gift/mindspeak) - -/datum/auspice/philodox - name = "Philodox" - desc = "Buried so heavily in his role as impartial judge and jury, the Philodox may seem aloof, even surprisingly cold-blooded for a werewolf. Those born under the waxing Half Moon may seem unusually serene and disaffected, their emotions only emerging when their Rage comes to a boil. The waning-moon Philodox is more incisive and judgmental, his all-seeing eye always carefully watching his packmates and colleagues for any departure from the expected. The Half Moons' opinions are somewhat feared, yet highly respected - a word of praise or condemnation means much coming from those born to see both sides of every struggle." - start_rage = 3 - gifts = list(/datum/action/gift/resist_pain, /datum/action/gift/scent_of_the_true_form, /datum/action/gift/truth_of_gaia) - -/datum/auspice/theurge - name = "Theurge" - desc = "The Crescent Moons can be strange and enigmatic, prone to falling into the convoluted symbolic logic of the spirits they truck with rather than the more familiar logic of humanity. Those Theurges born under the waning moon frequently have a harsher, more adversarial relationship with the spirit world - they tend to excel at binding and forcing spirits to their will, and are more vicious when battling spirits. Theurges born under the waxing moon tend to be more generous and open with the spirits, charming and cajoling rather than intimidating and threatening." - start_rage = 2 - gifts = list(/datum/action/gift/mothers_touch, /datum/action/gift/sense_wyrm, /datum/action/gift/spirit_speech) - -/datum/auspice/ragabash - name = "Ragabash" - desc = "The Ragabash born under the waxing new moon is usually light-hearted and capricious, while one born under the waning new moon has a slightly more wicked and ruthless streak. It's a rare Ragabash indeed that lacks a keen wit and the capacity to find some humor in any situation, no matter how bleak. Many other werewolves are slow to take the Ragabash seriously, though, as it's difficult to tell the difference between a New Moon's mockery that points out a grievous flaw in a plan and similar mockery that simply amuses him. Sometimes a Ragabash points out that the emperor has no clothes - but sometimes they're the first to cry wolf, so to speak." - start_rage = 1 - gifts = list(/datum/action/gift/blur_of_the_milky_eye, /datum/action/gift/open_seal, /datum/action/gift/infectious_laughter) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/garou_species.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/garou_species.dm deleted file mode 100644 index d4f498a15e00..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/garou_species.dm +++ /dev/null @@ -1,87 +0,0 @@ -/datum/species/garou - name = "Werewolf" - id = "garou" - default_color = "FFFFFF" - toxic_food = PINEAPPLE - species_traits = list(EYECOLOR, HAIR, FACEHAIR, LIPS, HAS_FLESH, HAS_BONE) - inherent_traits = list(TRAIT_ADVANCEDTOOLUSER, TRAIT_VIRUSIMMUNE, TRAIT_PERFECT_ATTACKER) - use_skintones = TRUE - limbs_id = "human" - wings_icon = "Dragon" - mutant_bodyparts = list("tail_human" = "None", "ears" = "None", "wings" = "None") - brutemod = 0.75 - heatmod = 1 - burnmod = 1 - dust_anim = "dust-h" - whitelisted = TRUE - selectable = TRUE - var/glabro = FALSE - -/datum/species/garou/on_species_gain(mob/living/carbon/human/C) - . = ..() -// ADD_TRAIT(C, TRAIT_NOBLEED, HIGHLANDER) - C.update_body(0) - C.last_experience = world.time+3000 - var/datum/action/gift/glabro/glabro = new() - glabro.Grant(C) - var/datum/action/gift/rage_heal/GH = new() - GH.Grant(C) - C.transformator = new(C) - C.transformator.human_form = C - - //garou resist vampire bites better than mortals - RegisterSignal(C, COMSIG_MOB_VAMPIRE_SUCKED, PROC_REF(on_garou_bitten)) - RegisterSignal(C.transformator.lupus_form, COMSIG_MOB_VAMPIRE_SUCKED, PROC_REF(on_garou_bitten)) - RegisterSignal(C.transformator.crinos_form, COMSIG_MOB_VAMPIRE_SUCKED, PROC_REF(on_garou_bitten)) - -/datum/species/garou/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) - . = ..() - UnregisterSignal(C, COMSIG_MOB_VAMPIRE_SUCKED) - UnregisterSignal(C.transformator.lupus_form, COMSIG_MOB_VAMPIRE_SUCKED) - UnregisterSignal(C.transformator.crinos_form, COMSIG_MOB_VAMPIRE_SUCKED) - for(var/datum/action/gift/G in C.actions) - G.Remove(C) - -/datum/species/garou/check_roundstart_eligible() - return FALSE - -/proc/adjust_rage(amount, mob/living/carbon/C, sound = TRUE) - if(amount > 0) - if(C.auspice.rage < 10) - if(sound) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/rage_increase.ogg', volume = 75)) - to_chat(C, span_userdanger("RAGE INCREASES")) - C.auspice.rage = min(10, C.auspice.rage+amount) - if(amount < 0) - if(C.auspice.rage > 0) - C.auspice.rage = max(0, C.auspice.rage+amount) - if(sound) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/rage_decrease.ogg', volume = 75)) - to_chat(C, span_userdanger("RAGE DECREASES")) - C.update_rage_hud() - -/proc/adjust_gnosis(amount, mob/living/carbon/C, sound = TRUE) - if(amount > 0) - if(C.auspice.gnosis < C.auspice.start_gnosis) - if(sound) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/humanity_gain.ogg', volume = 75)) - to_chat(C, span_boldnotice("GNOSIS INCREASES")) - C.auspice.gnosis = min(C.auspice.start_gnosis, C.auspice.gnosis+amount) - if(amount < 0) - if(C.auspice.gnosis > 0) - C.auspice.gnosis = max(0, C.auspice.gnosis+amount) - if(sound) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/rage_decrease.ogg', volume = 75)) - to_chat(C, span_boldnotice("GNOSIS DECREASES")) - C.update_rage_hud() - -/** - * On being bit by a vampire - * - * This handles vampire bite sleep immunity and any future special interactions. - */ -/datum/species/garou/proc/on_garou_bitten(datum/source, mob/living/carbon/being_bitten) - SIGNAL_HANDLER - - if(isgarou(being_bitten) || iswerewolf(being_bitten)) - return COMPONENT_RESIST_VAMPIRE_KISS diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/silver_slowdown.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/silver_slowdown.dm deleted file mode 100644 index 1d9f41e91a34..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/silver_slowdown.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/status_effect/silver_slowdown - id = "slowdown" - status_type = STATUS_EFFECT_REPLACE - duration = 5 SECONDS - -/datum/status_effect/silver_slowdown/on_apply() - . = ..() - var/mob/living/carbon/user = owner - user.add_movespeed_modifier(/datum/movespeed_modifier/silver_slowdown) - -/datum/status_effect/silver_slowdown/on_remove() - . = ..() - var/mob/living/carbon/user = owner - user.remove_movespeed_modifier(/datum/movespeed_modifier/silver_slowdown) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/transformation.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/transformation.dm deleted file mode 100644 index 3d6a546356d9..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/transformation.dm +++ /dev/null @@ -1,176 +0,0 @@ -/obj/werewolf_holder/transformation - var/mob/living/carbon/human/human_form - var/mob/living/carbon/werewolf/crinos/crinos_form - var/mob/living/carbon/werewolf/lupus/lupus_form - - var/transformating = FALSE - var/given_quirks = FALSE - -/obj/werewolf_holder/transformation/Initialize(mapload) - . = ..() - crinos_form = new() - crinos_form.transformator = src - lupus_form = new() - lupus_form.transformator = src - -/obj/werewolf_holder/transformation/proc/transfer_damage(mob/living/carbon/first, mob/living/carbon/second) - second.masquerade = first.masquerade - var/percentage = (100/first.maxHealth)*second.maxHealth - second.adjust_brute_loss(round((first.get_brute_loss()/100)*percentage)-second.get_brute_loss()) - second.adjust_fire_loss(round((first.get_fire_loss()/100)*percentage)-second.get_fire_loss()) - second.adjustToxLoss(round((first.get_tox_loss()/100)*percentage)-second.get_tox_loss()) - second.adjust_agg_loss(round((first.get_agg_loss()/100)*percentage)-second.get_agg_loss()) - -/obj/werewolf_holder/transformation/proc/trans_gender(mob/living/carbon/trans, form) - if(!given_quirks) - given_quirks = TRUE - if(trans.has_quirk(/datum/quirk/dancer)) - var/datum/action/dance/DA = new() - DA.Grant(lupus_form) - var/datum/action/dance/NE = new() - NE.Grant(crinos_form) - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - if(trans.auspice.rage == 0 && form != trans.auspice.base_breed) - to_chat(trans, "Not enough rage to transform into anything but [trans.auspice.base_breed].") - return - if(trans.in_frenzy) - to_chat(trans, "You can't transform while in frenzy.") - return - trans.inspired = FALSE - if(ishuman(trans)) - var/datum/species/garou/G = trans.dna.species - var/mob/living/carbon/human/H = trans - if(G.glabro) - H.remove_overlay(PROTEAN_LAYER) - G.punchdamagelow = G.punchdamagelow-15 - G.punchdamagehigh = G.punchdamagehigh-15 - H.st_remove_stat_mod(STAT_STRENGTH, "glabro_form") - H.physiology.armor.melee = H.physiology.armor.melee-15 - H.physiology.armor.bullet = H.physiology.armor.bullet-15 - var/matrix/M = matrix() - M.Scale(1) - H.transform = M - G.glabro = FALSE - H.update_icons() - switch(form) - if("Lupus") - if(iscrinos(trans)) - ntransform.Scale(0.75, 0.75) - if(ishuman(trans)) - ntransform.Scale(1, 0.75) - if("Crinos") - if(islupus(trans)) - ntransform.Scale(1.75, 1.75) - if(ishuman(trans)) - ntransform.Scale(1.25, 1.5) - if("Homid") - if(iscrinos(trans)) - ntransform.Scale(0.75, 0.5) - if(islupus(trans)) - ntransform.Scale(1, 1.5) - if(!transformating) - transformating = TRUE - switch(form) - if("Lupus") - if(trans == lupus_form) - transformating = FALSE - return - animate(trans, transform = ntransform, color = "#000000", time = 30) - playsound(get_turf(trans), 'modular_darkpack/modules/deprecated/sounds/transform.ogg', 50, FALSE) - for(var/mob/living/simple_animal/hostile/beastmaster/B in trans.beastmaster) - qdel(B) - spawn(30) - if(trans.stat == DEAD || !trans.client) // [ChillRaccoon] - preventing non-player transform issues - animate(trans, transform = null, color = "#FFFFFF") - return - var/items = trans.get_contents() - for(var/obj/item/item_worn in items) - if(!ismob(item_worn.loc)) - continue - trans.dropItemToGround(item_worn, TRUE) - var/current_loc = get_turf(trans) - lupus_form.color = "#000000" - lupus_form.forceMove(current_loc) - animate(lupus_form, color = "#FFFFFF", time = 10) - lupus_form.key = trans.key - forceMove(lupus_form) - lupus_form.bloodpool = trans.bloodpool - lupus_form.masquerade = trans.masquerade - lupus_form.nutrition = trans.nutrition - lupus_form.mind = trans.mind - lupus_form.update_blood_hud() - transfer_damage(trans, lupus_form) - lupus_form.add_movespeed_modifier(/datum/movespeed_modifier/lupusform) - trans.forceMove(src) - transformating = FALSE - animate(trans, transform = null, color = "#FFFFFF", time = 1) - lupus_form.update_icons() - if("Crinos") - if(trans == crinos_form) - transformating = FALSE - return - animate(trans, transform = ntransform, color = "#000000", time = 30) - playsound(get_turf(trans), 'modular_darkpack/modules/deprecated/sounds/transform.ogg', 50, FALSE) - for(var/mob/living/simple_animal/hostile/beastmaster/B in trans.beastmaster) - qdel(B) - spawn(30) - if(trans.stat == DEAD || !trans.client) // [ChillRaccoon] - preventing non-player transform issues - animate(trans, transform = null, color = "#FFFFFF") - return - var/items = trans.get_contents() - for(var/obj/item/item_worn in items) - if(!ismob(item_worn.loc)) - continue - trans.dropItemToGround(item_worn, TRUE) - var/current_loc = get_turf(trans) - crinos_form.color = "#000000" - crinos_form.forceMove(current_loc) - animate(crinos_form, color = "#FFFFFF", time = 10) - crinos_form.key = trans.key - forceMove(crinos_form) - crinos_form.bloodpool = trans.bloodpool - crinos_form.masquerade = trans.masquerade - crinos_form.nutrition = trans.nutrition - crinos_form.mind = trans.mind - crinos_form.update_blood_hud() - crinos_form.st_add_stat_mod(STAT_STRENGTH, 3, "crinos_form") - transfer_damage(trans, crinos_form) - crinos_form.add_movespeed_modifier(/datum/movespeed_modifier/crinosform) - trans.forceMove(src) - transformating = FALSE - animate(trans, transform = null, color = "#FFFFFF", time = 1) - crinos_form.update_icons() - if("Homid") - if(trans == human_form) - transformating = FALSE - return - animate(trans, transform = ntransform, color = "#000000", time = 30) - playsound(get_turf(trans), 'modular_darkpack/modules/deprecated/sounds/transform.ogg', 50, FALSE) - for(var/mob/living/simple_animal/hostile/beastmaster/B in trans.beastmaster) - qdel(B) - spawn(30) - if(trans.stat == DEAD || !trans.client) // [ChillRaccoon] - preventing non-player transform issues - animate(trans, transform = null, color = "#FFFFFF") - return - var/items = trans.get_contents() - for(var/obj/item/item_worn in items) - if(!ismob(item_worn.loc)) - continue - trans.dropItemToGround(item_worn, TRUE) - var/current_loc = get_turf(trans) - human_form.color = "#000000" - human_form.forceMove(current_loc) - animate(human_form, color = "#FFFFFF", time = 10) - human_form.key = trans.key - forceMove(human_form) - human_form.bloodpool = trans.bloodpool - human_form.masquerade = trans.masquerade - human_form.nutrition = trans.nutrition - human_form.mind = trans.mind - human_form.update_blood_hud() - transfer_damage(trans, human_form) - human_form.remove_movespeed_modifier(/datum/movespeed_modifier/crinosform) - human_form.remove_movespeed_modifier(/datum/movespeed_modifier/lupusform) - trans.forceMove(src) - transformating = FALSE - animate(trans, transform = null, color = "#FFFFFF", time = 1) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/life.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/life.dm deleted file mode 100644 index d50b76298918..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/life.dm +++ /dev/null @@ -1,131 +0,0 @@ -/mob/living/carbon/werewolf/Life() - update_icons() - update_rage_hud() - return..() - -/mob/living/carbon/Life() - . = ..() - if(isgarou(src) || iswerewolf(src)) - if(key && stat <= HARD_CRIT) - var/datum/preferences/P = GLOB.preferences_datums[ckey(key)] - if(P) - if(P.masquerade != masquerade) - P.masquerade = masquerade - P.save_preferences() - P.save_character() - - - if(stat != DEAD) - var/gaining_rage = TRUE - for(var/obj/structure/werewolf_totem/W in GLOB.totems) - if(W.totem_health) - if(W.tribe == auspice.tribe) - if(get_area(W) == get_area(src) && client) - gaining_rage = FALSE - if(last_gnosis_buff+300 < world.time) - last_gnosis_buff = world.time - adjust_gnosis(1, src, TRUE) - if(iscrinos(src)) - if(auspice.base_breed == "Crinos") - gaining_rage = FALSE - //else if(auspice.rage == 0) //! [ChillRaccoon] - FIXME - // transformator.trans_gender(src, auspice.base_breed) - if(islupus(src)) - if(auspice.base_breed == "Lupus") - gaining_rage = FALSE - //else if(auspice.rage == 0) - // transformator.trans_gender(src, auspice.base_breed) - if(ishuman(src)) - if(auspice.base_breed == "Homid") - gaining_rage = FALSE - //else if(auspice.rage == 0) - // transformator.trans_gender(src, auspice.base_breed) - - if(gaining_rage && client) - if((last_rage_gain + 1 MINUTES) < world.time) - last_rage_gain = world.time - adjust_rage(1, src, TRUE) - - if(masquerade == 0) - if(!is_special_character(src)) - if(auspice.gnosis) - to_chat(src, span_warning("My Veil is too low to connect with the spirits of Umbra!")) - adjust_gnosis(-1, src, FALSE) - - if(auspice.rage >= 9) - if(!in_frenzy) - if((last_frenzy_check + 40 SECONDS) <= world.time) - last_frenzy_check = world.time - rollfrenzy() - - if(istype(get_area(src), /area/vtm/outside/penumbra)) - if((last_veil_restore + 40 SECONDS) < world.time) - adjust_veil(1, src, TRUE) - last_veil_restore = world.time - - switch(auspice.tribe) - if("Wendigo") - if(istype(get_area(src), /area/vtm/forest)) - if((last_veil_restore + 50 SECONDS) <= world.time) - adjust_veil(1, src, TRUE) - last_veil_restore = world.time - - if("Glasswalkers") - if(istype(get_area(src), /area/vtm/interior/glasswalker)) - if((last_veil_restore + 50 SECONDS) <= world.time) - adjust_veil(1, src, TRUE) - last_veil_restore = world.time - - if("Black Spiral Dancers") - if(istype(get_area(src), /area/vtm/interior/endron_facility)) - if((last_veil_restore + 50 SECONDS) <= world.time) - adjust_veil(1, src, TRUE) - last_veil_restore = world.time - - -/mob/living/carbon/werewolf/crinos/Life() - . = ..() - if(CheckEyewitness(src, src, 5, FALSE)) - adjust_veil(-1) - -/mob/living/carbon/werewolf/check_breath(datum/gas_mixture/breath) - return - -/mob/living/carbon/werewolf/handle_status_effects() - . = ..() - //natural reduction of movement delay due to stun. - if(move_delay_add > 0) - move_delay_add = max(0, move_delay_add - rand(1, 2)) - -/mob/living/carbon/werewolf/handle_changeling() - return - -/mob/living/carbon/werewolf/handle_fire()//Aliens on fire code - . = ..() - if(.) //if the mob isn't on fire anymore - return - adjust_bodytemperature(BODYTEMP_HEATING_MAX) //If you're on fire, you heat up! - -/mob/living/carbon/proc/adjust_veil(var/amount) - if(last_veil_adjusting+200 >= world.time) - return - if(amount > 0) - if(HAS_TRAIT(src, TRAIT_VIOLATOR)) - return - if(amount < 0) - if(istype(get_area(src), /area/vtm)) - var/area/vtm/V = get_area(src) - if(V.zone_type != "masquerade") - return - last_veil_adjusting = world.time - if(!is_special_character(src)) - if(amount < 0) - if(masquerade > 0) - SEND_SOUND(src, sound('modular_darkpack/modules/deprecated/sounds/veil_violation.ogg', volume = 75)) - to_chat(src, span_boldnotice("VEIL VIOLATION")) - masquerade = max(0, masquerade+amount) - if(amount > 0) - if(masquerade < 5) - SEND_SOUND(src, sound('modular_darkpack/modules/deprecated/sounds/humanity_gain.ogg', volume = 75)) - to_chat(src, span_boldnotice("VEIL REINFORCEMENT")) - masquerade = min(5, masquerade+amount) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/lupus.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/lupus.dm deleted file mode 100644 index 47bd2fc3a811..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/lupus.dm +++ /dev/null @@ -1,73 +0,0 @@ -/mob/living/carbon/werewolf/lupus - name = "wolf" - icon_state = "black" - icon = 'code/modules/wod13/werewolf_lupus.dmi' - pass_flags = PASSTABLE - mob_size = MOB_SIZE_SMALL - butcher_results = list(/obj/item/food/meat/slab = 5) - possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) - hud_type = /datum/hud/werewolf - limb_destroyer = 1 - has_limbs = 0 -// dextrous = FALSE -// speed = -1.5 doesn't work on carbons -// var/move_delay_add = -1.5 // movement delay to add also didn't work - melee_damage_lower = 15 - melee_damage_upper = 35 - health = 150 - maxHealth = 150 - werewolf_armor = 10 -// bodyparts = list( -// /obj/item/bodypart/chest, -// /obj/item/bodypart/head, -// /obj/item/bodypart/r_arm, -// /obj/item/bodypart/l_arm, -// /obj/item/bodypart/r_leg, -// /obj/item/bodypart/l_leg, -// ) - var/hispo = FALSE - -/datum/movespeed_modifier/lupusform - multiplicative_slowdown = -0.80 - -/mob/living/carbon/werewolf/lupus/update_icons() - cut_overlays() - - var/laid_down = FALSE - - if(stat == UNCONSCIOUS || IsSleeping() || stat == HARD_CRIT || stat == SOFT_CRIT || IsParalyzed() || stat == DEAD || body_position == LYING_DOWN) - icon_state = "[sprite_color]_rest" - laid_down = TRUE - else - icon_state = "[sprite_color]" - - switch(get_fire_loss()+get_brute_loss()) - if(25 to 75) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage1[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - if(75 to 150) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage2[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - if(150 to INFINITY) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage3[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - - var/mutable_appearance/eye_overlay = mutable_appearance(icon, "eyes[laid_down ? "_rest" : ""]") - eye_overlay.color = sprite_eye_color - eye_overlay.plane = ABOVE_LIGHTING_PLANE - eye_overlay.layer = ABOVE_LIGHTING_LAYER - add_overlay(eye_overlay) - -/mob/living/carbon/werewolf/lupus/regenerate_icons() - if(!..()) - // update_icons() //Handled in update_transform(), leaving this here as a reminder - update_transform() - -/mob/living/carbon/werewolf/lupus/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that. - . = ..() - update_icons() - -/mob/living/carbon/werewolf/lupus/Life() - if(hispo) - CheckEyewitness(src, src, 7, FALSE) - . = ..() diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf.dm deleted file mode 100644 index 147c3341e3ce..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf.dm +++ /dev/null @@ -1,272 +0,0 @@ -/mob/living/carbon/werewolf - name = "werewolf" - icon = 'code/modules/wod13/werewolf.dmi' - gender = MALE - dna = null - faction = list(FACTION_GAIA) - ventcrawler = VENTCRAWLER_NONE - pass_flags = 0 -// sight = SEE_MOBS - see_in_dark = 2 - verb_say = "woofs" - rotate_on_lying = 0 - - movement_type = GROUND // [ChillRaccoon] - fucking flying werewolfes is a meme - - bloodpool = 20 - maxbloodpool = 20 - - var/move_delay_add = 0 // movement delay to add - - status_flags = CANUNCONSCIOUS|CANPUSH - - heat_protection = 0.5 // minor heat insulation - - var/leaping = FALSE - gib_type = /obj/effect/decal/cleanable/blood/gibs - unique_name = FALSE - var/environment_smash = ENVIRONMENT_SMASH_STRUCTURES - melee_damage_lower = 20 - melee_damage_upper = 20 - butcher_results = list(/obj/item/food/meat/slab = 5) - layer = LARGE_MOB_LAYER - var/obj_damage = 30 - var/wound_bonus = 20 - var/bare_wound_bonus = 25 - var/sharpness = 50 - var/armour_penetration = 100 - var/melee_damage_type = BRUTE - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, AGGRAVATED = 1, STAMINA = 0, OXY = 1) - var/attack_verb_continuous = "attacks" - var/attack_verb_simple = "attack" - var/friendly_verb_continuous = "nuzzles" - var/friendly_verb_simple = "nuzzle" - var/attack_sound = 'modular_darkpack/modules/deprecated/sounds/werewolf_bite.ogg' - - var/sprite_color = "black" - var/sprite_scar = 0 - var/sprite_hair = 0 - var/sprite_hair_color = "#000000" - var/sprite_eye_color = "#FFFFFF" - var/sprite_apparel = 0 - - var/step_variable = 0 - - var/werewolf_armor = 0 - - var/assigned_quirks = FALSE - -/mob/living/carbon/werewolf/update_resting() - if(resting) - ADD_TRAIT(src, TRAIT_IMMOBILIZED, RESTING_TRAIT) - else - REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, RESTING_TRAIT) - return ..() - -/mob/living/carbon/werewolf/crinos/Move(NewLoc, direct) - if(isturf(loc)) - step_variable = step_variable+1 - if(step_variable == 2) - step_variable = 0 - playsound(get_turf(src), 'modular_darkpack/modules/deprecated/sounds/werewolf_step.ogg', 50, FALSE) - . = ..() - -/mob/living/carbon/proc/epic_fall(var/apply_stun_self = TRUE, var/apply_stun_others = TRUE) - playsound(get_turf(src), 'modular_darkpack/modules/deprecated/sounds/werewolf_fall.ogg', 100, FALSE) - new /obj/effect/temp_visual/dir_setting/crack_effect(get_turf(src)) - new /obj/effect/temp_visual/dir_setting/fall_effect(get_turf(src)) - for(var/mob/living/carbon/C in range(5, src)) - if(apply_stun_others) - C.Stun(30) - shake_camera(C, (6-get_dist(C, src))+1, (6-get_dist(C, src))) - if(apply_stun_self) - Stun(20) - shake_camera(src, 5, 4) - -/mob/living/carbon/werewolf/Initialize(mapload) - var/datum/action/gift/rage_heal/GH = new() - GH.Grant(src) - add_verb(src, /mob/living/proc/mob_sleep) - add_verb(src, /mob/living/proc/toggle_resting) - - create_bodyparts() //initialize bodyparts - - create_internal_organs() - - ADD_TRAIT(src, TRAIT_NEVER_WOUNDED, ROUNDSTART_TRAIT) - - . = ..() - -/mob/living/carbon/werewolf/create_internal_organs() - internal_organs += new /obj/item/organ/brain - internal_organs += new /obj/item/organ/tongue - // DARKPACK TODO - Base type for night_vison eyes dont work. Make a subtype and fiddle with the values - internal_organs += new /obj/item/organ/eyes/night_vision - internal_organs += new /obj/item/organ/liver - internal_organs += new /obj/item/organ/stomach - internal_organs += new /obj/item/organ/heart - internal_organs += new /obj/item/organ/lungs - internal_organs += new /obj/item/organ/ears - ..() - -/mob/living/carbon/werewolf/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) // beepsky won't hunt aliums - return -10 - -/mob/living/carbon/werewolf/handle_environment(datum/gas_mixture/environment) - // Run base mob body temperature proc before taking damage - // this balances body temp to the environment and natural stabilization - . = ..() - - if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT) - //Body temperature is too hot. - throw_alert("alien_fire", /atom/movable/screen/alert/alien_fire) - switch(bodytemperature) - if(360 to 400) - apply_damage(HEAT_DAMAGE_LEVEL_1, BURN) - if(400 to 460) - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - if(460 to INFINITY) - if(on_fire) - apply_damage(HEAT_DAMAGE_LEVEL_3, BURN) - else - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - else - clear_alert("alien_fire") - -/mob/living/carbon/werewolf/reagent_check(datum/reagent/R) //can metabolize all reagents - return 0 - -/mob/living/carbon/werewolf/get_status_tab_items() - . = ..() - . += "Intent: [a_intent]" - -/mob/living/carbon/werewolf/getTrail() - return pick (list("trails_1", "trails2")) - -/mob/living/carbon/werewolf/canBeHandcuffed() - return FALSE - -/mob/living/carbon/werewolf/can_hold_items(obj/item/I) - return (I && (I.item_flags & WEREWOLF_HOLDABLE || ISADVANCEDTOOLUSER(src)) && ..()) - -/mob/living/carbon/werewolf/on_lying_down(new_lying_angle) - . = ..() - update_icons() - -/mob/living/carbon/werewolf/on_standing_up() - . = ..() - update_icons() - -///aliens are immune to stamina damage. -/mob/living/carbon/werewolf/adjust_stamina_loss(amount, updating_health = 1, forced = FALSE) - return FALSE - -///aliens are immune to stamina damage. -/mob/living/carbon/werewolf/setStaminaLoss(amount, updating_health = 1) - return FALSE - -/mob/living/carbon/werewolf/Stun(amount, ignore_canstun = FALSE) - . = ..() - if(!.) - move_delay_add = min(move_delay_add + round(amount / 2), 10) //a maximum delay of 10 - -/mob/living/carbon/werewolf/SetStun(amount, ignore_canstun = FALSE) - . = ..() - if(!.) - move_delay_add = min(move_delay_add + round(amount / 2), 10) - -/mob/living/carbon/werewolf/AdjustStun(amount, ignore_canstun = FALSE) - . = ..() - if(!.) - move_delay_add = clamp(move_delay_add + round(amount/2), 0, 10) - -/mob/living/carbon/werewolf/crinos - name = "werewolf" - icon_state = "black" - mob_size = MOB_SIZE_HUGE - butcher_results = list(/obj/item/food/meat/slab = 5) - possible_a_intents = list(INTENT_HELP, INTENT_DISARM, INTENT_GRAB, INTENT_HARM) - limb_destroyer = 1 - hud_type = /datum/hud/werewolf - melee_damage_lower = 35 - melee_damage_upper = 65 - health = 250 - maxHealth = 250 -// speed = -1 doesn't work on carbons - var/obj/item/r_store = null - var/obj/item/l_store = null - var/pounce_cooldown = 0 - var/pounce_cooldown_time = 30 - pixel_w = -8 -// deathsound = 'sound/voice/hiss6.ogg' - bodyparts = list( - /obj/item/bodypart/chest, - /obj/item/bodypart/head, - /obj/item/bodypart/l_arm, - /obj/item/bodypart/r_arm, - /obj/item/bodypart/r_leg, - /obj/item/bodypart/l_leg, - ) - - werewolf_armor = 30 - -/datum/movespeed_modifier/crinosform - multiplicative_slowdown = -0.2 - -/datum/movespeed_modifier/silver_slowdown - multiplicative_slowdown = 0.3 - -/mob/living/carbon/werewolf/crinos/Initialize(mapload) - . = ..() - var/datum/action/change_apparel/A = new() - A.Grant(src) -// AddComponent(/datum/component/footstep, FOOTSTEP_MOB_CLAW, 0.5, -11) - -/mob/living/carbon/werewolf/lupus/Initialize(mapload) - . = ..() - AddComponent(/datum/component/footstep, FOOTSTEP_MOB_CLAW, 0.5, -11) - var/datum/action/gift/hispo/hispo = new() - hispo.Grant(src) - -/mob/living/carbon/werewolf/crinos/show_inv(mob/user) - user.set_machine(src) - var/list/dat = list() - dat += "" - for(var/i in 1 to held_items.len) - var/obj/item/I = get_item_for_held_index(i) - dat += "" - dat += "" - dat += "" - - dat += {"
[get_held_index_name(i)]:[(I && !(I.item_flags & ABSTRACT)) ? I : "Empty"]
 
Empty Pouches
- Close - "} - - var/datum/browser/popup = new(user, "mob[REF(src)]", "[src]", 440, 510) - popup.set_content(dat.Join()) - popup.open() - - -/mob/living/carbon/werewolf/crinos/can_hold_items(obj/item/I) - return TRUE - -/mob/living/carbon/werewolf/crinos/Topic(href, href_list) - //strip panel - if(href_list["pouches"] && usr.canUseTopic(src, BE_CLOSE, NO_DEXTERITY)) - visible_message(span_danger("[usr] tries to empty [src]'s pouches."), \ - span_userdanger("[usr] tries to empty your pouches.")) - if(do_mob(usr, src, POCKET_STRIP_DELAY * 0.5)) - dropItemToGround(r_store) - dropItemToGround(l_store) - - . = ..() - -/mob/living/carbon/werewolf/crinos/resist_grab(moving_resist) - if(pulledby.grab_state) - visible_message(span_danger("[src] breaks free of [pulledby]'s grip!"), \ - span_danger("You break free of [pulledby]'s grip!")) - pulledby.stop_pulling() - . = 0 - -/mob/living/carbon/werewolf/crinos/get_permeability_protection(list/target_zones) - return 0.8 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_defense.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_defense.dm deleted file mode 100644 index 9af960232b13..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_defense.dm +++ /dev/null @@ -1,140 +0,0 @@ - -/mob/living/carbon/werewolf/get_eye_protection() - return ..() + 2 //potential cyber implants + natural eye protection - -/mob/living/carbon/werewolf/get_ear_protection() - return 2 //no ears - -/mob/living/carbon/werewolf/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - ..(AM, skipcatch = TRUE, hitpush = FALSE) - -/mob/living/carbon/werewolf/attack_hand(mob/living/carbon/human/M) - . = ..() - if(.) //to allow surgery to return properly. - return FALSE - - switch(M.a_intent) - if("help") - help_shake_act(M) - if("grab") - grabbedby(M) - if ("harm") - M.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - return TRUE - if("disarm") - M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - return TRUE - return FALSE - -/mob/living/carbon/werewolf/attack_animal(mob/living/simple_animal/M) - . = ..() - do_rage_from_attack() - if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - switch(M.melee_damage_type) - if(BRUTE) - adjust_brute_loss(damage) - if(BURN) - adjust_fire_loss(damage) - if(TOX) - adjustToxLoss(damage) - if(OXY) - adjust_oxy_loss(damage) - if(AGGRAVATED) - adjust_agg_loss(damage) - if(STAMINA) - adjust_stamina_loss(damage) - -/mob/living/carbon/werewolf/ex_act(severity, target, origin) - if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src)) - return - . = ..() - if(QDELETED(src)) - return - var/obj/item/organ/ears/ears = getorganslot(ORGAN_SLOT_EARS) - switch (severity) - if (EXPLODE_DEVASTATE) - gib() - return - - if (EXPLODE_HEAVY) - take_overall_damage(60, 60) - if(ears) - ears.adjustEarDamage(30,120) - - if(EXPLODE_LIGHT) - take_overall_damage(30,0) - if(prob(50)) - Unconscious(20) - if(ears) - ears.adjustEarDamage(15,60) - -/mob/living/carbon/werewolf/soundbang_act(intensity = 1, stun_pwr = 20, damage_pwr = 5, deafen_pwr = 15) - return 0 - -/mob/living/carbon/werewolf/acid_act(acidpwr, acid_volume) - return FALSE//aliens are immune to acid. - -/mob/living/carbon/werewolf/attack_hand(mob/living/carbon/human/M) - if(..()) - switch(M.a_intent) - if ("harm") - var/damage = rand(1, 9) - if (prob(90)) - playsound(loc, "punch", 25, TRUE, -1) - visible_message(span_danger("[M] punches [src]!"), \ - span_userdanger("[M] punches you!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), COMBAT_MESSAGE_RANGE, M) - to_chat(M, span_danger("You punch [src]!")) - if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down. - Unconscious(3 SECONDS) - visible_message(span_danger("[M] knocks [src] down!"), \ - span_userdanger("[M] knocks you down!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), null, M) - to_chat(M, span_danger("You knock [src] down!")) - var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected)) - apply_damage(damage, BRUTE, affecting) - log_combat(M, src, "attacked") - else - playsound(loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1) - visible_message(span_danger("[M]'s punch misses [src]!"), \ - span_danger("You avoid [M]'s punch!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, M) - to_chat(M, span_warning("Your punch misses [src]!")) - - if ("disarm") - if (body_position == STANDING_UP) - if (prob(5)) - Unconscious(3 SECONDS) - playsound(loc, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1) - log_combat(M, src, "pushed") - visible_message(span_danger("[M] pushes [src] down!"), \ - span_userdanger("[M] pushes you down!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), null, M) - to_chat(M, span_danger("You push [src] down!")) - else - if (prob(50)) - dropItemToGround(get_active_held_item()) - playsound(loc, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1) - visible_message(span_danger("[M] disarms [src]!"), \ - span_userdanger("[M] disarms you!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, M) - to_chat(M, span_danger("You disarm [src]!")) - else - playsound(loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1) - visible_message(span_danger("[M] fails to disarm [src]!"),\ - span_danger("[M] fails to disarm you!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, M) - to_chat(M, span_warning("You fail to disarm [src]!")) - - - -/mob/living/carbon/werewolf/crinos/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) - if(!no_effect && !visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_CLAW - . = ..() - -/mob/living/carbon/werewolf/lupus/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect) - if(!no_effect && !visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_BITE - . = ..() - -/mob/living/carbon/werewolf/getarmor(def_zone, type) - if(type == BRUTE) - return werewolf_armor - else - return 0 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_update_icons.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_update_icons.dm deleted file mode 100644 index 0e73f2c2627c..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou/werewolf_mobs/werewolf_update_icons.dm +++ /dev/null @@ -1,73 +0,0 @@ -/mob/living/carbon/werewolf/update_damage_overlays() //aliens don't have damage overlays. - return - -/mob/living/carbon/werewolf/update_body() // we don't use the bodyparts or body layers for aliens. - return - -/mob/living/carbon/werewolf/update_body_parts()//we don't use the bodyparts layer for aliens. - return - -/mob/living/carbon/werewolf/crinos/update_icons() - cut_overlays() - - var/laid_down = FALSE - - if(stat == UNCONSCIOUS || IsSleeping() || stat == HARD_CRIT || stat == SOFT_CRIT || IsParalyzed() || stat == DEAD || body_position == LYING_DOWN) - icon_state = "[sprite_color]_rest" - laid_down = TRUE - else - icon_state = "[sprite_color]" - var/mutable_appearance/shadow_overlay = mutable_appearance(icon, "undershadow") - shadow_overlay.pixel_z = -4 - shadow_overlay.alpha = 64 - shadow_overlay.layer = layer-1 - add_overlay(shadow_overlay) - - if(sprite_scar) - var/mutable_appearance/scar_overlay = mutable_appearance(icon, "scar[sprite_scar][laid_down ? "_rest" : ""]") - add_overlay(scar_overlay) - - switch(get_fire_loss()+get_brute_loss()) - if(25 to 100) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage1[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - if(100 to 250) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage2[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - if(250 to INFINITY) - var/mutable_appearance/damage_overlay = mutable_appearance(icon, "damage3[laid_down ? "_rest" : ""]") - add_overlay(damage_overlay) - - if(sprite_apparel) - switch(auspice.tribe) - if("Wendigo") - var/mutable_appearance/clothing_overlay = mutable_appearance(icon, "wendigo[sprite_apparel][laid_down ? "_rest" : ""]") - add_overlay(clothing_overlay) - if("Glasswalkers") - var/mutable_appearance/clothing_overlay = mutable_appearance(icon, "glasswalker[sprite_apparel][laid_down ? "_rest" : ""]") - add_overlay(clothing_overlay) - if("Black Spiral Dancers") - var/mutable_appearance/clothing_overlay = mutable_appearance(icon, "spiral[sprite_apparel][laid_down ? "_rest" : ""]") - add_overlay(clothing_overlay) - - if(sprite_hair) - var/mutable_appearance/hair_overlay = mutable_appearance(icon, "hair[sprite_hair][laid_down ? "_rest" : ""]") - hair_overlay.color = sprite_hair_color - add_overlay(hair_overlay) - - var/mutable_appearance/eye_overlay = mutable_appearance(icon, "eyes[laid_down ? "_rest" : ""]") - eye_overlay.color = sprite_eye_color - eye_overlay.plane = ABOVE_LIGHTING_PLANE - eye_overlay.layer = ABOVE_LIGHTING_LAYER - add_overlay(eye_overlay) - - update_inv_hands() - -/mob/living/carbon/werewolf/crinos/regenerate_icons() - if(!..()) - // update_icons() //Handled in update_transform(), leaving this here as a reminder - update_transform() - -/mob/living/carbon/werewolf/crinos/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that. - . = ..() - update_icons() diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/garou_languages.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou_languages.dm new file mode 100644 index 000000000000..7d66cf50a4db --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/garou_languages.dm @@ -0,0 +1,54 @@ +/datum/language_holder/garou + understood_languages = list( + /datum/language/common = list(LANGUAGE_ATOM), + /datum/language/garou_tongue = list(LANGUAGE_ATOM), + /datum/language/primal_tongue = list(LANGUAGE_ATOM), + ) + spoken_languages = list( + /datum/language/common = list(LANGUAGE_ATOM), + /datum/language/garou_tongue = list(LANGUAGE_ATOM), + ) + +/datum/language_holder/primal + understood_languages = list( + /datum/language/common = list(LANGUAGE_ATOM), + /datum/language/primal_tongue = list(LANGUAGE_ATOM), + ) + spoken_languages = list( + /datum/language/primal_tongue = list(LANGUAGE_ATOM), + ) + +/datum/language/garou_tongue + name = "Garou Tongue" + desc = "A guttural and pitchy language also known as 'High Tongue', the language of the Garou capable of being learned and spoken by Garou. It is hard to speak in human form." + key = "w" + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + space_chance = 40 + syllables = list( + "to", "lo", "of", "li", "ka", "ha", "he", "ah", "ny", "ro", + "li", "me", "ad", "he", "ah", "um", "co", "ga", "gar", "fa", + "el", "ra", "ia", "of", "os", "ra", "ta", "na", "ga", "ho", + "lu", "lu", "fe", "zi", "mo", "sha", "ru", "te", "vo", "ni", + "xa", "jo", "da", "ku", "pe", "su", "yo", "ve", "mi", "ba" + ) + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_languages.dmi' + icon_state = "garou" + default_priority = 90 + +/datum/language/primal_tongue + name = "Primal Tongue" + desc = "A language inherently known to all Garou breeds at birth, able to be spoken only in Lupus, Crinos and Hispo forms." + key = "p" + flags = TONGUELESS_SPEECH | LANGUAGE_HIDE_ICON_IF_NOT_UNDERSTOOD + space_chance = 40 + syllables = list ( + "gra", "grr", "gru", "gha", "sha", "zho", "yip", "whu", "zar", "ruk", + "kra", "hya", "tza", "ska", "yrr", "fru", "thra", "hwo", "vra", "snar", + "kru", "pha", "gha", "hro", "tzo", "wha", "brak", "thru", "chur", "dra", + "vru", "sna", "yru", "hru", "yla", "fro", "rik", "zru", "skra", "zhu", + "kro", "thro", "zyi", "sha", "hza", "mru", "wru", "bruk", "hka", "tza" + ) + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_languages.dmi' + icon_state = "garou" + default_priority = 90 + diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/_gift.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/_gift.dm new file mode 100644 index 000000000000..8dfddb0a207e --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/_gift.dm @@ -0,0 +1,55 @@ +/datum/action/cooldown/power/gift + background_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi' + background_icon_state = "bg_gift" + button_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi' + //button_icon_state = "" + overlay_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi' + + check_flags = AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS + + + var/rage_req = 0 + var/gnosis_req = 0 + +/datum/action/cooldown/power/gift/IsAvailable(feedback) + . = ..() + + if(!ishuman(owner)) + if(feedback) + owner.balloon_alert(owner, "not human!") + return FALSE + + var/datum/splat/werewolf/casting_splat = iswerewolfsplat(owner) + + if(get_rage_cost()) + if(casting_splat.rage < get_rage_cost()) + if(feedback) + to_chat(owner, span_warning("You don't have enough RAGE to do that!")) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_cast_failed.ogg', volume = 50)) + return FALSE + if(gnosis_req) + if(casting_splat.gnosis < gnosis_req) + if(feedback) + to_chat(owner, span_warning("You don't have enough GNOSIS to do that!")) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_cast_failed.ogg', volume = 50)) + return FALSE + + +/datum/action/cooldown/power/gift/Activate(atom/target) + . = ..() + if(!ishuman(owner)) + return FALSE + + var/datum/splat/werewolf/casting_splat = iswerewolfsplat(owner) + + if(get_rage_cost()) + casting_splat.adjust_rage(-get_rage_cost(), owner, FALSE) + if(get_gnosis_cost()) + casting_splat.adjust_gnosis(-get_gnosis_cost(), owner, FALSE) + to_chat(owner, span_notice("You activate the [name]...")) + +/datum/action/cooldown/power/gift/proc/get_rage_cost() + return rage_req + +/datum/action/cooldown/power/gift/proc/get_gnosis_cost() + return gnosis_req diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ahroun.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ahroun.dm new file mode 100644 index 000000000000..5422173bfc98 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ahroun.dm @@ -0,0 +1,45 @@ +/datum/action/cooldown/power/gift/falling_touch + name = "Falling Touch" + desc = "This Gift allows the Garou to send her foe sprawling with but a touch." + button_icon_state = "falling_touch" + click_to_activate = TRUE + +/datum/action/cooldown/power/gift/falling_touch/set_click_ability(mob/on_who) + . = ..() + SEND_SOUND(owner, 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch.ogg') + + +/datum/action/cooldown/power/gift/falling_touch/Activate(atom/target) + if(!isliving(target)) + return + if(!(target in range(1, owner))) + return + + . = ..() + + var/mob/living/victim = target + var/mob/living/caster = owner + var/datum/splat/werewolf/casting_splat = iswerewolfsplat(owner) + var/holding = caster.get_active_held_item() + if(caster.combat_mode) + victim.Knockdown(1 TURNS) + victim.Immobilize(1 TURNS) + playsound(get_turf(caster), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch_activate.ogg', 75, FALSE) // red-tailed hawk sound mixed with disintegrate.ogg + SEND_SIGNAL(owner, COMSIG_MASQUERADE_VIOLATION) + if(holding) + victim.attackby(holding, caster) + else + victim.attack_hand(caster) + + if(casting_splat.rage) + casting_splat.adjust_rage(-1) + else + caster.st_set_stat(STAT_TEMPORARY_WILLPOWER, max((caster.st_get_stat(STAT_TEMPORARY_WILLPOWER) - 1),0)) + to_chat(caster, span_userdanger("You used WILLPOWER to cast [src]!")) + else + victim.Knockdown(1 TURNS) + to_chat(caster, span_notice("You brush up against [victim], knocking them prone!")) + to_chat(victim, span_userdanger("You fall prone!")) + + StartCooldown() + return TRUE diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/galliard.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/galliard.dm new file mode 100644 index 000000000000..7774c0b3e5e7 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/galliard.dm @@ -0,0 +1,96 @@ +/* +/datum/action/cooldown/power/gift/beast_speech + name = "Beast Speech" + desc = "The werewolf with this Gift may communicate with any animals from fish to mammals." + button_icon_state = "beast_speech" + rage_req = 1 + //gnosis_req = 1 + +/datum/action/cooldown/power/gift/beast_speech/Activate(atom/target) + . = ..() + if(allowed_to_proceed) + var/mob/living/carbon/C = owner + if(length(C.beastmaster) > 3) + var/mob/living/simple_animal/hostile/beastmaster/B = pick(C.beastmaster) + qdel(B) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/wolves.ogg', 75, FALSE) + if(!length(C.beastmaster)) + var/datum/action/beastmaster_stay/E1 = new() + E1.Grant(C) + var/datum/action/beastmaster_deaggro/E2 = new() + E2.Grant(C) + var/mob/living/simple_animal/hostile/beastmaster/D = new(get_turf(C)) + D.my_creator = C + C.beastmaster |= D + D.beastmaster = C +*/ + +/* +/datum/action/cooldown/power/gift/call_of_the_wyld + name = "Call Of The Wyld" + desc = "The werewolf may send her howl far beyond the normal range of hearing and imbue it with great emotion, stirring the hearts of fellow Garou and chilling the bones of all others." + button_icon_state = "call_of_the_wyld" + rage_req = 1 + +/datum/action/cooldown/power/gift/call_of_the_wyld/Activate(atom/target) + . = ..() + + if(!ishuman(owner)) + return + var/mob/living/carbon/human/human_owner = owner + if(allowed_to_proceed) + var/mob/living/carbon/C = owner + C.emote("howl") + for(var/mob/living/carbon/A in orange(6, owner)) + if(isgarou(A) || iswerewolf(A)) + A.emote("howl") + spawn(1 SECONDS) + adjust_gnosis(1, A, TRUE) +// awo1 +*/ + +// Very inaccurate right now +/datum/action/cooldown/power/gift/mindspeak + name = "Mindspeak" + desc = "By invoking the power of waking dreams, the Garou can place any chosen characters into silent communion." + button_icon_state = "mindspeak" +// gnosis_req = 1 + + +/datum/action/cooldown/power/gift/mindspeak/Activate(atom/target) + var/input = tgui_input_text(usr, "What do you want to tell to your Tribe?", name, max_length = MAX_MESSAGE_LEN) + if(!input || !IsAvailable(feedback = TRUE)) + return + + var/list/filter_result = CAN_BYPASS_FILTER(usr) ? null : is_ic_filtered(input) + if(filter_result) + REPORT_CHAT_FILTER_TO_USER(usr, filter_result) + return + + var/list/soft_filter_result = CAN_BYPASS_FILTER(usr) ? null : is_soft_ic_filtered(input) + if(soft_filter_result) + if(tgui_alert(usr,"Your message contains \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". \"[soft_filter_result[CHAT_FILTER_INDEX_REASON]]\", Are you sure you want to say it?", "Soft Blocked Word", list("Yes", "No")) != "Yes") + return + message_admins("[ADMIN_LOOKUPFLW(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[html_encode(input)]\"") + log_admin_private("[key_name(usr)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\" they may be using a disallowed term. Message: \"[input]\"") + commune_tribe(usr, input) + +/datum/action/cooldown/power/gift/mindspeak/proc/commune_tribe(mob/living/user, message) + var/my_message + if(!message || !user.mind) + return + + my_message = "[findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" + var/datum/splat/werewolf/our_splat = iswerewolfsplat(user) + if(!our_splat?.tribe) + return + for(var/mob/living/listener in viewers(9, owner)) + var/datum/splat/werewolf/listener_splat = iswerewolfsplat(listener) + if(listener == user) + to_chat(user, "You transfer this message to your tribe members nearby: [message]", type = MESSAGE_TYPE_RADIO, avoid_highlighting = TRUE) + else if(listener_splat?.tribe?.name == our_splat.tribe.name) + to_chat(listener, "You hear a message in your head... [message]", type = MESSAGE_TYPE_RADIO) + + for(var/mob/listener in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(listener, user) + to_chat(listener, "[link] [my_message]", type = MESSAGE_TYPE_RADIO) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/philodox.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/philodox.dm new file mode 100644 index 000000000000..ceff6a58f470 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/philodox.dm @@ -0,0 +1,106 @@ +/datum/action/cooldown/power/gift/scent_of_the_true_form + name = "Scent Of The True Form" + desc = "This Gift allows the Garou to determine the true nature of a person." + button_icon_state = "scent_of_the_true_form" + click_to_activate = TRUE + var/static/list/wyld_descriptors = list( + "ozone", + "euphoria", + "flowers", + "an unseen breeze", + "petrichor", + "the calm after a thunderstorm", + "a primal ocean", + "the anticipation of limitless possibility" + ) + var/static/list/weaver_descriptors = list( + "sound patterns", + "cleaning fluid", + "hand sanitizer", + "a spider\'s web", + "silken thread", + "metal", + "a sudden drain of energy", + "flashing lights", + "alarms and sirens" + ) + var/static/list/wyrm_descriptors = list( + "rot", + "decay", + "fear", + "an animal that died in fear", + "depression", + "hopelessness", + "pain", + "lengethening shadows" + ) + +/datum/action/cooldown/power/gift/scent_of_the_true_form/set_click_ability(mob/on_who) + . = ..() + SEND_SOUND(owner, 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/scent_of_the_true_form.ogg') // Vulture sound mixed with fleshtostone.ogg + +/datum/action/cooldown/power/gift/scent_of_the_true_form/Activate(atom/target) + if(!isliving(target)) + return + if(!(target in range(3, owner))) + to_chat(owner, span_warning("You can't smell [target] from here.")) + return + + . = ..() + + var/mob/living/victim = target + var/mob/living/caster = owner + var/datum/splat/werewolf/target_splat = iswerewolfsplat(victim) + + if(istype(target_splat)) + var/secondary_descriptor = "[pick(wyld_descriptors)]" + switch(target_splat.tribe?.name) + if(TRIBE_GLASS_WALKERS) + secondary_descriptor = "[pick(weaver_descriptors)]" + if(TRIBE_BONE_GNAWERS) + secondary_descriptor = "[pick(weaver_descriptors)]" + if(TRIBE_BLACK_SPIRAL_DANCERS) + secondary_descriptor = "[pick(wyrm_descriptors)]" + to_chat(owner, span_purple("[victim] smells like kin[secondary_descriptor ? "...
...and of [secondary_descriptor]." : "."]")) + else + var/successes = SSroll.storyteller_roll(caster.st_get_stat(STAT_PERCEPTION) + PRIMAL_URGE_PLACEHOLDER, 6, owner, numerical = TRUE) + switch(successes) + if(0) + to_chat(owner, span_purple("You can't exactly tell what [victim] smells like.")) + if(1) + to_chat(owner, span_purple("[victim] smells mundane.")) + if(2 to 3) + if(iskindred(victim)) + to_chat(owner, span_purple("[victim] smells of [pick(wyrm_descriptors)]")) + if(isshifter(victim) && !isgarou(victim)) + to_chat(owner, span_purple("They smell of kin, but not Garou.")) +// if(ishungrydead(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyrm_descriptors)]")) +// if(ischangeling(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyld_descriptors)]")) +// if(isdemon(victim)) +// to_chat(owner, span_purple("[victim] smells of brimstone.")) +// if(ismummy(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyld_descriptors)]")) + if(4) + if(iskindred(victim)) + to_chat(owner, span_purple("[victim] smells of [pick(wyrm_descriptors)]")) + if(isghoul(victim)) + to_chat(owner, span_purple("[victim] smells of [pick(wyrm_descriptors)]")) + if(isshifter(victim) && !isgarou(victim)) + to_chat(owner, span_purple("They smell of kin, but not Garou.")) +// if(isfomor(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyrm_descriptors)]")) +// if(ischangeling(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyld_descriptors)]")) +// if(isdemon(victim)) +// to_chat(owner, span_purple("[victim] smells of brimstone.")) +// if(ismummy(victim)) +// to_chat(owner, span_purple("[victim] smells of [pick(wyld_descriptors)]")) +// if(ismage(victim)) +// to_chat(owner, span_purple("[victim] smells of pure energy.")) + + caster.emote("sniff") + + StartCooldown() + return TRUE diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ragabash.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ragabash.dm new file mode 100644 index 000000000000..5455734ebfa7 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/ragabash.dm @@ -0,0 +1,95 @@ +/* +/datum/action/cooldown/power/gift/blur_of_the_milky_eye + name = "Blur Of The Milky Eye" + desc = "The Garou's form becomes a shimmering blur, allowing him to pass unnoticed among others." + button_icon_state = "blur_of_the_milky_eye" + rage_req = 2 + //gnosis_req = 1 + +/datum/action/cooldown/power/gift/blur_of_the_milky_eye/Activate(atom/target) + . = ..() + if(allowed_to_proceed) + var/mob/living/carbon/C = owner + C.alpha = 36 + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/milky_blur.ogg', 75, FALSE) + spawn(20 SECONDS) + C.alpha = 255 +*/ + +/datum/action/cooldown/power/gift/infectious_laughter + name = "Infectious Laughter" + desc = "When the Ragabash laughs, those around her are compelled to follow along, forgetting their grievances. Uses the last message you spoke for the ability" + button_icon_state = "infectious_laughter" + // I dont acctually see anything in the book that is causes rage. + // rage_req = 1 + var/last_spoken_message + var/when_spoken = 0 + +/datum/action/cooldown/power/gift/infectious_laughter/Grant(mob/granted_to) + . = ..() + + RegisterSignal(granted_to, COMSIG_MOB_SAY, PROC_REF(on_mob_say)) + +/datum/action/cooldown/power/gift/infectious_laughter/Remove(mob/removed_from) + . = ..() + + UnregisterSignal(removed_from, COMSIG_MOB_SAY) + +/datum/action/cooldown/power/gift/infectious_laughter/proc/on_mob_say(datum/source, list/speech_args) + SIGNAL_HANDLER + + last_spoken_message = speech_args[SPEECH_MESSAGE] + when_spoken = world.time + +/datum/action/cooldown/power/gift/infectious_laughter/IsAvailable(feedback) + . = ..() + if(!last_spoken_message || (when_spoken + 3 TURNS < world.time)) + if(feedback) + to_chat(owner, span_warning("You haven't said anything making a joke of the current scene in the past few moments!")) + return FALSE + +/datum/action/cooldown/power/gift/infectious_laughter/Activate(atom/target) + . = ..() + + if(!ishuman(owner)) + return + if(!last_spoken_message || (when_spoken + 3 TURNS < world.time)) + return + var/mob/living/carbon/human/human_owner = owner + + owner.emote("laugh") + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/infectious_laughter.ogg', 50, FALSE) + var/list/hearers = oviewers(DEFAULT_MESSAGE_RANGE, owner) + var/highest_diff = 0 + for(var/mob/living/dice_guy in hearers) + var/datum/splat/werewolf/rage_haver = iswerewolfsplat(dice_guy) + if(!rage_haver) + continue + highest_diff = max(highest_diff, rage_haver.rage) + var/roll = SSroll.storyteller_roll(human_owner.st_get_stat(STAT_MANIPULATION) + human_owner.st_get_stat(STAT_EMPATHY), highest_diff, owner) + if(roll == ROLL_SUCCESS) + for(var/mob/living/hearer in hearers) + to_chat(hearer, span_hypnophrase("The message bounces around in your head, \"[last_spoken_message]\". You struggle to recall why you might have been mad.")) + hearer.emote("laugh") + + last_spoken_message = null + when_spoken = 0 + + + // laughers.emote("laugh") + +/* +/datum/action/cooldown/power/gift/open_seal + name = "Open Seal" + desc = "With this Gift, the Garou can open nearly any sort of closed or locked physical device." + button_icon_state = "open_seal" +// gnosis_req = 1 + +/datum/action/cooldown/power/gift/open_seal/Activate(atom/target) + . = ..() + if(allowed_to_proceed) + for(var/obj/structure/vampdoor/V in range(5, owner)) + if(V.closed) + if(V.lockpick_difficulty < 10) + V.open_door(owner, TRUE) +*/ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/theurge.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/theurge.dm new file mode 100644 index 000000000000..eb26fdefe41c --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/auspices/theurge.dm @@ -0,0 +1,40 @@ +/* +From W20 p. 164 +Mother's Touch +System: The player spends one Gnosis point and rolls +Intelligence + Empathy (difficulty is the target’s current +Rage, or 5 for those with no Rage). Each success heals one +level of lethal, bashing, or aggravated damage. The healer +may even heal fresh Battle Scars (see p. 259) in this man- +ner, if the Gift is applied during the same scene in which +the scar is received and an extra Gnosis point is spent. +*/ +/datum/action/cooldown/power/gift/mothers_touch + name = "Mother's Touch" + desc = "The Garou is able to heal the wounds of any living creature, aggravated or otherwise, simply by laying hands over the afflicted area." + button_icon_state = "mothers_touch" + click_to_activate = TRUE + + //rage_req = 1 + gnosis_req = 1 + + +/datum/action/cooldown/power/gift/mothers_touch/Activate(atom/target) + if(!isliving(target)) + return + if(!(target in range(1, owner))) + return + + . = ..() + + var/mob/living/living_owner = owner + var/datum/splat/werewolf/werewolf_splat = iswerewolfsplat(owner) + var/difficulty = werewolf_splat.uses_rage ? werewolf_splat.rage : 5 + var/successes = SSroll.storyteller_roll(living_owner.st_get_stat(STAT_INTELLIGENCE) + living_owner.st_get_stat(STAT_EMPATHY), difficulty, owner, TRUE) + + var/mob/living/living_target = target + living_target.heal_storyteller_health(successes, TRUE, TRUE, TRUE) + + SEND_SIGNAL(owner, COMSIG_MASQUERADE_VIOLATION) + StartCooldown() + return TRUE diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/innate/howling.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/innate/howling.dm new file mode 100644 index 000000000000..2432605cc06b --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/innate/howling.dm @@ -0,0 +1,119 @@ +/datum/action/cooldown/power/gift/howling + name = "Howl" + desc = "The werewolf may send her howl far beyond the normal range of hearing and communicate a single word or concept to all other Garou across the city." + button_icon_state = "call_of_the_wyld" + rage_req = 1 + check_flags = null + innate_ability = TRUE + var/static/list/howls = list( + "attack" = list( + "menu" = "Attack", + SPLAT_GAROU = "A wolf howls a fierce call to attack", + SPLAT_CORAX = "A raven hisses a fierce call to attack" + ), + "retreat" = list( + "menu" = "Retreat", + SPLAT_GAROU = "A wolf howls a warning to retreat", + SPLAT_CORAX = "A raven squawks a warning to retreat" + ), + "help" = list( + "menu" = "Help", + SPLAT_GAROU = "A wolf howls a desperate plea for help", + SPLAT_CORAX = "A raven shrieks a a desperate plea for help" + ), + "gather" = list( + "menu" = "Gather", + SPLAT_GAROU = "A wolf howls to gather the pack", + SPLAT_CORAX = "A raven beckons the conspiracy" + ), + "victory" = list( + "menu" = "Victory", + SPLAT_GAROU = "A wolf howls in celebration of victory", + SPLAT_CORAX = "A raven croaks in celebration of victory" + ), + "dying" = list( + "menu" = "Dying", + SPLAT_GAROU = "A wolf howls in pain and despair", + SPLAT_CORAX = "A raven shrieks in pain and despair" + ), + "mourning" = list( + "menu" = "Mourning", + SPLAT_GAROU = "A wolf howls in deep mourning for the fallen", + SPLAT_CORAX = "A raven mourns the loss of the fallen" + ) + ) + +/datum/action/cooldown/power/gift/howling/IsAvailable(feedback) + . = ..() + if(istype(get_area(owner), /area/vtm/interior/penumbra)) + if(feedback) + to_chat(owner, span_warning("Your howl echoes and dissipates into the Umbra, it's sound blanketed by the spiritual energy of the Velvet Shadow.")) + return + +/datum/action/cooldown/power/gift/howling/Activate(atom/target) + . = ..() + + var/mob/living/living_mob = owner + var/datum/splat/werewolf/shifter = isshifter(owner) + var/list/menu_options = list() + for(var/howl_key in howls) + menu_options += howls[howl_key]["menu"] + + var/choice = tgui_input_list(owner, "Select a howl to use!", "Howl Selection", menu_options) + if(!choice) + return + + var/howl + for(var/howl_key in howls) + if(howls[howl_key]["menu"] == choice) + howl = howls[howl_key] + break + + var/garou_message = howl[shifter.id] + /* + var/tribe = living_mob.auspice.tribe.name + if (tribe) + garou_message = replacetext(garou_message, "tribe", tribe) + */ + var/origin_turf = get_turf(living_mob) + ADD_TRAIT(living_mob, TRAIT_LOUD_HOWLER, type) + living_mob.emote("howl") + REMOVE_TRAIT(living_mob, TRAIT_LOUD_HOWLER, type) + + var/howl_details + var/final_message + for(var/mob/living/howled_at in GLOB.player_list - owner) + if(isshifter(howled_at)) + howl_details = get_message(howled_at, origin_turf) + final_message = garou_message + howl_details + to_chat(howled_at, span_boldnotice(final_message)) + + +/datum/action/cooldown/power/gift/howling/proc/get_message(mob/living/howled_at, turf/origin_turf) + var/distance = get_dist(howled_at, origin_turf) + var/dirtext = " to the " + var/direction = get_dir(howled_at, origin_turf) + + if(dir2text(direction)) + dirtext += dir2text(direction) + else + dirtext = " although I cannot make out an exact direction" + + var/disttext + switch(distance) + if(0 to 20) + disttext = " within 20 feet" + if(20 to 40) + disttext = " 20 to 40 feet away" + if(40 to 80) + disttext = " 40 to 80 feet away" + if(80 to 160) + disttext = " far" + else + disttext = " very far" + + var/place = get_area_name(origin_turf) + + var/returntext = "[disttext],[dirtext], at [place]." + + return returntext diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/tribes.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/tribes/tribes.dm similarity index 72% rename from modular_darkpack/modules/werewolf_the_apocalypse/code/tribes.dm rename to modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/tribes/tribes.dm index 3c9c78421165..4ecee4e784c0 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/tribes.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts/tribes/tribes.dm @@ -1,14 +1,14 @@ -/datum/action/gift/stoic_pose +/datum/action/cooldown/power/gift/stoic_pose name = "Stoic Pose" desc = "With this gift garou sends theirself into cryo-state, ignoring all incoming damage but also covering themself in a block of ice." button_icon_state = "stoic_pose" rage_req = 2 gnosis_req = 1 -/datum/action/gift/stoic_pose/Trigger() +/datum/action/cooldown/power/gift/stoic_pose/Trigger() . = ..() if(allowed_to_proceed) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/ice_blocking.ogg', 100, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/ice_blocking.ogg', 100, FALSE) var/mob/living/carbon/C = owner if(isgarou(C)) var/obj/were_ice/W = new (get_turf(owner)) @@ -32,17 +32,32 @@ C.forceMove(get_turf(W)) qdel(W) -/datum/action/gift/freezing_wind +/obj/were_ice + name = "ice block" + desc = "Stores some precious organs..." + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_lupus.dmi' + icon_state = "ice_man" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + +/obj/were_ice/lupus + icon_state = "ice_wolf" + +/obj/were_ice/crinos + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf.dmi' + icon_state = "ice" + pixel_w = -8 + +/datum/action/cooldown/power/gift/freezing_wind name = "Freezing Wind" desc = "Garou of Wendigo Tribe can create a stream of cold, freezing wind, and strike her foes with it." button_icon_state = "freezing_wind" rage_req = 1 //gnosis_req = 1 -/datum/action/gift/freezing_wind/Trigger() +/datum/action/cooldown/power/gift/freezing_wind/Trigger() . = ..() if(allowed_to_proceed) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/wind_cast.ogg', 100, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/wind_cast.ogg', 100, FALSE) for(var/turf/T in range(3, get_step(get_step(owner, owner.dir), owner.dir))) if(owner.loc != T) var/obj/effect/wind/W = new(T) @@ -52,14 +67,14 @@ qdel(W) // if(allowed_to_proceed) -/datum/action/gift/bloody_feast +/datum/action/cooldown/power/gift/bloody_feast name = "Bloody Feast" desc = "By eating a grabbed corpse, garou can redeem their lost health and heal the injuries." button_icon_state = "bloody_feast" rage_req = 2 gnosis_req = 1 -/datum/action/gift/bloody_feast/Trigger() +/datum/action/cooldown/power/gift/bloody_feast/Trigger() . = ..() if(allowed_to_proceed) var/mob/living/carbon/C = owner @@ -67,36 +82,36 @@ if(isliving(C.pulling)) var/mob/living/L = C.pulling if(L.stat == DEAD) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/bloody_feast.ogg', 50, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/bloody_feast.ogg', 50, FALSE) qdel(L) C.revive(full_heal = TRUE, admin_revive = TRUE) -/datum/action/gift/stinky_fur +/datum/action/cooldown/power/gift/stinky_fur name = "Stinky Fur" desc = "Garou creates an aura of very toxic smell, which disorientates everyone around." button_icon_state = "stinky_fur" -/datum/action/gift/stinky_fur/Trigger() +/datum/action/cooldown/power/gift/stinky_fur/Trigger() . = ..() if(allowed_to_proceed) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/necromancy.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/necromancy.ogg', 75, FALSE) for(var/mob/living/carbon/C in orange(5, owner)) if(prob(25)) C.vomit() C.dizziness += 10 C.add_confusion(10) -/datum/action/gift/venom_claws +/datum/action/cooldown/power/gift/venom_claws name = "Venom Claws" desc = "While this ability is active, strikes with claws poison foes of garou." button_icon_state = "venom_claws" rage_req = 1 -/datum/action/gift/venom_claws/Trigger() +/datum/action/cooldown/power/gift/venom_claws/Trigger() . = ..() if(allowed_to_proceed) if(ishuman(owner)) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/venom_claws.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/venom_claws.ogg', 75, FALSE) var/mob/living/carbon/human/H = owner H.melee_damage_lower = initial(H.melee_damage_lower)+15 H.melee_damage_upper = initial(H.melee_damage_upper)+15 @@ -108,7 +123,7 @@ H.melee_damage_upper = initial(H.melee_damage_upper) to_chat(owner, span_warning("Your claws are not poison anymore...")) else - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/venom_claws.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/venom_claws.ogg', 75, FALSE) var/mob/living/carbon/H = owner H.melee_damage_lower = initial(H.melee_damage_lower)+10 H.melee_damage_upper = initial(H.melee_damage_upper)+10 @@ -120,14 +135,14 @@ H.melee_damage_upper = initial(H.melee_damage_upper) to_chat(owner, span_warning("Your claws are not poison anymore...")) -/datum/action/gift/burning_scars +/datum/action/cooldown/power/gift/burning_scars name = "Burning Scars" desc = "Garou creates an aura of very hot air, which burns everyone around." button_icon_state = "burning_scars" rage_req = 2 gnosis_req = 1 -/datum/action/gift/burning_scars/Trigger() +/datum/action/cooldown/power/gift/burning_scars/Trigger() . = ..() if(allowed_to_proceed) owner.visible_message(span_danger("[owner.name] crackles with heat!"), span_danger("You crackle with heat, charging up your Gift!")) @@ -139,13 +154,13 @@ spawn(5) qdel(F) -/datum/action/gift/smooth_move +/datum/action/cooldown/power/gift/smooth_move name = "Smooth Move" desc = "Garou jumps forward, avoiding every damage for a moment." button_icon_state = "smooth_move" //rage_req = 1 somewhat useless gift with MMB pounce -/datum/action/gift/smooth_move/Trigger() +/datum/action/cooldown/power/gift/smooth_move/Trigger() . = ..() if(allowed_to_proceed) var/turf/T = get_turf(get_step(get_step(get_step(owner, owner.dir), owner.dir), owner.dir)) @@ -158,14 +173,14 @@ spawn(3) owner.throw_at(T, get_dist(owner, T), 1, owner, 0) -/datum/action/gift/digital_feelings +/datum/action/cooldown/power/gift/digital_feelings name = "Digital Feelings" desc = "Every technology creates an electrical strike, which hits garou's enemies." button_icon_state = "digital_feelings" rage_req = 2 gnosis_req = 1 -/datum/action/gift/digital_feelings/Trigger() +/datum/action/cooldown/power/gift/digital_feelings/Trigger() . = ..() if(allowed_to_proceed) owner.visible_message(span_danger("[owner.name] crackles with static electricity!"), span_danger("You crackle with static electricity, charging up your Gift!")) @@ -175,19 +190,19 @@ for(var/mob/living/L in orange(6, owner)) L.electrocute_act(30, owner, siemens_coeff = 1, flags = NONE) -/datum/action/gift/elemental_improvement +/datum/action/cooldown/power/gift/elemental_improvement name = "Elemental Improvement" desc = "Garou flesh replaces itself with prothesis, making it less vulnerable to brute damage, but more for burn damage." button_icon_state = "elemental_improvement" rage_req = 2 gnosis_req = 1 -/datum/action/gift/elemental_improvement/Trigger() +/datum/action/cooldown/power/gift/elemental_improvement/Trigger() . = ..() if(allowed_to_proceed) animate(owner, color = "#6a839a", time = 10) if(ishuman(owner)) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/electro_cast.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/electro_cast.ogg', 75, FALSE) var/mob/living/carbon/human/H = owner H.physiology.armor.melee = 25 H.physiology.armor.bullet = 45 @@ -198,7 +213,7 @@ to_chat(owner, span_warning("Your skin is natural again...")) owner.color = "#FFFFFF" else - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/electro_cast.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/electro_cast.ogg', 75, FALSE) var/mob/living/carbon/werewolf/H = owner H.werewolf_armor = 45 to_chat(owner, span_notice("You feel your skin replaced with the machine...")) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/glyphs.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/glyphs.dm index 96afd70ec212..f7c779af409c 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/glyphs.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/glyphs.dm @@ -1,32 +1,22 @@ /// Inits GLOB.glyph_list /proc/init_glyphs() var/glyph_list = list() - for(var/path in subtypesof(/obj/effect/decal/garou_glyph)) + for(var/path in valid_subtypesof(/obj/effect/decal/garou_glyph)) var/obj/effect/decal/garou_glyph/S = path glyph_list[S.garou_name] = S sort_list(glyph_list, GLOBAL_PROC_REF(cmp_typepaths_asc)) return glyph_list -/obj/item/charcoal_stick - name = "charcoal stick" - desc = "A piece of burnt charcoal." - icon = 'icons/obj/art/crayons.dmi' - icon_state = "crayonblack" - w_class = WEIGHT_CLASS_SMALL - // TODO: Needs a ear icon state - // slot_flags = ITEM_SLOT_EARS +/obj/item/pen/charcoal/interact_with_atom(atom/target, mob/user, list/modifiers, list/attack_modifiers) + if(!isopenturf(target) || isgroundlessturf(target)) + return NONE -/obj/item/charcoal_stick/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers) - // DARKPACK TODO - GAROU - //if(!isgarou(user)) - // return + if(!user.has_language(/datum/language/garou_tongue, UNDERSTOOD_LANGUAGE)) + return NONE if(!GLOB.glyph_list.len) to_chat(user, span_notice("There are no glyphs available.")) - return - - if(!isopenturf(target) || isgroundlessturf(target)) - return + return NONE var/list/glyph_names = list() @@ -34,41 +24,42 @@ glyph_names += glyph var/choice = tgui_input_list(user, "Select a glyph to draw.", "Glyph Selection", glyph_names) - if(choice) - var/obj/effect/decal/garou_glyph/drawn_glyph = GLOB.glyph_list[choice] - if(drawn_glyph) - user.visible_message(span_notice("[user] starts to scrape a glyph into the ground..."), \ - span_notice("You begin to etch the spirals and lines of your chosen glyph...")) - - if(do_after(user, 5 SECONDS, target)) - new drawn_glyph.type(target) - user.visible_message(span_notice("[user] finishes up their rune."), \ - span_notice("You put the finishing touches on your rune, as it marks the ground before you.")) - else - user.visible_message(span_notice("[user] slips, smduges and ruins their glyph."), \ - span_notice("You mess it up, the glyph turning into nothing more than a smear upon the ground.")) - . = ..() + if(!choice) + return ITEM_INTERACT_BLOCKING + + var/obj/effect/decal/garou_glyph/drawn_glyph = GLOB.glyph_list[choice] + if(drawn_glyph) + user.visible_message(span_notice("[user] starts to scrape a glyph into the ground..."), \ + span_notice("You begin to etch the spirals and lines of your chosen glyph...")) + + if(do_after(user, 5 SECONDS, target)) + new drawn_glyph.type(target) + user.visible_message(span_notice("[user] finishes up their rune."), \ + span_notice("You put the finishing touches on your rune, as it marks the ground before you.")) + return ITEM_INTERACT_SUCCESS + else + user.visible_message(span_notice("[user] slips, smduges and ruins their glyph."), \ + span_notice("You mess it up, the glyph turning into nothing more than a smear upon the ground.")) + return ITEM_INTERACT_FAILURE /obj/effect/decal/garou_glyph + abstract_type = /obj/effect/decal/garou_glyph name = "odd glyph" desc = "An odd collection of symbols drawn in what seems to be charcoal." - var/garou_name = "basic glyph" - var/garou_desc = "a basic glyph with no meaning." // This is shown to werewolves who examine the glyph in order to determine its true meaning. anchored = TRUE icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/glyphs.dmi' icon_state = "garou" resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF // Very likely not needed // layer = SIGIL_LAYER + var/garou_name = "basic glyph" + var/garou_desc = "a basic glyph with no meaning." // This is shown to werewolves who examine the glyph in order to determine its true meaning. /obj/effect/decal/garou_glyph/examine(mob/user) . = ..() - // DARKPACK TODO - GAROU - /* - if(isgarou(user) || iswerewolf(user)) // If they're a werewolf, show them the true meaning of the glyph. - . += "Name: [garou_name]\n" + \ - "Description: [garou_desc]\n" - */ + if(user.has_language(/datum/language/garou_tongue, UNDERSTOOD_LANGUAGE)) + . += "Name: [garou_name]\n" + . += "Description: [garou_desc]\n" /obj/effect/decal/garou_glyph/wyrm name = "creepy glyph" diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm new file mode 100644 index 000000000000..235ff019a939 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/hud.dm @@ -0,0 +1,217 @@ +#define UI_LIVING_AUSPICE "EAST-2:16,CENTER:40" +#define UI_LIVING_RAGE_AND_GNOSIS "EAST-2:20,CENTER-1:40" +#define UI_LIVING_TRANSFORM_HOMID "EAST-2,CENTER+1:40" +#define UI_LIVING_TRANSFORM_WAR "EAST-1,CENTER+1:40" +#define UI_LIVING_TRANSFORM_FERAL "EAST,CENTER+1:40" + +/datum/hud/proc/add_werewolf_elements() + // transform_werewolf = new(null, src) + // infodisplay += transform_werewolf + + if(!auspice_icon) + auspice_icon = new(null, src) + infodisplay += auspice_icon + + if(!rage_and_gnosis_icon) + rage_and_gnosis_icon = new(null, src) + infodisplay += rage_and_gnosis_icon + + if(!homid_trans_icon) + homid_trans_icon = new(null, src) + infodisplay += homid_trans_icon + if(!war_trans_icon) + war_trans_icon = new(null, src) + infodisplay += war_trans_icon + if(!feral_trans_icon) + feral_trans_icon = new(null, src) + infodisplay += feral_trans_icon + + +/datum/splat/werewolf/add_relevent_huds(datum/hud/hud_used) + hud_used.add_werewolf_elements() + +/atom/movable/screen/auspice + name = "auspice" + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_ui.dmi' + icon_state = "auspice_bar" + screen_loc = UI_LIVING_AUSPICE + mouse_over_pointer = MOUSE_HAND_POINTER + var/looked_at_moon = FALSE + COOLDOWN_DECLARE(force_rage_cd) + +/atom/movable/screen/auspice/Initialize(mapload, datum/hud/hud_owner) + . = ..() + + update_icon() + register_context() + +/atom/movable/screen/auspice/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + context[SCREENTIP_CONTEXT_LMB] = "Check Moon" + if(COOLDOWN_FINISHED(src, force_rage_cd)) + context[SCREENTIP_CONTEXT_RMB] = "Gain Rage" + + return CONTEXTUAL_SCREENTIP_SET + +/atom/movable/screen/auspice/Click(location, control, params) + . = ..() + var/mob/living/carbon/human/clicker = usr + if(!istype(clicker)) + return + var/datum/splat/werewolf/clicker_splat = iswerewolfsplat(clicker) + if(!istype(clicker_splat)) + return + + var/list/modifiers = params2list(params) + if(LAZYACCESS(modifiers, RIGHT_CLICK) && clicker_splat.uses_rage) + if(!COOLDOWN_FINISHED(src, force_rage_cd)) + return + + clicker_splat.adjust_rage(1) + message_admins("[ADMIN_LOOKUPFLW(clicker)] manually gained rage.") + clicker.log_message("manually gained rage.", LOG_GAME, color="red") + COOLDOWN_START(src, force_rage_cd, 1 SCENES) + return TRUE + + var/area/my_area = get_area(clicker) + if(!my_area || !my_area.outdoors) + to_chat(clicker, span_warning("You need to be outside to look at the moon!")) + return + + to_chat(clicker, span_notice("The phase of the Moon is a [GLOB.moon_state].")) + + if(looked_at_moon) + return + looked_at_moon = TRUE + + update_icon() + + if(!clicker_splat.uses_rage) + return + + var/rage_amount = 1 + // W20 p. 145 + switch(GLOB.moon_state) + if(MOON_NEW) + rage_amount = 1 + if(MOON_WANING_GIBBOUS, MOON_WANING_CRESCENT) + rage_amount = 2 + if(MOON_WAXING_CRESENT, MOON_FIRST_QUARTER, MOON_WAXING_GIBBOUS, MOON_LAST_QUARTER) + rage_amount = 3 + if(MOON_FULL) + rage_amount = 4 + + if(clicker_splat?.auspice && (GLOB.moon_state in clicker_splat.auspice.moons_born_under)) + rage_amount = MAX_RAGE + + clicker_splat.adjust_rage(rage_amount, TRUE) + return TRUE + +/atom/movable/screen/auspice/update_icon_state() + if(looked_at_moon) + icon_state = "[GLOB.moon_state]" + return ..() + + +/mob/living/proc/update_werewolf_hud() + if(!hud_used) + return + hud_used.rage_and_gnosis_icon?.update_icon() + +/atom/movable/screen/rage_and_gnosis + name = "rage and gnosis" + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_meters.dmi' + icon_state = "rage0" + screen_loc = UI_LIVING_RAGE_AND_GNOSIS + +/atom/movable/screen/rage_and_gnosis/Initialize(mapload, datum/hud/hud_owner) + . = ..() + + update_icon() + +/atom/movable/screen/rage_and_gnosis/update_icon_state() + var/mob/living/owner = hud?.mymob + if(!istype(owner)) + return + + var/datum/splat/werewolf/our_splat = iswerewolfsplat(owner) + if(!istype(our_splat)) + return + + icon_state = "rage[our_splat.rage]" + + // Should really be in update_overlays but i wanted to keep it to one iswerewolfsplat fetch + cut_overlays() + add_overlay("gnosis[our_splat.gnosis]") + + return ..() + +/atom/movable/screen/fera_transform + abstract_type = /atom/movable/screen/fera_transform + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_transforms.dmi' + mouse_over_pointer = MOUSE_HAND_POINTER + var/datum/species/left_click_transform + var/datum/species/right_click_transform + +/atom/movable/screen/fera_transform/Initialize(mapload, datum/hud/hud_owner) + . = ..() + + update_icon() + register_context() + +/atom/movable/screen/fera_transform/Click(location, control, params) + . = ..() + var/mob/living/carbon/human/clicker = usr + if(!istype(clicker)) + return + if(clicker.stat >= UNCONSCIOUS) + return + + var/datum/splat/werewolf/shifter/shifting = isshifter(clicker) + var/list/modifiers = params2list(params) + // Right click for alt forms like glabro and hispo. Ctrl click to use rage to do it instantly (doesnt matter if its breed form tho) + shifting.transform_fera(LAZYACCESS(modifiers, RIGHT_CLICK) ? right_click_transform : left_click_transform, LAZYACCESS(modifiers, CTRL_CLICK)) + + +/atom/movable/screen/fera_transform/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + var/datum/splat/werewolf/shifter/shifting = isshifter(user) + + if(left_click_transform) + context[SCREENTIP_CONTEXT_LMB] = "Shift to [left_click_transform::name]" + if(left_click_transform != shifting.get_breed_form_species()) + context[SCREENTIP_CONTEXT_CTRL_LMB] = "Shift using rage" + if(right_click_transform) + context[SCREENTIP_CONTEXT_RMB] = "Shift to [right_click_transform::name]" + if(right_click_transform != shifting.get_breed_form_species()) + context[SCREENTIP_CONTEXT_CTRL_RMB] = "Shift using rage" + + return CONTEXTUAL_SCREENTIP_SET + +/atom/movable/screen/fera_transform/homid + name = "homid form" + icon_state = "homid" + screen_loc = UI_LIVING_TRANSFORM_HOMID + left_click_transform = /datum/species/human/shifter/homid + right_click_transform = /datum/species/human/shifter/bestial + +/atom/movable/screen/fera_transform/war + name = "war form" + icon_state = "war" + screen_loc = UI_LIVING_TRANSFORM_WAR + left_click_transform = /datum/species/human/shifter/war + +/atom/movable/screen/fera_transform/feral + name = "feral form" + icon_state = "feral" + screen_loc = UI_LIVING_TRANSFORM_FERAL + left_click_transform = /datum/species/human/shifter/feral + right_click_transform = /datum/species/human/shifter/dire + +#undef UI_LIVING_TRANSFORM_HOMID +#undef UI_LIVING_TRANSFORM_WAR +#undef UI_LIVING_TRANSFORM_FERAL +#undef UI_LIVING_AUSPICE +#undef UI_LIVING_RAGE_AND_GNOSIS diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm similarity index 50% rename from modular_darkpack/modules/werewolf_the_apocalypse/code/gifts.dm rename to modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm index a572395bb31a..86fec51a85d5 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/gifts.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/old/gifts.dm @@ -1,61 +1,17 @@ -/datum/action/gift - icon_icon = 'modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi' - button_icon = 'modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi' - check_flags = AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS - var/rage_req = 0 - var/gnosis_req = 0 - var/cool_down = 0 - - var/allowed_to_proceed = FALSE - -/datum/action/gift/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force = FALSE) - icon_icon = 'modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi' - button_icon = 'modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi' - . = ..() - -/datum/action/gift/Trigger() - . = ..() - if(istype(owner, /mob/living/carbon)) - var/mob/living/carbon/H = owner - if(H.stat == DEAD) - allowed_to_proceed = FALSE - return - if(rage_req) - if(H.auspice.rage < rage_req) - to_chat(owner, span_warning("You don't have enough RAGE to do that!")) - SEND_SOUND(owner, sound('modular_darkpack/modules/deprecated/sounds/werewolf_cast_failed.ogg', volume = 75)) - allowed_to_proceed = FALSE - return - if(H.auspice.gnosis < gnosis_req) - to_chat(owner, span_warning("You don't have enough GNOSIS to do that!")) - SEND_SOUND(owner, sound('modular_darkpack/modules/deprecated/sounds/werewolf_cast_failed.ogg', volume = 75)) - allowed_to_proceed = FALSE - return - if(cool_down+150 >= world.time) - allowed_to_proceed = FALSE - return - cool_down = world.time - allowed_to_proceed = TRUE - if(rage_req) - adjust_rage(-rage_req, owner, FALSE) - if(gnosis_req) - adjust_gnosis(-gnosis_req, owner, FALSE) - to_chat(owner, span_notice("You activate the [name]...")) - -/datum/action/gift/falling_touch +/datum/action/cooldown/power/gift/falling_touch name = "Falling Touch" desc = "This Gift allows the Garou to send her foe sprawling with but a touch." button_icon_state = "falling_touch" rage_req = 1 -/datum/action/gift/falling_touch/Trigger() +/datum/action/cooldown/power/gift/falling_touch/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/H = owner - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/falling_touch.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/falling_touch.ogg', 75, FALSE) H.put_in_active_hand(new /obj/item/melee/touch_attack/werewolf(H)) -/datum/action/gift/inspiration +/datum/action/cooldown/power/gift/inspiration name = "Inspiration" desc = "The Garou with this Gift lends new resolve and righteous anger to his brethren." button_icon_state = "inspiration" @@ -80,28 +36,28 @@ to_chat(src, span_warning("You no longer feel inspired...")) inspired = FALSE -/datum/action/gift/inspiration/Trigger() +/datum/action/cooldown/power/gift/inspiration/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/H = owner - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/inspiration.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspiration.ogg', 75, FALSE) H.emote("scream") for(var/mob/living/carbon/C in range(5, owner)) if(iswerewolf(C) || isgarou(C)) if(C.auspice.tribe == H.auspice.tribe) C.inspired() -/datum/action/gift/razor_claws +/datum/action/cooldown/power/gift/razor_claws name = "Razor Claws" desc = "By raking his claws over stone, steel, or another hard surface, the Ahroun hones them to razor sharpness." button_icon_state = "razor_claws" rage_req = 1 -/datum/action/gift/razor_claws/Trigger() +/datum/action/cooldown/power/gift/razor_claws/Activate(atom/target) . = ..() if(allowed_to_proceed) if(ishuman(owner)) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/razor_claws.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/razor_claws.ogg', 75, FALSE) var/mob/living/carbon/human/H = owner H.dna.species.attack_verb = "slash" H.dna.species.attack_sound = 'sound/items/weapons/slash.ogg' @@ -119,7 +75,7 @@ H.agg_damage_plus = 0 to_chat(owner, span_warning("Your claws are not sharp anymore...")) else - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/razor_claws.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/razor_claws.ogg', 75, FALSE) var/mob/living/carbon/H = owner H.melee_damage_lower = H.melee_damage_lower+15 H.melee_damage_upper = H.melee_damage_upper+15 @@ -131,80 +87,17 @@ H.agg_damage_plus = 0 to_chat(owner, span_warning("Your claws are not sharp anymore...")) -/datum/action/gift/beast_speech - name = "Beast Speech" - desc = "The werewolf with this Gift may communicate with any animals from fish to mammals." - button_icon_state = "beast_speech" - rage_req = 1 - //gnosis_req = 1 - -/datum/action/gift/beast_speech/Trigger() - . = ..() - if(allowed_to_proceed) - var/mob/living/carbon/C = owner - if(length(C.beastmaster) > 3) - var/mob/living/simple_animal/hostile/beastmaster/B = pick(C.beastmaster) - qdel(B) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/wolves.ogg', 75, FALSE) - if(!length(C.beastmaster)) - var/datum/action/beastmaster_stay/E1 = new() - E1.Grant(C) - var/datum/action/beastmaster_deaggro/E2 = new() - E2.Grant(C) - var/mob/living/simple_animal/hostile/beastmaster/D = new(get_turf(C)) - D.my_creator = C - C.beastmaster |= D - D.beastmaster = C - -/datum/action/gift/call_of_the_wyld - name = "Call Of The Wyld" - desc = "The werewolf may send her howl far beyond the normal range of hearing and imbue it with great emotion, stirring the hearts of fellow Garou and chilling the bones of all others." - button_icon_state = "call_of_the_wyld" - rage_req = 1 - -/datum/action/gift/call_of_the_wyld/Trigger() - . = ..() - if(allowed_to_proceed) - var/mob/living/carbon/C = owner - C.emote("howl") - playsound(get_turf(C), pick('modular_darkpack/modules/deprecated/sounds/awo1.ogg', 'modular_darkpack/modules/deprecated/sounds/awo2.ogg'), 100, FALSE) - for(var/mob/living/carbon/A in orange(6, owner)) - if(isgarou(A) || iswerewolf(A)) - A.emote("howl") - playsound(get_turf(A), pick('modular_darkpack/modules/deprecated/sounds/awo1.ogg', 'modular_darkpack/modules/deprecated/sounds/awo2.ogg'), 100, FALSE) - spawn(10) - adjust_gnosis(1, A, TRUE) -// awo1 - -/datum/action/gift/mindspeak - name = "Mindspeak" - desc = "By invoking the power of waking dreams, the Garou can place any chosen characters into silent communion." - button_icon_state = "mindspeak" -// gnosis_req = 1 - -/datum/action/gift/mindspeak/Trigger() - . = ..() - if(allowed_to_proceed) - var/new_thought = input(owner, "What do you want to tell to your Tribe?") as text|null - if(new_thought) - var/mob/living/carbon/C = owner - to_chat(C, "You transfer this message to your tribe members nearby: [sanitize_text(new_thought)]") - for(var/mob/living/carbon/A in orange(9, owner)) - if(isgarou(A) || iswerewolf(A)) - if(A.auspice.tribe == C.auspice.tribe) - to_chat(A, "You hear a message in your head... [sanitize_text(new_thought)]") - -/datum/action/gift/resist_pain +/datum/action/cooldown/power/gift/resist_pain name = "Resist Pain" desc = "Through force of will, the Philodox is able to ignore the pain of his wounds and continue acting normally." button_icon_state = "resist_pain" rage_req = 2 -/datum/action/gift/resist_pain/Trigger() +/datum/action/cooldown/power/gift/resist_pain/Activate(atom/target) . = ..() if(allowed_to_proceed) if(ishuman(owner)) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/resist_pain.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/resist_pain.ogg', 75, FALSE) var/mob/living/carbon/human/H = owner H.physiology.armor.melee = 40 H.physiology.armor.bullet = 25 @@ -214,7 +107,7 @@ H.physiology.armor.bullet = initial(H.physiology.armor.bullet) to_chat(owner, span_warning("Your skin is thin again...")) else - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/resist_pain.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/resist_pain.ogg', 75, FALSE) var/mob/living/carbon/werewolf/H = owner H.werewolf_armor = 40 to_chat(owner, span_notice("You feel your skin thickering...")) @@ -222,14 +115,14 @@ H.werewolf_armor = initial(H.werewolf_armor) to_chat(owner, span_warning("Your skin is thin again...")) -/datum/action/gift/scent_of_the_true_form +/datum/action/cooldown/power/gift/scent_of_the_true_form name = "Scent Of The True Form" desc = "This Gift allows the Garou to determine the true nature of a person." button_icon_state = "scent_of_the_true_form" rage_req = 1 //gnosis_req = 1 -/datum/action/gift/scent_of_the_true_form/Trigger() +/datum/action/cooldown/power/gift/scent_of_the_true_form/Activate(atom/target) . = ..() if(allowed_to_proceed) var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] @@ -237,49 +130,36 @@ spawn(200) abductor_hud.remove_hud_from(owner) -/datum/action/gift/truth_of_gaia +/datum/action/cooldown/power/gift/truth_of_gaia name = "Truth Of Gaia" desc = "As judges of the Litany, Philodox have the ability to sense whether others have spoken truth or falsehood." button_icon_state = "truth_of_gaia" // rage_req = 1 -/datum/action/gift/mothers_touch - name = "Mother's Touch" - desc = "The Garou is able to heal the wounds of any living creature, aggravated or otherwise, simply by laying hands over the afflicted area." - button_icon_state = "mothers_touch" - rage_req = 2 - //gnosis_req = 1 - -/datum/action/gift/mothers_touch/Trigger() - . = ..() - if(allowed_to_proceed) - var/mob/living/carbon/H = owner - H.put_in_active_hand(new /obj/item/melee/touch_attack/mothers_touch(H)) - -/datum/action/gift/sense_wyrm +/datum/action/cooldown/power/gift/sense_wyrm name = "Sense Wyrm" desc = "This Gift allows the werewolf to sense the presence of Wyrm." button_icon_state = "sense_wyrm" rage_req = 1 -/datum/action/gift/sense_wyrm/Trigger() +/datum/action/cooldown/power/gift/sense_wyrm/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/C = owner C.sight = SEE_MOBS|SEE_OBJS - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/sense_wyrm.ogg', 75, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/sense_wyrm.ogg', 75, FALSE) to_chat(owner, span_notice("You feel your sense sharpening...")) spawn(200) C.sight = initial(C.sight) to_chat(owner, span_warning("You no longer sense anything more than normal...")) -/datum/action/gift/spirit_speech +/datum/action/cooldown/power/gift/spirit_speech name = "Spirit Speech" desc = "This Gift allows the Garou to communicate with encountered spirits." button_icon_state = "spirit_speech" //gnosis_req = 1 -/datum/action/gift/spirit_speech/Trigger() +/datum/action/cooldown/power/gift/spirit_speech/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/C = owner @@ -287,66 +167,19 @@ spawn(200) C.see_invisible = initial(C.see_invisible) -/datum/action/gift/blur_of_the_milky_eye - name = "Blur Of The Milky Eye" - desc = "The Garou's form becomes a shimmering blur, allowing him to pass unnoticed among others." - button_icon_state = "blur_of_the_milky_eye" - rage_req = 2 - //gnosis_req = 1 - -/datum/action/gift/blur_of_the_milky_eye/Trigger() - . = ..() - if(allowed_to_proceed) - var/mob/living/carbon/C = owner - C.alpha = 36 - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/milky_blur.ogg', 75, FALSE) - spawn(20 SECONDS) - C.alpha = 255 - -/datum/action/gift/open_seal - name = "Open Seal" - desc = "With this Gift, the Garou can open nearly any sort of closed or locked physical device." - button_icon_state = "open_seal" -// gnosis_req = 1 - -/datum/action/gift/open_seal/Trigger() - . = ..() - if(allowed_to_proceed) - for(var/obj/structure/vampdoor/V in range(5, owner)) - if(V.closed) - if(V.lockpick_difficulty < 10) - V.open_door(owner, TRUE) - -/datum/action/gift/infectious_laughter - name = "Infectious Laughter" - desc = "When the Ragabash laughs, those around her are compelled to follow along, forgetting their grievances." - button_icon_state = "infectious_laughter" - rage_req = 1 - -/datum/action/gift/infectious_laughter/Trigger() - . = ..() - if(allowed_to_proceed) - var/mob/living/carbon/C = owner - C.emote("laugh") - C.Stun(10) - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/infectious_laughter.ogg', 100, FALSE) - for(var/mob/living/L in oviewers(4, owner)) - L.emote("laugh") - L.Stun(20) - -/datum/action/gift/rage_heal +/datum/action/cooldown/power/gift/rage_heal name = "Rage Heal" desc = "This Gift allows the Garou to heal severe injuries with rage." button_icon_state = "rage_heal" rage_req = 1 check_flags = null -/datum/action/gift/rage_heal/Trigger() +/datum/action/cooldown/power/gift/rage_heal/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/C = owner if(C.stat != DEAD) - SEND_SOUND(owner, sound('modular_darkpack/modules/deprecated/sounds/rage_heal.ogg', volume = 75)) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_heal.ogg', volume = 50)) C.adjust_brute_loss(-40*C.auspice.level, TRUE) C.adjust_fire_loss(-30*C.auspice.level, TRUE) C.adjust_agg_loss(-10*C.auspice.level, TRUE) @@ -376,10 +209,10 @@ name = "Change Apparel" desc = "Choose the clothes of your Crinos form." button_icon_state = "choose_apparel" - icon_icon = 'modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi' + icon_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi' check_flags = AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS -/datum/action/change_apparel/Trigger() +/datum/action/change_apparel/Activate(atom/target) . = ..() var/mob/living/carbon/werewolf/crinos/C = owner if(C.stat == CONSCIOUS) @@ -388,18 +221,18 @@ else C.sprite_apparel = min(4, C.sprite_apparel+1) -/datum/action/gift/hispo +/datum/action/cooldown/power/gift/hispo name = "Hispo Form" desc = "Change your Lupus form into Hispo and backwards." button_icon_state = "hispo" -/datum/action/gift/hispo/Trigger() +/datum/action/cooldown/power/gift/hispo/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/werewolf/lupus/H = owner - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/transform.ogg', 50, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/transform.ogg', 50, FALSE) if(H.hispo) - H.icon = 'modular_darkpack/modules/deprecated/icons/werewolf_lupus.dmi' + H.icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_lupus.dmi' H.pixel_w = 0 H.pixel_z = 0 H.melee_damage_lower = initial(H.melee_damage_lower) @@ -409,7 +242,7 @@ H.remove_movespeed_modifier(/datum/movespeed_modifier/crinosform) H.add_movespeed_modifier(/datum/movespeed_modifier/lupusform) else - H.icon = 'modular_darkpack/modules/deprecated/icons/hispo.dmi' + H.icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/hispo.dmi' H.pixel_w = -16 H.pixel_z = -16 H.melee_damage_lower = 35 @@ -419,17 +252,17 @@ H.remove_movespeed_modifier(/datum/movespeed_modifier/lupusform) H.add_movespeed_modifier(/datum/movespeed_modifier/crinosform) -/datum/action/gift/glabro +/datum/action/cooldown/power/gift/glabro name = "Glabro Form" desc = "Change your Homid form into Glabro and backwards." button_icon_state = "glabro" -/datum/action/gift/glabro/Trigger() +/datum/action/cooldown/power/gift/glabro/Activate(atom/target) . = ..() if(allowed_to_proceed) var/mob/living/carbon/human/H = owner var/datum/species/garou/G = H.dna.species - playsound(get_turf(owner), 'modular_darkpack/modules/deprecated/sounds/transform.ogg', 50, FALSE) + playsound(get_turf(owner), 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/transform.ogg', 50, FALSE) if(G.glabro) H.remove_overlay(PROTEAN_LAYER) G.punchdamagelow -= 15 @@ -444,7 +277,7 @@ H.update_icons() else H.remove_overlay(PROTEAN_LAYER) - var/mutable_appearance/glabro_overlay = mutable_appearance('modular_darkpack/modules/deprecated/icons/werewolf_abilities.dmi', H.transformator.crinos_form?.sprite_color, -PROTEAN_LAYER) + var/mutable_appearance/glabro_overlay = mutable_appearance('modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/werewolf_abilities.dmi', H.transformator.crinos_form?.sprite_color, -PROTEAN_LAYER) H.overlays_standing[PROTEAN_LAYER] = glabro_overlay H.apply_overlay(PROTEAN_LAYER) G.punchdamagelow += 15 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/auspice.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/auspice.dm new file mode 100644 index 000000000000..7c767bd00f21 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/auspice.dm @@ -0,0 +1,20 @@ +/datum/preference/choiced/garou_auspice + savefile_key = "garou_auspice" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + priority = PREFERENCE_PRIORITY_WORLD_OF_DARKNESS + main_feature_name = "Auspice" + relevant_inherent_trait = TRAIT_WTA_GAROU_AUSPICE + must_have_relevant_trait = TRUE + should_generate_icons = TRUE + +/datum/preference/choiced/garou_auspice/init_possible_values() + return assoc_to_keys(GLOB.auspices_list) // This would be inclusive of ALL auspices so many need to be reworked when adding other fera + +/datum/preference/choiced/garou_auspice/icon_for(value) + var/datum/universal_icon/auspice_icon = uni_icon('icons/effects/effects.dmi', "nothing") + auspice_icon.blend_icon(uni_icon('modular_darkpack/modules/werewolf_the_apocalypse/icons/auspices.dmi', replacetext(LOWER_TEXT(value), " ", "_")), ICON_OVERLAY) + return auspice_icon + +/datum/preference/choiced/garou_auspice/apply_to_human(mob/living/carbon/human/target, value) + target.set_auspice(value, TRUE) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/breed.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/breed.dm new file mode 100644 index 000000000000..f07ab142ad4e --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/breed.dm @@ -0,0 +1,40 @@ +/datum/preference/choiced/garou_breed + savefile_key = "garou_breed" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + priority = PREFERENCE_PRIORITY_WORLD_OF_DARKNESS + main_feature_name = "Breed" + relevant_inherent_trait = TRAIT_WTA_GAROU_BREED + must_have_relevant_trait = TRUE + should_generate_icons = TRUE + +/datum/preference/choiced/garou_breed/init_possible_values() + return assoc_to_keys(GLOB.breed_forms_list) + +/datum/preference/choiced/garou_breed/icon_for(value) + var/datum/universal_icon/garou_icon = uni_icon('icons/effects/effects.dmi', "nothing") + switch(value) + if(BREED_HOMID) + var/datum/universal_icon/breed_homid = uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_head_m") + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_chest_m"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_l_arm"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_r_arm"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_r_leg"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_l_leg"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_r_hand"), ICON_OVERLAY) + breed_homid.blend_icon(uni_icon('icons/mob/human/bodyparts_greyscale.dmi', "human_l_hand"), ICON_OVERLAY) + breed_homid.blend_color(skintone2hex("caucasian1"), ICON_MULTIPLY) + breed_homid.scale(32, 32) + garou_icon.blend_icon(breed_homid, ICON_OVERLAY) + if(BREED_LUPUS) + var/datum/universal_icon/breed_lupus = uni_icon('modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi', "black") + breed_lupus.scale(32, 32) + garou_icon.blend_icon(breed_lupus, ICON_OVERLAY) + if(BREED_CRINOS) + var/datum/universal_icon/breed_crinos = uni_icon('modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi', "black") + breed_crinos.scale(32, 32) + garou_icon.blend_icon(breed_crinos, ICON_OVERLAY) + return garou_icon + +/datum/preference/choiced/garou_breed/apply_to_human(mob/living/carbon/human/target, value) + target.set_breed_form(value, TRUE) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/fur.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/fur.dm new file mode 100644 index 000000000000..5ffe5a8988fd --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/fur.dm @@ -0,0 +1,15 @@ +/datum/preference/choiced/garou_fur_color + savefile_key = "garou_fur_color" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_SECONDARY_FEATURES + priority = PREFERENCE_PRIORITY_WORLD_OF_DARKNESS + main_feature_name = "Fera Fur Color" + relevant_inherent_trait = TRAIT_FERA_FUR + must_have_relevant_trait = TRUE + +/datum/preference/choiced/garou_fur_color/init_possible_values() + return assoc_to_keys(GLOB.garou_fur_colors) + + +/datum/preference/choiced/garou_fur_color/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features[FEATURE_FUR_COLOR] = value diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/renown.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/renown.dm new file mode 100644 index 000000000000..d166f2f8a39b --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/renown.dm @@ -0,0 +1,47 @@ +/datum/preference/numeric/renown + abstract_type = /datum/preference/numeric/renown + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED // DARKPACK TODO - Render this somewhere + priority = PREFERENCE_PRIORITY_REQUIRES_SUBSPLAT + savefile_identifier = PREFERENCE_CHARACTER + + minimum = 1 + maximum = 10 + +/datum/preference/numeric/renown/create_default_value() + return 1 + +/datum/preference/numeric/renown/apply_to_human(mob/living/carbon/human/target, value) + var/datum/splat/werewolf/splat = iswerewolfsplat(target) + if(!splat) + return + + splat.renown[savefile_key] = value + +/datum/preference/numeric/renown/honor + savefile_key = RENOWN_HONOR + +/datum/preference/numeric/renown/glory + savefile_key = RENOWN_GLORY + +/datum/preference/numeric/renown/wisdom + savefile_key = RENOWN_WISDOM + + +/datum/preference/numeric/fera_rank + category = PREFERENCE_CATEGORY_MANUALLY_RENDERED // DARKPACK TODO - Render this somewhere + priority = PREFERENCE_PRIORITY_REQUIRES_SUBSPLAT + savefile_identifier = PREFERENCE_CHARACTER + savefile_key = "fera_rank" + + minimum = 0 + maximum = 5 + +/datum/preference/numeric/fera_rank/create_default_value() + return 0 + +/datum/preference/numeric/fera_rank/apply_to_human(mob/living/carbon/human/target, value) + var/datum/splat/werewolf/splat = iswerewolfsplat(target) + if(!splat) + return + + splat.renown_rank = value diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/tribe.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/tribe.dm new file mode 100644 index 000000000000..e5fe60606149 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/preferences/tribe.dm @@ -0,0 +1,20 @@ +/datum/preference/choiced/garou_tribe + savefile_key = "garou_tribe" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + priority = PREFERENCE_PRIORITY_WORLD_OF_DARKNESS + main_feature_name = "Tribe" + relevant_inherent_trait = TRAIT_WTA_GAROU_TRIBE + must_have_relevant_trait = TRUE + should_generate_icons = TRUE + +/datum/preference/choiced/garou_tribe/init_possible_values() + return assoc_to_keys(GLOB.tribes_list) // This would be inclusive of ALL tribes so many need to be reworked when adding other fera + +/datum/preference/choiced/garou_tribe/icon_for(value) + var/datum/universal_icon/tribe_icon = uni_icon('icons/effects/effects.dmi', "nothing") + tribe_icon.blend_icon(uni_icon('modular_darkpack/modules/werewolf_the_apocalypse/icons/tribes.dmi', replacetext(LOWER_TEXT(value), " ", "_")), ICON_OVERLAY) + return tribe_icon + +/datum/preference/choiced/garou_tribe/apply_to_human(mob/living/carbon/human/target, value) + target.set_fera_tribe(value, TRUE) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/silver_damage.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/silver_damage.dm new file mode 100644 index 000000000000..3932efaa0f8f --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/silver_damage.dm @@ -0,0 +1,41 @@ +// W20 p. 259 +/datum/status_effect/stacking/silver_bullets + id = "silver_bullet_stacks" + tick_interval = 1 TURNS + delay_before_decay = 1 SCENES + stacks = 1 + stack_threshold = 5 + max_stacks = 5 + // This renders ONTOP of the mob. Not as a status effect. Which is prob what we need to do. + // overlay_file = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/silver_dam_status.dmi' + // overlay_state = "silver" + +/datum/status_effect/stacking/silver_bullets/threshold_cross_effect() + var/datum/splat/werewolf/shifter/splat = isshifter(owner) + if(splat) + splat.adjust_gnosis(-1, TRUE) + + +/obj/projectile/bullet/proc/fera_silver_damage(mob/living/carbon/human/target, dice = 0) + if(!istype(target)) + return + var/datum/splat/werewolf/shifter/shot_pup_splat = isshifter(target) + if(shot_pup_splat) + var/mob/living/carbon/human/shot_pup = target + shot_pup.apply_status_effect(STATUS_EFFECT_SILVER_BULLLET_STACKS) + + if(!shot_pup_splat.is_breed_form()) + // IDK. This is might TTRPG inaccurate RN because i think it should acctaully convert ALL the damage to agg not just add some agg to it. + shot_pup.apply_damage(dice TTRPG_DAMAGE, AGGRAVATED) + +/obj/item/proc/fera_silver_damage(mob/living/carbon/human/target, dice = 0, gnosis_damage = 0) + if(!istype(target)) + return + var/datum/splat/werewolf/shifter/shot_pup_splat = isshifter(target) + if(shot_pup_splat) + var/mob/living/carbon/human/shot_pup = target + shot_pup_splat.adjust_gnosis(-gnosis_damage, TRUE) + + // W20 p. 290 - Werewolves dont take silver damage in breed form because they arent spirits + if(!shot_pup_splat.is_breed_form()) + shot_pup.apply_damage(dice TTRPG_DAMAGE, AGGRAVATED) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/species/garou_organs.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/garou_organs.dm new file mode 100644 index 000000000000..d956c2c45b63 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/species/garou_organs.dm @@ -0,0 +1,53 @@ +// Organs and limbs are applied where it makes sense to limited behavoir. +// e.g only the proper dogs on all 4s get the brain as that is to restrict there use of tools and force biting. + +/obj/item/bodypart/head/fera + // limb_id = SPECIES_FERA + head_flags = NONE + unarmed_attack_sound = 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_bite.ogg' + +/obj/item/bodypart/chest/fera + // limb_id = SPECIES_FERA + +/obj/item/bodypart/arm/left/fera + // limb_id = SPECIES_FERA + unarmed_attack_verbs = list("claw") + unarmed_attack_verbs_continuous = list("claws") + appendage_noun = "paw" + unarmed_attack_effect = ATTACK_EFFECT_CLAW + unarmed_attack_sound = 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_bite.ogg' + unarmed_miss_sound = 'sound/items/weapons/slashmiss.ogg' + +/obj/item/bodypart/arm/right/fera + // limb_id = SPECIES_FERA + unarmed_attack_verbs = list("claw") + unarmed_attack_verbs_continuous = list("claws") + appendage_noun = "paw" + unarmed_attack_effect = ATTACK_EFFECT_CLAW + unarmed_attack_sound = 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_bite.ogg' + unarmed_miss_sound = 'sound/items/weapons/slashmiss.ogg' + +/obj/item/bodypart/leg/left/fera + // limb_id = SPECIES_FERA + +/obj/item/bodypart/leg/right/fera + // limb_id = SPECIES_FERA + + +// Specificly to restrict use of tools... because that was moved to the brain.. +/obj/item/organ/brain/fera + name = "exotic brain" + organ_traits = list(TRAIT_LITERATE, TRAIT_CAN_STRIP) + +/obj/item/organ/brain/fera/get_attacking_limb(mob/living/carbon/human/target) + if(!HAS_TRAIT(owner, TRAIT_ADVANCEDTOOLUSER) || HAS_TRAIT(owner, TRAIT_FERAL_BITER)) + return owner.get_bodypart(BODY_ZONE_HEAD) + return ..() + +/obj/item/organ/tongue/fera + name = "exotic tongue" + languages_native = list(/datum/language/garou_tongue) + +// Garou tongues can speak all default + garou tongue +/obj/item/organ/tongue/fera/get_possible_languages() + return ..() + /datum/language/garou_tongue diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/examine_text.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/examine_text.dm new file mode 100644 index 000000000000..94f123df8a6d --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/examine_text.dm @@ -0,0 +1,46 @@ +/datum/splat/werewolf/proc/examine_other_human(mob/living/carbon/examined) + var/datum/splat/werewolf/wolp_splat = iswerewolfsplat(examined) + if(wolp_splat) + var/list/honor_flavor = list("claim to good conduct", "claim to honor", "claim to chivalry") + var/list/wisdom_flavor = list("claim to insight", "claim to wisdom", "claim to sagacity") + var/list/glory_flavor = list("claim to bravery", "claim to valor", "claim to glory") + + var/same_tribe = FALSE + var/is_known = FALSE + + if(!wolp_splat.tribe || !wolp_splat.auspice) + return + if(tribe.name == wolp_splat.tribe.name) + same_tribe = TRUE + + switch(wolp_splat.renown_rank) + if(RANK_CUB to RANK_FOSTERN) + if(same_tribe) + . += "You know [examined.p_them()] as \a [fera_rank_name(wolp_splat.renown_rank)] of the [wolp_splat.tribe.name]." + is_known = TRUE + if(RANK_ADREN to RANK_LEGEND) + . += "You know [examined.p_them()] as \a [fera_rank_name(wolp_splat.renown_rank)] [wolp_splat.auspice.name] of the [wolp_splat.tribe.name]." + is_known = TRUE + + if(is_known) + switch(wolp_splat.renown[RENOWN_HONOR]) + if(4,5,6) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [honor_flavor[1]]." + if(7,8,9) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [honor_flavor[2]]." + if(10) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [honor_flavor[3]]." + switch(wolp_splat.renown[RENOWN_WISDOM]) + if(4,5,6) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [wisdom_flavor[1]]." + if(7,8,9) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [wisdom_flavor[2]]." + if(10) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [wisdom_flavor[3]]." + switch(wolp_splat.renown[RENOWN_GLORY]) + if(4,5,6) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [glory_flavor[1]]." + if(7,8,9) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [glory_flavor[2]]." + if(10) + . += "In the local Garou, you have heard of [examined.p_their(TRUE)] [glory_flavor[3]]." diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_species.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_species.dm new file mode 100644 index 000000000000..384cfec4311f --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_species.dm @@ -0,0 +1,302 @@ +//Required so werewolves can almost entirely override body rendering +/mob/living/carbon/human/update_body_parts(update_limb_data) + if(dna?.species?.update_body_parts(src)) + return + return ..() + +/datum/species/proc/update_body_parts(mob/living/carbon/human/human) + return + +/mob/living/carbon/human/update_damage_overlays() + if(dna?.species?.update_damage_overlays(src)) + return + return ..() + +/datum/species/proc/update_damage_overlays(mob/living/carbon/human/human) + return + + +/datum/species/human/shifter + abstract_type = /datum/species/human/shifter + name = "Fera" + plural_form = "Fera" + id = SPECIES_FERA + species_language_holder = /datum/language_holder/garou + var/mob_pixel_w + var/mob_pixel_z + /// Stats added and removed upon gaining the species + var/list/form_bonus_stats = list() + /// Dice roll difficulty required to shift into this form + var/shift_difficulty = 6 + /// If update_body_parts is allowed to override the body render + var/custom_body_render = FALSE + /// If update_damage_parts is allowed to override the damage render + var/custom_damage_render = FALSE + /// Fallback dmi to refrence if we fail to get one from our splat + var/fallback_icon + /// Speed mod applied and removed upon gaining this species + var/speed_mod + /// Causes delerium, which if the user is affected by, does not cause breaches + var/causes_delerium + /// IF this form can be witnessed, causes masqurade breaches + var/veil_breaching_form = FALSE + +/datum/species/human/shifter/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load, regenerate_icons) + . = ..() + if(speed_mod) + human_who_gained_species.add_movespeed_modifier(speed_mod) + + human_who_gained_species.add_offsets(type, w_add = mob_pixel_w, z_add = mob_pixel_z) + + for(var/key, value in form_bonus_stats) + human_who_gained_species.st_add_stat_mod(key, value, type) + +/datum/species/human/shifter/on_species_loss(mob/living/carbon/human/human, datum/species/new_species, pref_load) + . = ..() + if(speed_mod) + human.remove_movespeed_modifier(speed_mod) + + human.remove_offsets(type) + + for(var/key, value in form_bonus_stats) + human.st_remove_stat_mod(key, type) + + +/// Fetch the mobs fur color from their features. +/datum/species/human/shifter/proc/get_fur_color(mob/living/carbon/human/human) + return human.dna.features[FEATURE_FUR_COLOR] ? human.dna.features[FEATURE_FUR_COLOR] : "black" + + +/// Fetch the mob dmi from our splat +/datum/species/human/shifter/proc/get_mob_icon(mob/living/carbon/human/human) + var/datum/splat/werewolf/shifter/shifter_splat = isshifter(human) + var/icon_to_use + if(shifter_splat) + icon_to_use = shifter_splat.mob_icons[id] + + return icon_to_use ? icon_to_use : fallback_icon + +/datum/species/human/shifter/update_body_parts(mob/living/carbon/human/human) + if(!custom_body_render) + return FALSE + + human.remove_overlay(BODYPARTS_LAYER) + + var/fur_color = get_fur_color(human) + var/mob_icon = get_mob_icon(human) + + var/main_iconstate = "" + if(HAS_TRAIT(human, TRAIT_WYRMTAINTED)) + main_iconstate += "spiral" + main_iconstate += fur_color + if(human.body_position == LYING_DOWN) + main_iconstate += "_rest" + + human.overlays_standing[BODYPARTS_LAYER] = list(image(mob_icon, main_iconstate)) + + human.apply_overlay(BODYPARTS_LAYER) + + return TRUE + +/datum/species/human/shifter/update_damage_overlays(mob/living/carbon/human/human) + if(!custom_damage_render) + return FALSE + + human.remove_overlay(DAMAGE_LAYER) + + var/dam_amount + switch(human.get_brute_loss() + human.get_fire_loss() + human.get_agg_loss()) + if(25 to 100) + dam_amount = 1 + if(100 to 250) + dam_amount = 2 + if(250 to INFINITY) + dam_amount = 3 + if(dam_amount) + human.overlays_standing[DAMAGE_LAYER] = mutable_appearance(get_mob_icon(human), "damage[dam_amount][human.body_position == LYING_DOWN ? "_rest" : ""]") + + human.apply_overlay(DAMAGE_LAYER) + + return TRUE + +/datum/species/human/shifter/homid + name = "homid form" + id = SPECIES_FERA_HOMID + + +/datum/species/human/shifter/bestial + name = "bestial form" + id = SPECIES_FERA_BESTIAL + form_bonus_stats = list( + STAT_STRENGTH = 2, + STAT_STAMINA = 2, + STAT_MANIPULATION = -2, + STAT_APPEARANCE = -1 + ) + shift_difficulty = 7 + fallback_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/glabro.dmi' + veil_breaching_form = TRUE + +/datum/species/human/shifter/bestial/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load, regenerate_icons) + . = ..() + human_who_gained_species.update_mob_height() + human_who_gained_species.update_transform(1.25) + + + human_who_gained_species.remove_overlay(BODY_ADJ_LAYER) + + var/fur_color = get_fur_color(human_who_gained_species) + var/mob_icon = get_mob_icon(human_who_gained_species) + + human_who_gained_species.overlays_standing[BODY_ADJ_LAYER] = list(image(mob_icon, fur_color)) + + human_who_gained_species.apply_overlay(BODY_ADJ_LAYER) + +/datum/species/human/shifter/bestial/on_species_loss(mob/living/carbon/human/human, datum/species/new_species, pref_load) + . = ..() + human.update_mob_height() + human.update_transform() + human.remove_overlay(BODY_ADJ_LAYER) + +/datum/species/human/shifter/bestial/update_species_heights(mob/living/carbon/human/holder) + if(HAS_TRAIT(holder, TRAIT_DWARF)) + return HUMAN_HEIGHT_MEDIUM + + if(HAS_TRAIT(holder, TRAIT_TOO_TALL)) + return HUMAN_HEIGHT_TALLEST + + return HUMAN_HEIGHT_TALL + + +/datum/species/human/shifter/war + name = "war form" + id = SPECIES_FERA_WAR + inherent_traits = list( + TRAIT_NO_UNDERWEAR, + TRAIT_NO_BLOOD_OVERLAY, + TRAIT_NO_LYING_ANGLE, + TRAIT_TRANSFORM_UPDATES_ICON, + ) + causes_delerium = TRUE + veil_breaching_form = TRUE + + mutanttongue = /obj/item/organ/tongue/fera + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/fera, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/fera, + BODY_ZONE_HEAD = /obj/item/bodypart/head/fera, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/fera, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/fera, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/fera, + ) + + no_equip_flags = ITEM_SLOT_ON_BODY + + visible_gender_override = "beast" + + form_bonus_stats = list( + STAT_STRENGTH = 4, + STAT_STAMINA = 3, + STAT_DEXTERITY = 1, + STAT_MANIPULATION = -3, + // STAT_APPEARANCE = 0 // NOT YET SUPPORTED + ) + mob_pixel_w = -8 + custom_body_render = TRUE + custom_damage_render = TRUE + fallback_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi' + speed_mod = /datum/movespeed_modifier/shifter/war + +/datum/species/human/shifter/dire + name = "dire form" + id = SPECIES_FERA_DIRE + inherent_traits = list( + TRAIT_NO_UNDERWEAR, + TRAIT_NO_BLOOD_OVERLAY, + TRAIT_NO_LYING_ANGLE, + TRAIT_TRANSFORM_UPDATES_ICON, + TRAIT_FERAL_BITER, + TRAIT_SMALL_HANDS, + ) + veil_breaching_form = TRUE + + mutantbrain = /obj/item/organ/brain/fera + mutanttongue = /obj/item/organ/tongue/fera + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/fera, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/fera, + BODY_ZONE_HEAD = /obj/item/bodypart/head/fera, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/fera, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/fera, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/fera, + ) + + no_equip_flags = ITEM_SLOT_ON_BODY + + visible_gender_override = "beast" + + form_bonus_stats = list( + STAT_STRENGTH = 3, + STAT_STAMINA = 3, + STAT_DEXTERITY = 2, + STAT_MANIPULATION = -3, + ) + shift_difficulty = 7 + mob_pixel_w = -16 + mob_pixel_z = -8 + custom_body_render = TRUE + custom_damage_render = TRUE + fallback_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/hispo.dmi' + speed_mod = /datum/movespeed_modifier/shifter/dire + +/datum/species/human/shifter/feral + name = "feral form" + id = SPECIES_FERA_FERAL + inherent_traits = list( + TRAIT_NO_UNDERWEAR, + TRAIT_NO_BLOOD_OVERLAY, + TRAIT_NO_LYING_ANGLE, + TRAIT_TRANSFORM_UPDATES_ICON, + TRAIT_FERAL_BITER, + TRAIT_SMALL_HANDS, + ) + + mutantbrain = /obj/item/organ/brain/fera + mutanttongue = /obj/item/organ/tongue/fera + bodypart_overrides = list( + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/fera, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/fera, + BODY_ZONE_HEAD = /obj/item/bodypart/head/fera, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/fera, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/fera, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/fera, + ) + + no_equip_flags = ITEM_SLOT_ON_BODY + + visible_gender_override = "wolf" + + form_bonus_stats = list( + STAT_STRENGTH = 1, + STAT_STAMINA = 2, + STAT_DEXTERITY = 2, + STAT_MANIPULATION = -3, + ) + custom_body_render = TRUE + custom_damage_render = TRUE + fallback_icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi' + speed_mod = /datum/movespeed_modifier/shifter/feral + +/datum/movespeed_modifier/shifter + abstract_type = /datum/movespeed_modifier/shifter + movetypes = GROUND + +// Verify these nums are ttrpg accurate. +/datum/movespeed_modifier/shifter/war + multiplicative_slowdown = -0.1 + +/datum/movespeed_modifier/shifter/dire + multiplicative_slowdown = -0.3 + +/datum/movespeed_modifier/shifter/feral + multiplicative_slowdown = -0.5 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_splat.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_splat.dm new file mode 100644 index 000000000000..1b5e676b5e10 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/fera_splat.dm @@ -0,0 +1,175 @@ +// Represents the system not that they are a werewolf/fera +/datum/splat/werewolf + abstract_type = /datum/splat/werewolf + + power_type = /datum/action/cooldown/power/gift + + var/uses_rage = FALSE + var/rage = 0 + // without a merit kinfolk cannot use gnosis + var/uses_gnosis = FALSE + var/gnosis = 0 + + var/list/renown = list() + var/renown_rank = RANK_CUB + + var/datum/subsplat/werewolf/breed_form/breed_form + var/datum/subsplat/werewolf/auspice/auspice + var/datum/subsplat/werewolf/tribe/tribe + +/datum/splat/werewolf/proc/adjust_rage(amount, sound = TRUE) + if(!uses_rage) + return FALSE + + if(amount > 0) + if(rage < MAX_RAGE) + rage = min(MAX_RAGE, rage+amount) + if(sound) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_increase.ogg', volume = 50)) + to_chat(owner, span_userdanger("RAGE INCREASES")) + else + return FALSE + if(amount < 0) + if(rage > 0) + rage = max(0, rage+amount) + if(sound) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_decrease.ogg', volume = 50)) + to_chat(owner, span_userdanger("RAGE DECREASES")) + else + return FALSE + + owner.update_werewolf_hud() + return TRUE + +/datum/splat/werewolf/proc/adjust_gnosis(amount, sound = TRUE) + if(!uses_gnosis) + return FALSE + + if(amount > 0) + if(gnosis < MAX_GNOSIS) + gnosis = clamp(gnosis + amount, 0, MAX_GNOSIS) + if(sound) + SEND_SOUND(owner, sound('modular_darkpack/modules/deprecated/sounds/humanity_gain.ogg', volume = 50)) + to_chat(owner, span_boldnotice("GNOSIS INCREASES")) + else + return FALSE + if(amount < 0) + if(gnosis > 0) + gnosis = clamp(gnosis + amount, 0, MAX_GNOSIS) + if(sound) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_decrease.ogg', volume = 50)) + to_chat(owner, span_boldnotice("GNOSIS DECREASES")) + else + return FALSE + + owner.update_werewolf_hud() + return TRUE + + +/datum/splat/werewolf/kinfolk + name = "Kinfolk" + id = SPLAT_KINFOLK + // incompatible_splats = list(/datum/splat/werewolf/shifter) // TODO: Becoming a shifter should get rid of your kinfolk splat + +/datum/splat/werewolf/shifter + abstract_type = /datum/splat/werewolf/shifter + splat_traits = list( + TRAIT_WTA_GAROU_BREED, + TRAIT_WTA_GAROU_AUSPICE, + TRAIT_WTA_GAROU_TRIBE, + TRAIT_FERA_FUR, + TRAIT_FRENETIC_AURA + ) + // id = SPLAT_FERA + incompatible_splats = list( + /datum/splat/werewolf + ) // We dont support being multiple fera or gaining kinfolk as a fera + uses_rage = TRUE + uses_gnosis = TRUE + + var/list/transformation_list = list() + var/transform_sound = 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/transform.ogg' + COOLDOWN_DECLARE(transform_cd) + /** + * [SPECIES_ID -> dmi path] assoc list + * + * Only required for forms that you can into (corax lack dire and bestial) + * and acctually have custom sprite behavoir (homid are exempt, bestial are fluff added to homid) + */ + var/list/mob_icons = list( + SPECIES_FERA_BESTIAL = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/glabro.dmi', + SPECIES_FERA_WAR = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi', + SPECIES_FERA_DIRE = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/hispo.dmi', + SPECIES_FERA_FERAL = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi' + ) + COOLDOWN_DECLARE(passive_healing_cd) + COOLDOWN_DECLARE(gnosis_regain_cd) + +/datum/splat/werewolf/shifter/on_gain() + . = ..() + owner.set_species(/datum/species/human/shifter/homid) + add_power(/datum/action/cooldown/power/gift/howling) + + RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(revert_to_breed_form)) + +/datum/splat/werewolf/shifter/on_lose_or_destroy() + . = ..() + if(!QDELETED(owner)) + owner.set_species(/datum/species/human) + + UnregisterSignal(owner, COMSIG_LIVING_DEATH) + +/datum/splat/werewolf/shifter/splat_life(seconds_per_tick) + regain_gnosis_process(seconds_per_tick) + if(COOLDOWN_FINISHED(src, passive_healing_cd)) + // Metis heal in all forms. Lupus and homid born dont heal FAST FAST in their breed form + // their fast healing is represented in day/days in breed-form so we just dont. + if(is_breed_form() && (get_breed_form_species() != /datum/species/human/shifter/war)) + return + owner.heal_storyteller_health(1, heal_scars = TRUE, heal_blood = TRUE) + COOLDOWN_START(src, passive_healing_cd, 1 TURNS) + var/datum/species/human/shifter/shifter_species = owner.dna.species + if(istype(shifter_species)) + if(shifter_species.veil_breaching_form && !shifter_species.causes_delerium) + SEND_SIGNAL(owner, COMSIG_MASQUERADE_VIOLATION) + +// Being used to represent meditating in your caern +/datum/splat/werewolf/shifter/proc/regain_gnosis_process(seconds_per_tick) + if(!COOLDOWN_FINISHED(src, gnosis_regain_cd)) + return + for(var/obj/structure/werewolf_totem/totem in GLOB.totems) + if(totem.broken) + continue + if(!(tribe.name in totem.tribes)) + continue + if(get_area(totem) != get_area(owner)) + continue + adjust_gnosis(1, TRUE) + COOLDOWN_START(src, gnosis_regain_cd, 1 SCENES) + +/datum/splat/werewolf/shifter/garou + name = "Garou" + id = SPLAT_GAROU + transformation_list = list( + /datum/species/human/shifter/homid, + /datum/species/human/shifter/bestial, + /datum/species/human/shifter/war, + /datum/species/human/shifter/dire, + /datum/species/human/shifter/feral + ) + +/* // DARKPACK TODO - CORAX +/datum/splat/werewolf/shifter/corax + name = "Corax" + id = SPLAT_CORAX + transformation_list = list( + /datum/species/human/shifter/homid, + /datum/species/human/shifter/war, + /datum/species/human/shifter/feral + ) + mob_icons = list( + SPECIES_FERA_WAR = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/crinos.dmi', + SPECIES_FERA_FERAL = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/corvid.dmi' + ) + transform_sound = 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/corax_transform.ogg' +*/ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/is_werewolf_helpers.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/is_werewolf_helpers.dm new file mode 100644 index 000000000000..427b1be1b8ca --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/is_werewolf_helpers.dm @@ -0,0 +1,30 @@ +/** + * If the character is any kind of fera or kinfolk creature, named after the game line + */ +/proc/iswerewolfsplat(mob/character) + RETURN_TYPE(/datum/splat/werewolf) + + return character.get_splat(/datum/splat/werewolf) + +/proc/isshifter(mob/character) + RETURN_TYPE(/datum/splat/werewolf/shifter) + + return character.get_splat(/datum/splat/werewolf/shifter) + +/proc/isgarou(mob/character) + RETURN_TYPE(/datum/splat/werewolf/shifter/garou) + + return character.get_splat(/datum/splat/werewolf/shifter/garou) + +/* // DARKPACK TODO - CORAX +/proc/iscorax(mob/character) + RETURN_TYPE(/datum/splat/werewolf/shifter/cora + + return character.get_splat(/datum/splat/werewolf/shifter/corax) +*/ + +/proc/iskinfolk(mob/character) + RETURN_TYPE(/datum/splat/werewolf/kinfolk) + + return character.get_splat(/datum/splat/werewolf/kinfolk) + diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/pref_lore.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/pref_lore.dm new file mode 100644 index 000000000000..a37c33482a3e --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/pref_lore.dm @@ -0,0 +1,52 @@ +/datum/splat/werewolf/kinfolk/prepare_human_for_preview(mob/living/carbon/human/human) + human.set_haircolor("#C3BA88", update = FALSE) + human.set_eye_color("B2B2B2", "B2B2B2") + human.set_hairstyle("Bangs (Diagonal Alt)", update = TRUE) + human.undershirt = "Shirt (Ian)" + human.update_body() + +// DARKPACK TODO - WEREWOLF - (len lore) +/datum/splat/werewolf/kinfolk/get_splat_description() + return "Lorem Ipsum" + +// DARKPACK TODO - WEREWOLF - (len lore) +/datum/splat/werewolf/kinfolk/get_splat_lore() + return list( + "Lorem Ipsum", + ) + +/datum/splat/werewolf/shifter/garou/prepare_human_for_preview(mob/living/carbon/human/human) + human.set_haircolor("#502D15", update = FALSE) + human.set_hairstyle("Long Hair 3", update = TRUE) + human.undershirt = "Shirt (Alien)" + human.update_body() + +// DARKPACK TODO - WEREWOLF - (len lore) +/datum/splat/werewolf/shifter/garou/get_splat_description() + return "Lorem Ipsum" + +// DARKPACK TODO - WEREWOLF - (len lore) +/datum/splat/werewolf/shifter/garou/get_splat_lore() + return list( + "Lorem Ipsum", + ) + +/datum/splat/werewolf/shifter/garou/create_pref_unique_perks() + var/list/to_add = list() + + to_add += list( + list( + SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK, + SPECIES_PERK_ICON = FA_ICON_DOG, + SPECIES_PERK_NAME = "Shapeshifting", + SPECIES_PERK_DESC = "Garou can shift between 5 diffrent forms that grant them bonus.", + ), + list( + SPECIES_PERK_TYPE = SPECIES_POSITIVE_PERK, + SPECIES_PERK_ICON = FA_ICON_BAND_AID, + SPECIES_PERK_NAME = "Passive healing", + SPECIES_PERK_DESC = "Garou have a strong passive healing while outside of their breed form.", + ) + ) + + return to_add diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/renown.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/renown.dm new file mode 100644 index 000000000000..2ae22c6d2604 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/renown.dm @@ -0,0 +1,100 @@ +#define MAX_RENOWN 10 + +/datum/splat/werewolf/proc/adjust_renown(attribute, amount) + if(!renown[attribute]) + renown[attribute] = 0 + + + var/old_rank = renown_rank + var/new_amount = clamp(renown[attribute] + amount, 0, MAX_RENOWN) + + renown[attribute] = new_amount + if(amount < 0) + to_chat(owner, span_userdanger("You feel [get_negative_emotion(attribute)]!")) + else if(amount > 0) + to_chat(owner, span_bold("You feel [get_positive_emotion(attribute)]!")) + + switch(attribute) + if(RENOWN_HONOR) + owner.write_preference_midround(/datum/preference/numeric/renown/honor, new_amount) + if(RENOWN_GLORY) + owner.write_preference_midround(/datum/preference/numeric/renown/glory, new_amount) + if(RENOWN_WISDOM) + owner.write_preference_midround(/datum/preference/numeric/renown/wisdom, new_amount) + + renown_rank = auspice_rank_check() + if(old_rank != renown_rank) + to_chat(owner, span_boldnotice("You are now a [fera_rank_name(renown_rank)].")) + + owner.write_preference_midround(/datum/preference/numeric/fera_rank, renown_rank) + + +/datum/splat/werewolf/proc/get_negative_emotion(attribute) + switch(attribute) + if(RENOWN_HONOR) + return "ashamed" + + if(RENOWN_GLORY) + return "humiliated" + + if(RENOWN_WISDOM) + return "foolish" + + return "unsure" + +/datum/splat/werewolf/proc/get_positive_emotion(attribute) + switch(attribute) + + if(RENOWN_HONOR) + return "vindicated" + + if(RENOWN_GLORY) + return "brave" + + if(RENOWN_WISDOM) + return "clever" + + return "confident" + + +/datum/splat/werewolf/proc/auspice_rank_check() + return auspice.rank_requirments(renown) + +// Pretty iffy on this. This could likely just be moved onto the splat itself so corax and other breeds can override it. +/proc/fera_rank_name(rank, breed) + + // if(breed != "Corax") DARKPACK TODO - CORAX + switch(rank) + if(RANK_CUB) + return "cub" // in lowercase so that \a might function during the character examine + if(RANK_CLIATH) + return "cliath" + if(RANK_FOSTERN) + return "fostern" + if(RANK_ADREN) + return "adren" + if(RANK_ATHRO) + return "athro" + if(RANK_ELDER) + return "elder" + if(RANK_LEGEND) + return "legend" +/* DARKPACK TODO - CORAX + switch(rank) + if(0) + return "fledgling" + if(1) + return "oviculum" + if(2) + return "neocornix" + if(3) + return "ales" + if(4) + return "volucris" + if(5) + return "corvus" + if(6) + return "grey eminence" +*/ + +#undef MAX_RENOWN diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/transformation.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/transformation.dm new file mode 100644 index 000000000000..5988945b512a --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/transformation.dm @@ -0,0 +1,79 @@ +#define DOGGY_ANIMATION_TIME 1 TURNS + +/datum/storyteller_roll/fera_trans + bumper_text = "transformation" + applicable_stats = list(STAT_STAMINA) + + +// Remeber if you remove homid being species that this will break. +/datum/splat/werewolf/shifter/proc/transform_fera(datum/species/human/shifter/form_to_transform, costs_rage = FALSE, requires_roll = TRUE, force = FALSE) + if(!form_to_transform) + return + if(!istype(owner)) + return + if(!(form_to_transform in transformation_list)) + return + if(owner?.dna?.species?.type == form_to_transform) + return + if(!force && !COOLDOWN_FINISHED(src, transform_cd)) + to_chat(owner, span_warning("Your shifting is on cooldown for one turn.")) + return + + if(ispath(get_breed_form_species(), form_to_transform)) + requires_roll = FALSE + else if(costs_rage) + if(adjust_rage(-1, TRUE)) + requires_roll = FALSE + else + to_chat(owner, span_warning("You don't have enough RAGE to do that!")) + SEND_SOUND(owner, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_cast_failed.ogg', volume = 50)) + return + + COOLDOWN_START(src, transform_cd, 1 TURNS) + var/time_to_transform = DOGGY_ANIMATION_TIME + + // TODO: should accctually require an amount of successes equal to the forms your shifting through + if(requires_roll) + var/datum/storyteller_roll/fera_trans/transform_roll = new() + transform_roll.difficulty = form_to_transform::shift_difficulty + switch(transform_roll.st_roll(owner, owner, PRIMAL_URGE_PLACEHOLDER)) + if(ROLL_SUCCESS) + EMPTY_BLOCK_GUARD + if(ROLL_FAILURE, ROLL_BOTCH) + return + + // If it doesnt require a roll it must be instant/free action + if(requires_roll) + playsound(owner, transform_sound, 50, FALSE) + else + playsound(owner, 'modular_darkpack/modules/werewolf_the_apocalypse/sounds/speedtrans.ogg', 50, FALSE) + time_to_transform *= 0.1 + + // owner.Stun(time_to_transform, ignore_canstun = TRUE) + + var/matrix/ntransform = matrix(owner.transform) + ntransform.Scale(1.1, 1.1) + animate(owner, transform = ntransform, color = "#000000", time = time_to_transform * 0.9) + + SEND_SIGNAL(owner, COMSIG_MASQUERADE_VIOLATION) + + addtimer(CALLBACK(src, PROC_REF(transform_finish), form_to_transform, time_to_transform), time_to_transform * 0.9) + +/datum/splat/werewolf/shifter/proc/revert_to_breed_form() + transform_fera(get_breed_form_species(), force = TRUE) + +/datum/splat/werewolf/shifter/proc/transform_finish(form_to_transform, time_taken = DOGGY_ANIMATION_TIME) + animate(owner, transform = null, color = "#FFFFFF", time = time_taken * 0.1) + owner.set_species(form_to_transform) + +/datum/splat/werewolf/shifter/proc/is_breed_form() + if(!owner?.dna) + return FALSE + if(owner.dna.species?.type != get_breed_form_species()) + return FALSE + return TRUE + +/datum/splat/werewolf/shifter/proc/get_breed_form_species() + return breed_form?.breed_species + +#undef DOGGY_ANIMATION_TIME diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/varediting.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/varediting.dm new file mode 100644 index 000000000000..0e490eefd889 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/varediting.dm @@ -0,0 +1,37 @@ +#define VV_HK_EDIT_RENOWN "edit_renown" + +/datum/splat/werewolf/vv_get_dropdown() + . = ..() + VV_DROPDOWN_OPTION("", "---------") + VV_DROPDOWN_OPTION(VV_HK_EDIT_RENOWN, "Edit Renown") + +/datum/splat/werewolf/vv_do_topic(list/href_list) + . = ..() + + if(!.) + return + + if(href_list[VV_HK_EDIT_RENOWN]) + if(!check_rights(R_SPAWN)) + return + var/list/options = ALL_RENOWNS + var/result = tgui_input_list(usr, "Please choose a renown to adjust", "Renown", options) + if(!result) + return + var/amount = tgui_input_number(usr, "Enter to renown add/remove.", "Renown", 1, 10, -10) + if(isnull(amount)) + return + adjust_renown(result, amount) + + +/datum/splat/werewolf/vv_edit_var(var_name, var_value) + . = ..() + if(!.) + return + switch(var_name) + if(NAMEOF(src, rage)) + owner.update_werewolf_hud() + if(NAMEOF(src, gnosis)) + owner.update_werewolf_hud() + +#undef VV_HK_EDIT_RENOWN diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/werewolf_gift_management.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/werewolf_gift_management.dm new file mode 100644 index 000000000000..587d1f8c2f36 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/splats/werewolf_gift_management.dm @@ -0,0 +1,31 @@ +/datum/splat/werewolf/get_power(power_type) + RETURN_TYPE(/datum/action/cooldown/power/gift) + + for(var/datum/action/cooldown/power/gift/found_action as anything in powers) + if(!istype(found_action, power_type)) + continue + + return found_action + +/datum/splat/werewolf/add_power(power_type, level) + // Prevent duplicates + if(get_power(power_type)) + return FALSE + var/datum/action/cooldown/power/gift/adding_action = new power_type() + adding_action.Grant(owner) + LAZYADD(powers, adding_action) + return TRUE + +/datum/splat/werewolf/remove_power(power_type) + var/datum/action/cooldown/power/gift/found_action = get_power(power_type) + if(!found_action) + return FALSE + + LAZYREMOVE(powers, found_action) + qdel(found_action) + return TRUE + +/datum/splat/werewolf/change_power_level(power_type, new_level) + return // We dont really have discs in the same way, and we dont have diffrent power levels for an individual power? + + diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/status_effects/delirium.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/status_effects/delirium.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/status_effects/rage_heal.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/status_effects/rage_heal.dm new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/_subsplat.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/_subsplat.dm new file mode 100644 index 000000000000..c4f78ab568cc --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/_subsplat.dm @@ -0,0 +1,16 @@ +/datum/subsplat/werewolf + abstract_type = /datum/subsplat/werewolf + // Currently un-implemented + /// Fera required to have this subsplat. If null its takeable by any splat. + var/fera_restriction + + // At present it grants all of them but this is a mechanical limitation while i wait for the disc rework. + // /datum/action/cooldown/power/gift + /// All gifts avalible via this subsplat. + var/list/gifts_provided = list() + +/datum/subsplat/werewolf/on_gain(datum/splat/gaining, joining_round) + . = ..() + // Placeholder! + for(var/gift in gifts_provided) + gaining.add_power(gift) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/_auspice.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/_auspice.dm new file mode 100644 index 000000000000..4564a2b57334 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/_auspice.dm @@ -0,0 +1,77 @@ + + +/datum/subsplat/werewolf/auspice + abstract_type = /datum/subsplat/werewolf/auspice + + var/start_rage + + var/moons_born_under = list() + +/datum/subsplat/werewolf/auspice/on_gain(datum/splat/gaining, joining_round) + . = ..() + if(istype(gaining, /datum/splat/werewolf)) + var/datum/splat/werewolf/werewolf_splat = gaining + werewolf_splat.adjust_rage(start_rage) + +/datum/subsplat/werewolf/auspice/proc/rank_requirments(list/renown) + return 0 + +/** + * Gets the singleton of an Auspice + * from its name, typepath, or returns the + * argument if given a Auspice singleton. + * + * Arguments: + * * auspice_identifier - Name, typepath, or singleton of the Auspice being retrieved + */ +/proc/get_fera_auspice(auspice_identifier) + RETURN_TYPE(/datum/subsplat/werewolf/auspice) + + if (ispath(auspice_identifier)) + return GLOB.auspices[auspice_identifier] + else if (istext(auspice_identifier)) + return GLOB.auspices[GLOB.auspices_list[auspice_identifier]] + else + return auspice_identifier + +/** + * Gives the human a Auspice, applying + * on_gain effects and post_gain effects if the + * parameter is true. Can also remove Auspices + * with or without a replacement, and apply + * on_lose effects. Will have no effect the human + * is being given the Auspice it already has. + * + * Arguments: + * * setting_auspice - Typepath or Auspice singleton to give to the human + * * joining_round - If this Auspice is being given at roundstart and should call on_join_round + */ +/mob/living/carbon/human/proc/set_auspice(setting_auspice, joining_round) + var/datum/subsplat/werewolf/auspice/previous_auspice = get_our_auspice() + + // Convert IDs and typepaths to singletons, or just directly assign if already singleton + var/datum/subsplat/werewolf/auspice/new_auspice = get_fera_auspice(setting_auspice) + + // Handle losing Auspice + previous_auspice?.on_lose(src) + + var/datum/splat/werewolf/shifter/shifter = isshifter(src) + if (!shifter) + return + + shifter.auspice = new_auspice + + // Auspice's been cleared, don't apply effects + if (!new_auspice) + return + + // Gaining Auspice effects + new_auspice.on_gain(shifter, joining_round) + +/mob/living/proc/get_our_auspice() + RETURN_TYPE(/datum/subsplat/werewolf/auspice) + + return isshifter(src)?.auspice + +/mob/living/proc/is_auspice(auspice_type) + return istype(get_our_auspice(), auspice_type) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/garou.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/garou.dm new file mode 100644 index 000000000000..c045c1e49215 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/auspices/garou.dm @@ -0,0 +1,142 @@ +/datum/subsplat/werewolf/auspice/garou + abstract_type = /datum/subsplat/werewolf/auspice/garou + fera_restriction = SPLAT_GAROU + + +/datum/subsplat/werewolf/auspice/garou/ahroun + name = AUSPICE_AHROUN + desc = "The Ahroun is the archetype of the werewolf as murderous beast, though they range from unapologetic berserkers to hardened veterans tempering their Rage with discipline. Their high levels of Rage put them on the edge at all times - the Full Moon's blessing is a hair trigger, among other things. Those closer to the waxing moon tend to exult in the glory of the war, while those closer to the waning moon are more viciously pragmatic, ruthless in their bloodthirst. Every Ahroun is a dangerous individual to be around, but when the forces of the Wyrm attack, their packmates are glad to have a Full Moon warrior at the front of the charge." + start_rage = 5 + gifts_provided= list(/datum/action/cooldown/power/gift/falling_touch)//, /datum/action/cooldown/power/gift/inspiration, /datum/action/cooldown/power/gift/razor_claws) + moons_born_under = list(MOON_FULL) + +/datum/subsplat/werewolf/auspice/garou/ahroun/rank_requirments(list/renown) + var/glory = renown[RENOWN_GLORY] + var/honor = renown[RENOWN_HONOR] + var/wisdom = renown[RENOWN_WISDOM] + + if(glory >= 10 && honor >= 9 && wisdom >= 4) + return RANK_ELDER + if(glory >= 9 && honor >= 4 && wisdom >= 2) + return RANK_ATHRO + if(glory >= 6 && honor >= 3 && wisdom >= 1) + return RANK_ADREN + if(glory >= 4 && honor >= 1 && wisdom >= 1) + return RANK_FOSTERN + if(glory >= 2 || honor >= 1) + return RANK_CLIATH + return RANK_CUB + +/datum/subsplat/werewolf/auspice/garou/galliard + name = AUSPICE_GALLIARD + desc = "Where the Philodox is stoic, the Galliard is a creature of unbridled passion. The Gibbous Moon is a fiery muse, and stirs its children into great heights and depths of emotion. While all Galliards are prone to immense mirth and immense melancholy, those born under a waning moon fall more readily into dark, consuming passions; they are the tragedians of the Garou, mastering tales of doom, ruin, sacrifice and loss. Conversely, their waxing-moon cousins sing of triumph and conquest, of the pounding heart and the love of life. They tend to be the soul of their pack's morale - when the Galliard is willing to go on, so too are all the others." + start_rage = 4 + gifts_provided = list( + // /datum/action/cooldown/power/gift/beast_speech, + // /datum/action/cooldown/power/gift/call_of_the_wyld, + /datum/action/cooldown/power/gift/mindspeak + ) + moons_born_under = list(MOON_WAXING_GIBBOUS, MOON_WANING_GIBBOUS) + +/datum/subsplat/werewolf/auspice/garou/galliard/rank_requirments(list/renown) + var/glory = renown[RENOWN_GLORY] + var/honor = renown[RENOWN_HONOR] + var/wisdom = renown[RENOWN_WISDOM] + + if(glory >= 9 && honor >= 5 && wisdom >= 9) + return RANK_ELDER + if(glory >= 7 && honor >= 2 && wisdom >= 6) + return RANK_ATHRO + if(glory >= 4 && honor >= 2 && wisdom >= 4) + return RANK_ADREN + if(glory >= 4 && wisdom >= 2) + return RANK_FOSTERN + if(glory >= 2 && wisdom >= 1) + return RANK_CLIATH + return RANK_CUB + +/datum/subsplat/werewolf/auspice/garou/philodox + name = AUSPICE_PHILODOX + desc = "Buried so heavily in his role as impartial judge and jury, the Philodox may seem aloof, even surprisingly cold-blooded for a werewolf. Those born under the waxing Half Moon may seem unusually serene and disaffected, their emotions only emerging when their Rage comes to a boil. The waning-moon Philodox is more incisive and judgmental, his all-seeing eye always carefully watching his packmates and colleagues for any departure from the expected. The Half Moons' opinions are somewhat feared, yet highly respected - a word of praise or condemnation means much coming from those born to see both sides of every struggle." + start_rage = 3 + gifts_provided= list(/datum/action/cooldown/power/gift/scent_of_the_true_form)//,/datum/action/cooldown/power/gift/resist_pain, /datum/action/cooldown/power/gift/truth_of_gaia) + moons_born_under = list(MOON_FIRST_QUARTER, MOON_LAST_QUARTER) + +/datum/subsplat/werewolf/auspice/garou/philodox/rank_requirments(list/renown) + var/glory = renown[RENOWN_GLORY] + var/honor = renown[RENOWN_HONOR] + var/wisdom = renown[RENOWN_WISDOM] + + if(glory >= 4 && honor >= 10 && wisdom >= 9) + return RANK_ELDER + if(glory >= 3 && honor >= 8 && wisdom >= 4) + return RANK_ATHRO + if(glory >= 2 && honor >= 6 && wisdom >= 2) + return RANK_ADREN + if(glory >= 1 && honor >= 4 && wisdom >= 1) + return RANK_FOSTERN + if(honor >= 3) + return RANK_CLIATH + return RANK_CUB + + +/datum/subsplat/werewolf/auspice/garou/theurge + name = AUSPICE_THEURGE + desc = "The Crescent Moons can be strange and enigmatic, prone to falling into the convoluted symbolic logic of the spirits they truck with rather than the more familiar logic of humanity. Those Theurges born under the waning moon frequently have a harsher, more adversarial relationship with the spirit world - they tend to excel at binding and forcing spirits to their will, and are more vicious when battling spirits. Theurges born under the waxing moon tend to be more generous and open with the spirits, charming and cajoling rather than intimidating and threatening." + start_rage = 2 + gifts_provided = list( + /datum/action/cooldown/power/gift/mothers_touch, + // /datum/action/cooldown/power/gift/sense_wyrm, + // /datum/action/cooldown/power/gift/spirit_speech + ) + moons_born_under = list(MOON_WANING_CRESCENT, MOON_WAXING_CRESENT) + +/datum/subsplat/werewolf/auspice/garou/theurge/rank_requirments(list/renown) + var/glory = renown[RENOWN_GLORY] + var/honor = renown[RENOWN_HONOR] + var/wisdom = renown[RENOWN_WISDOM] + + if(glory >= 4 && honor >= 9 && wisdom >= 10) + return RANK_ELDER + if(glory >= 4 && honor >= 2 && wisdom >= 9) + return RANK_ATHRO + if(glory >= 2 && honor >= 1 && wisdom >= 7) + return RANK_ADREN + if(glory >= 1 && wisdom >= 5) + return RANK_FOSTERN + if(wisdom >= 3) + return RANK_CLIATH + return RANK_CUB + + +/datum/subsplat/werewolf/auspice/garou/ragabash + name = AUSPICE_RAGABASH + desc = "The Ragabash born under the waxing new moon is usually light-hearted and capricious, while one born under the waning new moon has a slightly more wicked and ruthless streak. It's a rare Ragabash indeed that lacks a keen wit and the capacity to find some humor in any situation, no matter how bleak. Many other werewolves are slow to take the Ragabash seriously, though, as it's difficult to tell the difference between a New Moon's mockery that points out a grievous flaw in a plan and similar mockery that simply amuses him. Sometimes a Ragabash points out that the emperor has no clothes - but sometimes they're the first to cry wolf, so to speak." + start_rage = 1 + gifts_provided= list( + // /datum/action/cooldown/power/gift/blur_of_the_milky_eye, + /datum/action/cooldown/power/gift/infectious_laughter + // /datum/action/cooldown/power/gift/open_seal, + ) + moons_born_under = list(MOON_NEW) + +/datum/subsplat/werewolf/auspice/garou/ragabash/rank_requirments(list/renown) + var/total_score = renown[RENOWN_GLORY] + renown[RENOWN_HONOR] + renown[RENOWN_WISDOM] + + if(total_score >= 25) + return RANK_ELDER + if(total_score >= 19) + return RANK_ATHRO + if(total_score >= 13) + return RANK_ADREN + if(total_score >= 7) + return RANK_FOSTERN + if(total_score >= 3) + return RANK_CLIATH + return RANK_CUB + +/datum/subsplat/werewolf/auspice/garou/stolen_moon + name = AUSPICE_NONE + // DARKPACK TODO - WEREWOLF - (len lore) + desc = "Your not a dog are you." + // Stolen moon get no gifts diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/_breed.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/_breed.dm new file mode 100644 index 000000000000..aeaee128398c --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/_breed.dm @@ -0,0 +1,73 @@ +// Not a splat in the TTRPG but functions like one in terms of code. +/datum/subsplat/werewolf/breed_form + abstract_type = /datum/subsplat/werewolf/breed_form + + var/start_gnosis + + var/breed_species + +/datum/subsplat/werewolf/breed_form/on_gain(datum/splat/gaining, joining_round) + . = ..() + if(istype(gaining, /datum/splat/werewolf)) + var/datum/splat/werewolf/werewolf_splat = gaining + werewolf_splat.adjust_gnosis(start_gnosis) + +/** + * Gets the singleton of an breed_form + * from its name, typepath, or returns the + * argument if given a breed_form singleton. + * + * Arguments: + * * breed_form_identifier - Name, typepath, or singleton of the breed_form being retrieved + */ +/proc/get_fera_breed_form(breed_form_identifier) + RETURN_TYPE(/datum/subsplat/werewolf/breed_form) + + if (ispath(breed_form_identifier)) + return GLOB.breed_forms[breed_form_identifier] + else if (istext(breed_form_identifier)) + return GLOB.breed_forms[GLOB.breed_forms_list[breed_form_identifier]] + else + return breed_form_identifier + +/** + * Gives the human a breed_form, applying + * on_gain effects and post_gain effects if the + * parameter is true. Can also remove breed_forms + * with or without a replacement, and apply + * on_lose effects. Will have no effect the human + * is being given the breed_form it already has. + * + * Arguments: + * * setting_breed_form - Typepath or breed_form singleton to give to the human + * * joining_round - If this breed_form is being given at roundstart and should call on_join_round + */ +/mob/living/carbon/human/proc/set_breed_form(setting_breed_form, joining_round) + var/datum/subsplat/werewolf/breed_form/previous_breed_form = get_our_breed_form() + + // Convert IDs and typepaths to singletons, or just directly assign if already singleton + var/datum/subsplat/werewolf/breed_form/new_breed_form = get_fera_breed_form(setting_breed_form) + + // Handle losing breed_form + previous_breed_form?.on_lose(src) + + var/datum/splat/werewolf/shifter/shifter = isshifter(src) + if (!shifter) + return + + shifter.breed_form = new_breed_form + + // breed_form's been cleared, don't apply effects + if (!new_breed_form) + return + + // Gaining breed_form effects + new_breed_form.on_gain(shifter, joining_round) + +/mob/living/proc/get_our_breed_form() + RETURN_TYPE(/datum/subsplat/werewolf/breed_form) + + return isshifter(src)?.breed_form + +/mob/living/proc/is_breed_form(breed_form_type) + return istype(get_our_breed_form(), breed_form_type) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/garou.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/garou.dm new file mode 100644 index 000000000000..89a797ef5237 --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/breeds/garou.dm @@ -0,0 +1,18 @@ +/datum/subsplat/werewolf/breed_form/garou + abstract_type = /datum/subsplat/werewolf/breed_form/garou + fera_restriction = SPLAT_GAROU + +/datum/subsplat/werewolf/breed_form/garou/homid + name = BREED_HOMID + start_gnosis = 1 + breed_species = /datum/species/human/shifter/homid + +/datum/subsplat/werewolf/breed_form/garou/metis + name = BREED_CRINOS + start_gnosis = 3 + breed_species = /datum/species/human/shifter/war + +/datum/subsplat/werewolf/breed_form/garou/lupus + name = BREED_LUPUS + start_gnosis = 5 + breed_species = /datum/species/human/shifter/feral diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/_tribe.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/_tribe.dm new file mode 100644 index 000000000000..07ca1d1efc9f --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/_tribe.dm @@ -0,0 +1,65 @@ +/datum/subsplat/werewolf/tribe + abstract_type = /datum/subsplat/werewolf/tribe + + var/tribe_keys + //var/tribe_trait + +/** + * Gets the singleton of an tribe + * from its name, typepath, or returns the + * argument if given a tribe singleton. + * + * Arguments: + * * tribe_identifier - Name, typepath, or singleton of the tribe being retrieved + */ +/proc/get_fera_tribe(tribe_identifier) + RETURN_TYPE(/datum/subsplat/werewolf/tribe) + + if (ispath(tribe_identifier)) + return GLOB.fera_tribes[tribe_identifier] + else if (istext(tribe_identifier)) + return GLOB.fera_tribes[GLOB.tribes_list[tribe_identifier]] + else + return tribe_identifier + +/** + * Gives the human a tribe, applying + * on_gain effects and post_gain effects if the + * parameter is true. Can also remove tribes + * with or without a replacement, and apply + * on_lose effects. Will have no effect the human + * is being given the tribe it already has. + * + * Arguments: + * * setting_tribe - Typepath or tribe singleton to give to the human + * * joining_round - If this tribe is being given at roundstart and should call on_join_round + */ +/mob/living/carbon/human/proc/set_fera_tribe(setting_tribe, joining_round) + var/datum/subsplat/werewolf/tribe/previous_tribe = get_our_tribe() + + // Convert IDs and typepaths to singletons, or just directly assign if already singleton + var/datum/subsplat/werewolf/tribe/new_tribe = get_fera_tribe(setting_tribe) + + // Handle losing tribe + previous_tribe?.on_lose(src) + + var/datum/splat/werewolf/shifter/shifter = isshifter(src) + if (!shifter) + return + + shifter.tribe = new_tribe + + // tribe's been cleared, don't apply effects + if (!new_tribe) + return + + // Gaining tribe effects + new_tribe.on_gain(shifter, joining_round) + +/mob/living/proc/get_our_tribe() + RETURN_TYPE(/datum/subsplat/werewolf/tribe) + + return isshifter(src)?.tribe + +/mob/living/proc/is_tribe(tribe_type) + return istype(get_our_tribe(), tribe_type) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/garou.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/garou.dm new file mode 100644 index 000000000000..fcf46b013c7f --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/subsplats/tribes/garou.dm @@ -0,0 +1,164 @@ + +/datum/subsplat/werewolf/tribe/garou + abstract_type = /datum/subsplat/werewolf/tribe/garou + fera_restriction = SPLAT_GAROU + +/datum/subsplat/werewolf/tribe/garou/galestalkers + name = TRIBE_GALESTALKERS + desc = "Tireless trackers and peerless hunters, the galestalkers carry the namesake of the wind that crosses the tundra." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/ghostcouncil + name = TRIBE_UKTENA + desc = "Seekers of mystery and highly secretive, the Uktena is one of the most misunderstood tribes. Their ranks include guides, academics and the religious." + // gifts_provided = list( + // /datum/action/gift/shroud = 1, + // /datum/action/gift/coils_of_the_serpent = 2, + // /datum/action/gift/banish_totem = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/hartwardens + name = TRIBE_FIANNA + desc = "Growing, creating, cultivating and maintaining the most natural of Gaia's creations, the Wardens are some of the closest to nature. Wherever they are, they coax Gaia's blessing out of whatever they can." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/glasswalkers + name = TRIBE_GLASS_WALKERS + desc = "The closest to the Weaver, they find themselves deeply entrenched in modern human society, religion, technology and cities. Every new invention and every new discovery is one that aids the Glass Walkers, instead of impeding them." + // gifts_provided = list( + // /datum/action/gift/smooth_move = 1, + // /datum/action/gift/digital_feelings = 2, + // /datum/action/gift/elemental_improvement = 3 + // ) + tribe_keys = /obj/item/vamp/keys/techstore + +/datum/subsplat/werewolf/tribe/garou/bonegnawers + name = TRIBE_BONE_GNAWERS + desc = "Survivors and scavengers, often destitute and homeless. The Gnawers are seen as mongrels who live off scraps, but they know better. They're the true survivors, patiently waiting for their moment to strike against overconfident foes." + // gifts_provided = list( + // /datum/action/gift/guise_of_the_hound = 1, + // /datum/action/gift/infest = 2, + // /datum/action/gift/gift_of_the_termite = 3 + // ) + tribe_keys = /obj/item/vamp/keys/children_of_gaia + +/datum/subsplat/werewolf/tribe/garou/childrenofgaia + name = TRIBE_CHILDREN_OF_GAIA + desc = "Peacekeepers, negotiators, treaty-makers and philosophers. The Children of Gaia strive as hard as they can create an understanding and unity between the disparate tribes that will allow them to form a united front against their foes." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/children_of_gaia + +/datum/subsplat/werewolf/tribe/garou/getoffenris + name = TRIBE_GET_OF_FENRIS + desc = "Warriors, compassionate and fierce. They view themselves are Gaia's strongest heroes, but the rest of the tribes view them with caution, their violence more famous than their courage." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/blackfuries + name = TRIBE_BLACK_FURIES + desc = "An all-female tribe, and the matriarchs of the Garou. The Black Furies are known fondly for their honor, wisdom, pride and impressive prowess in battle." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/silentstriders + name = TRIBE_SILENT_STRIDERS + desc = "Highly spiritual nomads, the Silent Striders have headed deeper and longer into the depths of the Umbra than any other tribe." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/shadowlords + name = TRIBE_SHADOW_LORDS + desc = "The closest one could consider a Garou to being a 'politician'. They manipulate the tribes, and their enemies, and rely on cunning and wits more than physical strength. Not to say there aren't adept warriors in their ranks, but the tribe tends towards brains than brawn." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/techstore + +/datum/subsplat/werewolf/tribe/garou/redtalons + name = TRIBE_RED_TALONS + desc = "Exclusively consisting of lupus, the Red Talons shun humanity and think of them as a blight on Gaia." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + +/datum/subsplat/werewolf/tribe/garou/silverfangs + name = TRIBE_SILVER_FANGS + desc = "Commonly known as the 'Alphas' of the Garou Nation, their ranks consist of traditional rulers and wartime leaders. Known for being honorable and having courage, odd mental quirks have begun plaguing their young members, and the tribe is beginning to suffer from diseases of the spirit and mind." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/stargazers + name = TRIBE_STARGAZERS + desc = "The calmest of the Garou, they are well known for their introversion. They are the smallest of the remaining tribes, many of their kind wiped out by the Wyrm." + // gifts_provided = list( + // /datum/action/gift/stoic_pose = 1, + // /datum/action/gift/freezing_wind = 2, + // /datum/action/gift/bloody_feast = 3 + // ) + tribe_keys = /obj/item/vamp/keys/nps + +/datum/subsplat/werewolf/tribe/garou/blackspiraldancers + name = TRIBE_BLACK_SPIRAL_DANCERS + desc = "The lost tribe. The dreadwolves. Those who dance lockstep with the Wyrm. They who have entered the labyrinth and come back, changed.\n{THIS IS AN ADVANCED TRIBE AND NOT RECOMMENDED FOR BEGINNERS. LORE KNOWLEDGE IS REQUIRED TO PLAY THIS TRIBE}" + // gifts_provided = list( + // /datum/action/gift/stinky_fur = 1, + // /datum/action/gift/venom_claws = 2, + // /datum/action/gift/burning_scars = 3 + // ) + +/datum/subsplat/werewolf/tribe/garou/ronin + name = TRIBE_RONIN + desc = "Garou who, for one reason or another, find themselves as outcasts of the Nation." + // gifts_provided = list( + // /datum/action/gift/guise_of_the_hound = 1, + // /datum/action/gift/stoic_pose = 2, + // /datum/action/gift/smooth_move = 3, + // /datum/action/gift/shroud = 4 + // ) + +/* // DARKPACK TODO - CORAX +/datum/subsplat/werewolf/tribe/garou/corax + name = TRIBE_CORAX + desc = "{CONSIDER : THIS IS A PLACEHOLDER, FEATURES WILL BE MISSING.} \nMessengers of Gaia, children of Raven, and scions of Helios; the wereravens travel accross the globe, guided by their innate curiosity and insatiable thirst for gossip. \nThey are renowned for their ability to gather useful intelligence, and the difficulty of making them stop talking." + gifts_provided = list( + /datum/action/gift/eye_drink = 1, + /datum/action/gift/smooth_move = 2, + /datum/action/gift/suns_guard = 3 + ) + tribe_trait = TRAIT_CORAX +*/ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/totems.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/totems.dm index 67c5c06971de..5566401817ef 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/totems.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/totems.dm @@ -1,172 +1,242 @@ /obj/structure/werewolf_totem + abstract_type = /obj/structure/werewolf_totem name = "tribe totem" - desc = "Gives power to all Garou of that tribe and steals it from others." + desc = "Gives power to all Garou of that tribe." icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/totems.dmi' - icon_state = "glassw" - plane = GAME_PLANE - layer = SPACEVINE_LAYER + icon_state = "wendigo" + base_icon_state = "wendigo" anchored = TRUE density = TRUE - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - var/tribe - var/totem_health = 500 - var/obj/overlay/totem_light_overlay - var/totem_overlay_color = "#FFFFFF" - var/last_rage = 0 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + max_integrity = 500 // Fuck you flav for having this orginally be snowflaked health + + // light_color = "#FFFFFF" + light_range = 3 + light_power = 0.5 + damage_deflection = 5 + + var/tribes = list() + + COOLDOWN_DECLARE(rage_notify_cd) var/turf/teleport_turf var/opening = FALSE -// DARKPACK TODO - GAROU - Fuck this not respecting normal integrity -/* -/obj/structure/werewolf_totem/attackby(obj/item/I, mob/living/user, params) - . = ..() - if(I.force) - adjust_totem_health(round(I.force/2)) - -/obj/structure/werewolf_totem/bullet_act(obj/projectile/P, def_zone, piercing_hit = FALSE) - . = ..() - adjust_totem_health(round(P.damage/2)) /obj/structure/werewolf_totem/Initialize(mapload) . = ..() + var/list/candidates = list() for(var/obj/effect/landmark/teleport_mark/T in GLOB.landmarks_list) - if(T.tribe == tribe) - teleport_turf = get_turf(T) - set_light(3, 0.5, totem_overlay_color) + for(var/entry in T.tribes) + if(entry in tribes) + candidates += T + break + + if(!length(candidates)) + if(mapload) + log_mapping("[src] failed to find a candidate for an exit point.") + else + var/candidate = pick(candidates) + teleport_turf = get_turf(candidate) + qdel(candidate) GLOB.totems += src - totem_light_overlay = new(src) - totem_light_overlay.icon = icon - totem_light_overlay.icon_state = "[icon_state]_overlay" - totem_light_overlay.plane = ABOVE_LIGHTING_PLANE - totem_light_overlay.layer = ABOVE_LIGHTING_LAYER - totem_light_overlay.color = totem_overlay_color - overlays |= totem_light_overlay - -/obj/structure/werewolf_totem/proc/adjust_totem_health(amount) - if(amount > 0) - if(totem_health == 0) + + update_icon(UPDATE_ICON) + +/obj/structure/werewolf_totem/update_icon_state() + . = ..() + + if(broken) + icon_state = "[base_icon_state]_broken" + else + icon_state = base_icon_state + +/obj/structure/werewolf_totem/update_overlays() + . = ..() + + var/mutable_appearance/totem_light_overlay = mutable_appearance(icon, "[icon_state]_overlay") + SET_PLANE(totem_light_overlay, ABOVE_LIGHTING_PLANE, src) + totem_light_overlay.color = light_color + // totem_light_overlay.layer = ABOVE_LIGHTING_LAYER + . += totem_light_overlay + +/obj/structure/werewolf_totem/Destroy(force) + . = ..() + GLOB.totems -= src + +/obj/structure/werewolf_totem/take_damage(damage_amount, damage_type = BRUTE, damage_flag = "", sound_effect = TRUE, attack_dir, armour_penetration = 0) + . = ..() + if(. && !broken) + if(!COOLDOWN_FINISHED(src, rage_notify_cd)) + return . + notify_fera(-damage_amount) + COOLDOWN_START(src, rage_notify_cd, 5 SECONDS) + +/obj/structure/werewolf_totem/atom_break(damage_flag) + . = ..() + break_totem() + +/obj/structure/werewolf_totem/atom_destruction(damage_flag) + SHOULD_CALL_PARENT(FALSE) + break_totem() + +/obj/structure/werewolf_totem/atom_fix() + . = ..() + set_light(initial(light_range)) + update_icon(UPDATE_ICON) + notify_fera(1) + +/obj/structure/werewolf_totem/proc/break_totem() + if(broken) + return + broken = TRUE + set_light(0) + update_icon(UPDATE_ICON) + var/obj/umbra_portal/prev = locate() in get_step(src, SOUTH) + if(prev) + collapse_portal(prev) + notify_fera(-1) + +/obj/structure/werewolf_totem/proc/notify_fera(damage_change) + for(var/mob/living/carbon/human/human in GLOB.player_list) + var/datum/splat/werewolf/shifter/shifter_splat = isshifter(human) + if(!istype(shifter_splat)) + continue + if(human.stat == DEAD) + continue + if(!(shifter_splat.tribe.name in tribes)) + continue + + if(damage_change < 0) + if(broken) + to_chat(human, span_userdanger("YOUR TOTEM IS DESTROYED")) + SEND_SOUND(human, sound('sound/effects/tendril_destroyed.ogg', volume = 50)) + shifter_splat.adjust_gnosis(-5, FALSE) + else + to_chat(human, span_userdanger("YOUR TOTEM IS BREAKING DOWN")) + SEND_SOUND(human, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/bumps.ogg', volume = 50)) + shifter_splat.adjust_rage(1, FALSE) + else + to_chat(human, span_boldnotice("YOUR TOTEM IS RESTORED")) + SEND_SOUND(human, sound('modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspire.ogg', volume = 50)) + shifter_splat.adjust_gnosis(1, FALSE) + +/obj/structure/werewolf_totem/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(user.combat_mode) + attack_generic(user, rand(user.melee_damage_lower, user.melee_damage_upper)) + else + var/datum/splat/werewolf/shifter/shifter_splat = isshifter(user) + if(!istype(shifter_splat)) + return . + if(broken) + to_chat(user, span_warning("[src] is broken!")) return - totem_health = max(0, totem_health-amount) - if(totem_health == 0) - icon_state = "[initial(icon_state)]_broken" - overlays -= totem_light_overlay - totem_light_overlay.icon_state = "[icon_state]_overlay" - overlays |= totem_light_overlay - for(var/mob/living/carbon/C in GLOB.player_list) - if(iswerewolf(C) || isgarou(C)) - if(C.stat != DEAD) - if(C.auspice.tribe == tribe) - set_light(0) - to_chat(C, span_userdanger("YOUR TOTEM IS DESTROYED")) - SEND_SOUND(C, sound('sound/effects/tendril_destroyed.ogg', volume = 75)) - adjust_gnosis(-5, C, FALSE) - var/obj/umbra_portal/prev = locate() in get_step(src, SOUTH) - if(prev) - qdel(prev.exit) - qdel(prev) + var/obj/umbra_portal/prev = locate() in get_step(src, SOUTH) + if(!prev) + if(shifter_splat.auspice.name == AUSPICE_THEURGE) + if(!opening) + opening = TRUE + if(do_after(user, 10 SECONDS, src)) + spawn_portal() + opening = FALSE + else + to_chat(user, span_warning("You need a Theurge to open the Moon Gates!")) else - for(var/mob/living/carbon/C in GLOB.player_list) - if(iswerewolf(C) || isgarou(C)) - if(C.stat != DEAD) - if(C.auspice.tribe == tribe) - if(last_rage+50 < world.time) - last_rage = world.time - to_chat(C, span_userdanger("YOUR TOTEM IS BREAKING DOWN")) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/bumps.ogg', volume = 75)) - adjust_rage(1, C, FALSE) - if(amount < 0) - totem_health = min(initial(totem_health), totem_health-amount) - if(totem_health > 0) - if(icon_state != initial(icon_state)) - for(var/mob/living/carbon/C in GLOB.player_list) - if(iswerewolf(C) || isgarou(C)) - if(C.stat != DEAD) - if(C.auspice.tribe == tribe) - to_chat(C, span_userhelp("YOUR TOTEM IS RESTORED")) - SEND_SOUND(C, sound('modular_darkpack/modules/deprecated/sounds/inspire.ogg', volume = 75)) - adjust_gnosis(1, C, FALSE) - icon_state = "[initial(icon_state)]" - overlays -= totem_light_overlay - totem_light_overlay.icon_state = "[icon_state]_overlay" - overlays |= totem_light_overlay -*/ + if(shifter_splat.auspice.name == AUSPICE_THEURGE) + collapse_portal(prev) + +/obj/structure/werewolf_totem/proc/spawn_portal() + var/obj/umbra_portal/prev = locate() in get_step(src, SOUTH) + if(prev) + collapse_portal(prev) + playsound(src, 'modular_darkpack/modules/deprecated/sounds/portal.ogg', 50, FALSE) + var/obj/umbra_portal/U = new (get_step(src, SOUTH)) + // New code doesnt relay on ID for these two's connections buy why not. + U.id = "[pick(tribes)][rand(1, 999)]" + var/obj/umbra_portal/P = new (teleport_turf) + P.id = U.id + U.link_portal(P) + +/obj/structure/werewolf_totem/proc/collapse_portal(obj/umbra_portal/old_portal) + playsound(src, 'modular_darkpack/modules/deprecated/sounds/portal.ogg', 50, FALSE) + qdel(old_portal.exit) + qdel(old_portal) /obj/structure/werewolf_totem/wendigo - name = "\improper Wendigo totem" - desc = "Gives power to all Garou of that tribe and steals it from others." - icon_state = "wendigo" - tribe = "Wendigo" - totem_overlay_color = "#81ff4f" + name = "\improper " + TRIBE_GALESTALKERS + " totem" + tribes = list(TRIBE_GALESTALKERS) + light_color = "#81ff4f" /obj/structure/werewolf_totem/children_of_gaia - name = "Children of Gaia Totem" - desc = "Gives power to all Garou of that tribe and steals it from others." - icon_state = "wendigo" - tribe = "Children of Gaia" - totem_overlay_color = "#00CEC8" + name = "\improper " + TRIBE_CHILDREN_OF_GAIA + " totem" + tribes = list(TRIBE_CHILDREN_OF_GAIA) + light_color = "#00CEC8" /obj/structure/werewolf_totem/bone_gnawer - name = "Bone Gnawer Totem" - desc = "Gives power to all Garou of that tribe and steals it from others." - icon_state = "wendigo" - tribe = "Bone Gnawers" - totem_overlay_color = "#FFA500" + name = "\improper " + TRIBE_BONE_GNAWERS + " totem" + light_color = "#FFA500" + tribes = list(TRIBE_BONE_GNAWERS) /obj/structure/werewolf_totem/glasswalker - name = "\improper Glasswalker totem" - desc = "Gives power to all Garou of that tribe and steals it from others." + name = "\improper " + TRIBE_GLASS_WALKERS + " totem" icon_state = "glassw" - tribe = "Glasswalkers" - totem_overlay_color = "#35b0ff" + base_icon_state = "glassw" + light_color = "#35b0ff" + tribes = list(TRIBE_GLASS_WALKERS) /obj/structure/werewolf_totem/spiral name = "spiral totem" - desc = "Gives power to all Garou of that tribe and steals it from others." icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/spiral_totem.dmi' icon_state = "spiral" - tribe = "Black Spiral Dancers" - totem_overlay_color = "#ff5235" + base_icon_state = "spiral" + light_color = "#ff5235" + tribes = list(TRIBE_BLACK_SPIRAL_DANCERS) + +/obj/structure/werewolf_totem/generic + light_color = "#81ff4f" + tribes = TRIBE_GAIA + +/obj/structure/werewolf_totem/generic/wyld + light_color = "#81ff4f" + tribes = TRIBE_WYLD + +/obj/structure/werewolf_totem/generic/weaver + icon_state = "glassw" + base_icon_state = "glassw" + light_color = "#35b0ff" + tribes = TRIBE_WEAVER + +/obj/structure/werewolf_totem/generic/wyrm + icon = 'modular_darkpack/modules/werewolf_the_apocalypse/icons/spiral_totem.dmi' + icon_state = "spiral" + base_icon_state = "spiral" + light_color = "#ff5235" + tribes = TRIBE_WYRM + +/obj/structure/werewolf_totem/generic/alltribes + tribes = TRIBE_ALL + + +// This things type path sucks /obj/effect/landmark/teleport_mark - name = "Teleport" - icon_state = "x" - var/tribe + name = "totem Exit Mark" + icon_state = "portal_exit" + var/tribes = list() -// DARKPACK TODO - GAROU -/* -/obj/structure/werewolf_totem/attack_hand(mob/user) - . = ..() - if(iswerewolf(user) || isgarou(user)) - var/mob/living/carbon/C = user - if(C.a_intent != INTENT_HARM) - if(totem_health <= 0) - to_chat(C, span_warning("[src] is broken!")) - return - var/obj/umbra_portal/prev = locate() in get_step(src, SOUTH) - if(!prev) - if(C.auspice.name == "Theurge") - if(!opening) - opening = TRUE - if(do_mob(user, src, 10 SECONDS)) - playsound(loc, 'modular_darkpack/modules/deprecated/sounds/portal.ogg', 75, FALSE) - var/obj/umbra_portal/U = new (get_step(src, SOUTH)) - // New code doesnt relay on ID for these two's connections buy why not. - U.id = "[tribe][rand(1, 999)]" - var/obj/umbra_portal/P = new (teleport_turf) - P.id = U.id - U.link_portal(P) - opening = FALSE - else - opening = FALSE - else - to_chat(C, span_warning("You need a Theurge to open the Moon Gates!")) - else - if(C.auspice.name == "Theurge") - playsound(loc, 'modular_darkpack/modules/deprecated/sounds/portal.ogg', 75, FALSE) - qdel(prev.exit) - qdel(prev) - else - adjust_totem_health(round(C.melee_damage_lower/2)) -*/ +/obj/effect/landmark/teleport_mark/gaia + tribes = TRIBE_GAIA + +/obj/effect/landmark/teleport_mark/wyld + tribes = TRIBE_WYLD + +/obj/effect/landmark/teleport_mark/weaver + tribes = TRIBE_WEAVER + +/obj/effect/landmark/teleport_mark/wyrm + tribes = TRIBE_WYRM + +/obj/effect/landmark/teleport_mark/alltribes + tribes = TRIBE_ALL diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_globals.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_globals.dm index 5fedbf24d242..e112a527852d 100644 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_globals.dm +++ b/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_globals.dm @@ -1,7 +1,65 @@ /// Current phase of the moon, randomly chosen -GLOBAL_VAR_INIT(moon_state, pick("New", "Crescent", "Half", "Gibbous", "Full")) +GLOBAL_VAR_INIT(moon_state, get_moon_phase()) + +#define LUNAR_CYCLE 29.530588 +/proc/get_moon_phase() + // First known fullmoon since the BYOND EPOCH. + var/ref_year = 2000 + var/ref_month = 1 + var/ref_day = 20 + + var/year = text2num(station_time_timestamp("YYYY")) + var/month = text2num(station_time_timestamp("MM")) + var/day = text2num(station_time_timestamp("DD")) + + var/ref_days = ref_year * 365 + ref_month * 30 + ref_day + var/current_days = year * 365 + month * 30 + day + + var/days_since_full = current_days - ref_days + + var/phase_day = days_since_full % LUNAR_CYCLE + if(phase_day < 0) + phase_day += LUNAR_CYCLE + + return moon_phase_name(phase_day) +#undef LUNAR_CYCLE + +/proc/moon_phase_name(phase_day) + if(phase_day < 1.84566) + return MOON_NEW + if(phase_day < 5.53699) + return MOON_WAXING_CRESENT + if(phase_day < 9.22831) + return MOON_FIRST_QUARTER + if(phase_day < 12.91963) + return MOON_WAXING_GIBBOUS + if(phase_day < 16.61096) + return MOON_FULL + if(phase_day < 20.30228) + return MOON_WANING_GIBBOUS + if(phase_day < 23.99361) + return MOON_LAST_QUARTER + if(phase_day < 27.68493) + return MOON_WANING_CRESCENT + return MOON_FULL /// List of all Tribe totems GLOBAL_LIST_EMPTY(totems) + +/// Associative list of auspice names to typepaths +GLOBAL_LIST_INIT(auspices_list, init_subsplat_list(/datum/subsplat/werewolf/auspice)) +/// Associative list of auspice typepaths to singletons +GLOBAL_LIST_INIT_TYPED(auspices, /datum/subsplat/werewolf/auspice, init_subtypes_w_path_keys(/datum/subsplat/werewolf/auspice, list())) + +/// Associative list of tribe names to typepaths +GLOBAL_LIST_INIT(tribes_list, init_subsplat_list(/datum/subsplat/werewolf/tribe)) +/// Associative list of tribe typepaths to singletons +GLOBAL_LIST_INIT_TYPED(fera_tribes, /datum/subsplat/werewolf/tribe, init_subtypes_w_path_keys(/datum/subsplat/werewolf/tribe, list())) + +/// Associative list of breed form names to typepaths +GLOBAL_LIST_INIT(breed_forms_list, init_subsplat_list(/datum/subsplat/werewolf/breed_form)) +/// Associative list of breed_form typepaths to singletons +GLOBAL_LIST_INIT_TYPED(breed_forms, /datum/subsplat/werewolf/breed_form, init_subtypes_w_path_keys(/datum/subsplat/werewolf/breed_form, list())) + GLOBAL_LIST_INIT(glyph_list, init_glyphs()) diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_hud.dm b/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_hud.dm deleted file mode 100644 index 652ee0e5d886..000000000000 --- a/modular_darkpack/modules/werewolf_the_apocalypse/code/werewolf_hud.dm +++ /dev/null @@ -1,249 +0,0 @@ -/atom/movable/screen/werewolf - icon = 'icons/hud/screen_midnight.dmi' - -/datum/hud/werewolf - ui_style = 'icons/hud/screen_midnight.dmi' - -/atom/movable/screen/rage - name = "Rage" - icon = 'code/modules/wod13/48x48.dmi' - icon_state = "rage0" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/transform_homid - name = "Homid" - icon = 'code/modules/wod13/32x48.dmi' - icon_state = "homid" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/transform_homid/Click() - var/mob/living/carbon/C = usr - if(C.stat >= SOFT_CRIT || C.IsSleeping() || C.IsUnconscious() || C.IsParalyzed() || C.IsKnockdown() || C.IsStun()) - return - if(C.transformator) - C.transformator.trans_gender(C, "Homid") - -/atom/movable/screen/transform_crinos - name = "Crinos" - icon = 'code/modules/wod13/32x48.dmi' - icon_state = "crinos" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/transform_crinos/Click() - var/mob/living/carbon/C = usr - if(C.stat >= SOFT_CRIT || C.IsSleeping() || C.IsUnconscious() || C.IsParalyzed() || C.IsKnockdown() || C.IsStun()) - return - if(C.transformator) - C.transformator.trans_gender(C, "Crinos") - -/atom/movable/screen/transform_lupus - name = "Lupus" - icon = 'code/modules/wod13/32x48.dmi' - icon_state = "lupus" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/transform_lupus/Click() - var/mob/living/carbon/C = usr - if(C.stat >= SOFT_CRIT || C.IsSleeping() || C.IsUnconscious() || C.IsParalyzed() || C.IsKnockdown() || C.IsStun()) - return - if(C.transformator) - C.transformator.trans_gender(C, "Lupus") - -/atom/movable/screen/auspice - name = "Auspice" - icon = 'code/modules/wod13/werewolf_ui.dmi' - icon_state = "auspice_bar" - layer = HUD_LAYER - plane = HUD_PLANE - -/atom/movable/screen/auspice/Click() - if(!GLOB.moon_state) - GLOB.moon_state = pick("Full", "Gibbous", "Half", "Crescent", "New") - var/mob/living/carbon/C = usr - if(C.stat >= SOFT_CRIT || C.IsSleeping() || C.IsUnconscious() || C.IsParalyzed() || C.IsKnockdown() || C.IsStun()) - return - var/area/vtm/V = get_area(C) - if(!V.outdoors) - to_chat(C, span_warning("You need to be outside to look at the moon!")) - return - if(C.last_moon_look == 0 || C.last_moon_look+600 < world.time) -// last_moon_look = world.time - C.transformator.lupus_form.last_moon_look = world.time - C.transformator.crinos_form.last_moon_look = world.time - C.transformator.human_form.last_moon_look = world.time - to_chat(C, span_notice("The Moon is [GLOB.moon_state].")) -// icon_state = "[GLOB.moon_state]" - C.emote("howl") - playsound(get_turf(C), pick('modular_darkpack/modules/deprecated/sounds/awo1.ogg', 'modular_darkpack/modules/deprecated/sounds/awo2.ogg'), 100, FALSE) - icon_state = "[GLOB.moon_state]" - spawn(10) - adjust_rage(1, C, TRUE) - -/datum/hud - var/atom/movable/screen/auspice_icon - -/datum/hud/werewolf/New(mob/living/carbon/werewolf/owner) - ..() - - var/atom/movable/screen/using - var/atom/movable/screen/transform_werewolf - -//equippable shit - -//hands - if(iscrinos(owner)) - build_hand_slots() - -//begin buttons - - using = new /atom/movable/screen/fullscreen_hud() - using.screen_loc = ui_full_inventory - using.hud = src - static_inventory += using - - transform_werewolf = new /atom/movable/screen/transform_lupus() - transform_werewolf.screen_loc = ui_werewolf_lupus - transform_werewolf.hud = src - static_inventory += transform_werewolf - - transform_werewolf = new /atom/movable/screen/transform_crinos() - transform_werewolf.screen_loc = ui_werewolf_crinos - transform_werewolf.hud = src - static_inventory += transform_werewolf - - transform_werewolf = new /atom/movable/screen/transform_homid() - transform_werewolf.screen_loc = ui_werewolf_homid - transform_werewolf.hud = src - static_inventory += transform_werewolf - - auspice_icon = new /atom/movable/screen/auspice() - auspice_icon.screen_loc = ui_werewolf_auspice - auspice_icon.hud = src - static_inventory += auspice_icon - - rage_icon = new /atom/movable/screen/rage() - rage_icon.screen_loc = ui_werewolf_rage - rage_icon.hud = src - infodisplay += rage_icon - - if(iscrinos(owner)) - using = new /atom/movable/screen/swap_hand() - using.icon = 'code/modules/wod13/UI/buttons32.dmi' - using.icon_state = "swap_1" - using.screen_loc = ui_swaphand_position(owner,1) - using.hud = src - static_inventory += using - - using = new /atom/movable/screen/swap_hand() - using.icon = 'code/modules/wod13/UI/buttons32.dmi' - using.icon_state = "swap_2" - using.screen_loc = ui_swaphand_position(owner,2) - using.hud = src - static_inventory += using - - using = new /atom/movable/screen/act_intent() - using.icon_state = mymob.a_intent - using.icon = 'code/modules/wod13/UI/buttons32.dmi' - using.hud = src - static_inventory += using - action_intent = using - - using = new/atom/movable/screen/language_menu - using.screen_loc = ui_language_menu - using.icon = 'code/modules/wod13/UI/buttons_wide.dmi' - using.hud = src - static_inventory += using - - using = new /atom/movable/screen/drop() - using.icon = 'code/modules/wod13/UI/buttons_wide.dmi' - using.screen_loc = ui_drop - using.hud = src - static_inventory += using - - using = new /atom/movable/screen/resist() - using.icon = 'code/modules/wod13/UI/buttons_wide.dmi' - using.screen_loc = ui_resist - using.hud = src - hotkeybuttons += using - - throw_icon = new /atom/movable/screen/throw_catch() - throw_icon.icon = 'code/modules/wod13/UI/buttons_wide.dmi' - throw_icon.screen_loc = ui_throw - throw_icon.hud = src - hotkeybuttons += throw_icon - - pull_icon = new /atom/movable/screen/pull() - pull_icon.icon = 'code/modules/wod13/UI/buttons_wide.dmi' - pull_icon.update_icon() - pull_icon.screen_loc = ui_pull - pull_icon.hud = src - static_inventory += pull_icon - -//begin indicators - - healths = new /atom/movable/screen/healths() - healths.icon = 'code/modules/wod13/UI/buttons32.dmi' - healths.hud = src - infodisplay += healths - blood_icon = new /atom/movable/screen/blood() - blood_icon.screen_loc = ui_bloodpool - blood_icon.hud = src - infodisplay += blood_icon - - zone_select = new /atom/movable/screen/zone_sel() - zone_select.icon = 'code/modules/wod13/UI/buttons64.dmi' - zone_select.hud = src - zone_select.update_icon() - static_inventory += zone_select - - for(var/atom/movable/screen/inventory/inv in (static_inventory + toggleable_inventory)) - if(inv.slot_id) - inv.hud = src - inv_slots[TOBITSHIFT(inv.slot_id) + 1] = inv - inv.update_icon() - -/datum/hud/werewolf/persistent_inventory_update() - if(!mymob) - return - if(!iscrinos(mymob)) - return - var/mob/living/carbon/werewolf/H = mymob - if(hud_version != HUD_STYLE_NOHUD) - for(var/obj/item/I in H.held_items) - I.screen_loc = ui_hand_position(H.get_held_index_of_item(I)) - H.client.screen += I - else - for(var/obj/item/I in H.held_items) - I.screen_loc = null - H.client.screen -= I - -/mob/living/carbon/werewolf/Life() - . = ..() - update_blood_hud() - update_rage_hud() - -/mob/living/Initialize(mapload) - . = ..() - gnosis = new(src) - gnosis.icon = 'modular_darkpack/modules/deprecated/icons/48x48.dmi' - gnosis.plane = ABOVE_HUD_PLANE - gnosis.layer = ABOVE_HUD_LAYER - -/mob/living/proc/update_rage_hud() - if(!client || !hud_used) - return - if(isgarou(src) || iswerewolf(src)) - if(hud_used.rage_icon) - hud_used.rage_icon.overlays -= gnosis - var/mob/living/carbon/C = src - hud_used.rage_icon.icon_state = "rage[C.auspice.rage]" - gnosis.icon_state = "gnosis[C.auspice.gnosis]" - hud_used.rage_icon.overlays |= gnosis - if(hud_used.auspice_icon) - var/mob/living/carbon/C = src - if(C.last_moon_look != 0) - hud_used.auspice_icon.icon_state = "[GLOB.moon_state]" diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/auspices.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/auspices.dmi new file mode 100644 index 000000000000..efd83db23af7 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/auspices.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/corvid.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/corvid.dmi new file mode 100644 index 000000000000..2a85620d7f36 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/corvid.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/crinos.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/crinos.dmi new file mode 100644 index 000000000000..1ea873d3f994 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/corax_forms/crinos.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi new file mode 100644 index 000000000000..9debf63f88ee Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/crinos.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/glabro.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/glabro.dmi new file mode 100644 index 000000000000..b9e56bde76da Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/glabro.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/hispo.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/hispo.dmi new file mode 100644 index 000000000000..ebe6d3627c02 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/hispo.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi new file mode 100644 index 000000000000..187416f28ece Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_forms/lupus.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_languages.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_languages.dmi new file mode 100644 index 000000000000..25eb894453a1 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/garou_languages.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_meters.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_meters.dmi new file mode 100644 index 000000000000..784237294bd9 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_meters.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_transforms.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_transforms.dmi new file mode 100644 index 000000000000..c40a0aff234e Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/hud_transforms.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/silver_dam_status.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/silver_dam_status.dmi new file mode 100644 index 000000000000..6b49d8e4aae6 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/silver_dam_status.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/tribes.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/tribes.dmi new file mode 100644 index 000000000000..45e6712d55ce Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/tribes.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi new file mode 100644 index 000000000000..087f71f43b1f Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_abilities.dmi differ diff --git a/modular_darkpack/modules/deprecated/icons/werewolf_lupus.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_lupus.dmi similarity index 100% rename from modular_darkpack/modules/deprecated/icons/werewolf_lupus.dmi rename to modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_lupus.dmi diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_ui.dmi b/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_ui.dmi new file mode 100644 index 000000000000..c6a4c4a02bd5 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/icons/werewolf_ui.dmi differ diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/readme.md b/modular_darkpack/modules/werewolf_the_apocalypse/readme.md new file mode 100644 index 000000000000..0ab88e7f778f --- /dev/null +++ b/modular_darkpack/modules/werewolf_the_apocalypse/readme.md @@ -0,0 +1,28 @@ +https://github.com/DarkPack13/SecondCity/pull/489 + +## \ + +Module ID: WEREWOLF + +### Description: + +This module contains all content from WTA that is strongly codependent, primarly due to its relation to the splat datum. + +### TG Proc/File Changes: + +### Modular Overrides: + +- N/A + +### Defines: + +- `code/__DEFINES/~darkpack/fera/*` + +### Included files that are not contained in this module: + +- `tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_fur_color.tsx` +- `code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_human_shifter_*.png` + +### Credits: + +XeonMations, FalloutFalcon diff --git a/modular_darkpack/modules/deprecated/sounds/bumps.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/bumps.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/bumps.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/bumps.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/corax_transform.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/corax_transform.ogg new file mode 100644 index 000000000000..1d55511f4acd Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/corax_transform.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/awo1.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo1.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/awo1.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo1.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/awo2.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo2.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/awo2.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/awo2.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/cawcrinos.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/cawcrinos.ogg new file mode 100644 index 000000000000..2e1b831bd741 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/cawcrinos.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/crinos_growl.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/crinos_growl.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/crinos_growl.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/crinos_growl.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/female_growl.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/female_growl.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/female_growl.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/female_growl.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/lupus_growl.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/lupus_growl.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/lupus_growl.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/lupus_growl.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/male_growl.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/male_growl.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/male_growl.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/emotes/male_growl.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/falling_touch.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/falling_touch.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch_activate.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch_activate.ogg new file mode 100644 index 000000000000..7b751b8c2561 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/falling_touch_activate.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/infectious_laughter.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/infectious_laughter.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/infectious_laughter.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/infectious_laughter.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/scent_of_the_true_form.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/scent_of_the_true_form.ogg new file mode 100644 index 000000000000..b7ba54caebe5 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/gifts/scent_of_the_true_form.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/ice_blocking.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/ice_blocking.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/ice_blocking.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/ice_blocking.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/inspiration.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspiration.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/inspiration.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspiration.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/inspire.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspire.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/inspire.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/inspire.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/instant_transmission.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/instant_transmission.ogg new file mode 100644 index 000000000000..ec42f8c1447d Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/instant_transmission.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/milky_blur.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/milky_blur.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/milky_blur.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/milky_blur.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/rage_decrease.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_decrease.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/rage_decrease.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_decrease.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/rage_heal.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_heal.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/rage_heal.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_heal.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/rage_increase.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_increase.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/rage_increase.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/rage_increase.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/razor_claws.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/razor_claws.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/razor_claws.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/razor_claws.ogg diff --git a/modular_darkpack/modules/werewolf_the_apocalypse/sounds/speedtrans.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/speedtrans.ogg new file mode 100644 index 000000000000..55eb06319f01 Binary files /dev/null and b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/speedtrans.ogg differ diff --git a/modular_darkpack/modules/deprecated/sounds/transform.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/transform.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/transform.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/transform.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/werewolf_bite.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_bite.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/werewolf_bite.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_bite.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/werewolf_cast_failed.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_cast_failed.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/werewolf_cast_failed.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_cast_failed.ogg diff --git a/modular_darkpack/modules/deprecated/sounds/werewolf_step.ogg b/modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_step.ogg similarity index 100% rename from modular_darkpack/modules/deprecated/sounds/werewolf_step.ogg rename to modular_darkpack/modules/werewolf_the_apocalypse/sounds/werewolf_step.ogg diff --git a/modular_darkpack/modules/z_travel/code/transfer_point.dm b/modular_darkpack/modules/z_travel/code/transfer_point.dm index 04585df8e8c2..54b7308a944b 100644 --- a/modular_darkpack/modules/z_travel/code/transfer_point.dm +++ b/modular_darkpack/modules/z_travel/code/transfer_point.dm @@ -76,7 +76,7 @@ GLOBAL_LIST_EMPTY(unallocted_transfer_points) arrived.forceMove(get_turf(exit)) return TRUE -// Use inside the umbra. Visable +// Use inside the umbra. visible /obj/transfer_point_vamp/umbral name = "portal" icon = 'modular_darkpack/modules/deprecated/icons/48x48.dmi' @@ -85,7 +85,7 @@ GLOBAL_LIST_EMPTY(unallocted_transfer_points) //layer = ABOVE_LIGHTING_LAYER pixel_w = -8 -// Use in the base map/outside the umbra. Invisable +// Use in the base map/outside the umbra. Invisible /obj/transfer_point_vamp/umbral/exit name = "umbral exit" invisibility = INVISIBILITY_OBSERVER diff --git a/tgstation.dme b/tgstation.dme index 5c77866e7e84..2c9465302ad7 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -238,6 +238,7 @@ #include "code\__DEFINES\species_clothing_paths.dm" #include "code\__DEFINES\speech_channels.dm" #include "code\__DEFINES\sprite_accessories.dm" +#include "code\__DEFINES\sprite_editor.dm" #include "code\__DEFINES\stack.dm" #include "code\__DEFINES\stack_trace.dm" #include "code\__DEFINES\stat.dm" @@ -445,7 +446,6 @@ #include "code\__DEFINES\~darkpack\keybindings.dm" #include "code\__DEFINES\~darkpack\library.dm" #include "code\__DEFINES\~darkpack\lock_access_defines.dm" -#include "code\__DEFINES\~darkpack\lockpick_difficulty.dm" #include "code\__DEFINES\~darkpack\mapswitch.dm" #include "code\__DEFINES\~darkpack\masquerade.dm" #include "code\__DEFINES\~darkpack\miscellaneous.dm" @@ -464,8 +464,6 @@ #include "code\__DEFINES\~darkpack\subsystems.dm" #include "code\__DEFINES\~darkpack\vampire_clan.dm" #include "code\__DEFINES\~darkpack\walls.dm" -#include "code\__DEFINES\~darkpack\werewolf_auspice.dm" -#include "code\__DEFINES\~darkpack\werewolf_tribe.dm" #include "code\__DEFINES\~darkpack\xp_and_freebie.dm" #include "code\__DEFINES\~darkpack\dcs\bouncer_signals.dm" #include "code\__DEFINES\~darkpack\dcs\fire_signals.dm" @@ -478,6 +476,10 @@ #include "code\__DEFINES\~darkpack\dcs\signals\signals_global.dm" #include "code\__DEFINES\~darkpack\dcs\signals_mob\signals_mob.dm" #include "code\__DEFINES\~darkpack\dcs\signals_mob\signals_mob_living.dm" +#include "code\__DEFINES\~darkpack\fera\fera.dm" +#include "code\__DEFINES\~darkpack\fera\werewolf_auspice.dm" +#include "code\__DEFINES\~darkpack\fera\werewolf_renown.dm" +#include "code\__DEFINES\~darkpack\fera\werewolf_tribe.dm" #include "code\__DEFINES\~darkpack\traits\declarations.dm" #include "code\__DEFINES\~darkpack\traits\macros.dm" #include "code\__DEFINES\~darkpack\traits\sources.dm" @@ -639,6 +641,7 @@ #include "code\_globalvars\lists\disposals.dm" #include "code\_globalvars\lists\engineering.dm" #include "code\_globalvars\lists\ghost.dm" +#include "code\_globalvars\lists\girder_wall_recipes.dm" #include "code\_globalvars\lists\icons.dm" #include "code\_globalvars\lists\keybindings.dm" #include "code\_globalvars\lists\languages.dm" @@ -910,6 +913,7 @@ #include "code\datums\embedding.dm" #include "code\datums\emotes.dm" #include "code\datums\ert.dm" +#include "code\datums\girder_wall_recipe.dm" #include "code\datums\hailer_phrase.dm" #include "code\datums\holocall.dm" #include "code\datums\hotkeys_help.dm" @@ -1215,6 +1219,7 @@ #include "code\datums\components\crank_recharge.dm" #include "code\datums\components\crate_carrier.dm" #include "code\datums\components\cuff_n_stun.dm" +#include "code\datums\components\cult_kill_tracker.dm" #include "code\datums\components\cult_ritual_item.dm" #include "code\datums\components\curse_of_hunger.dm" #include "code\datums\components\curse_of_polymorph.dm" @@ -1755,6 +1760,7 @@ #include "code\datums\elements\update_icon_blocker.dm" #include "code\datums\elements\update_icon_updates_onmob.dm" #include "code\datums\elements\uplink_reimburse.dm" +#include "code\datums\elements\uses_girder_wall_recipes.dm" #include "code\datums\elements\venomous.dm" #include "code\datums\elements\volatile_gas_storage.dm" #include "code\datums\elements\voucher_redeemer.dm" @@ -2494,6 +2500,7 @@ #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\shield.dm" #include "code\game\objects\effects\spiderwebs.dm" #include "code\game\objects\effects\step_triggers.dm" #include "code\game\objects\effects\wanted_poster.dm" @@ -2525,12 +2532,13 @@ #include "code\game\objects\effects\decals\turfdecal\dirt.dm" #include "code\game\objects\effects\decals\turfdecal\markings.dm" #include "code\game\objects\effects\decals\turfdecal\tilecoloring.dm" +#include "code\game\objects\effects\decals\turfdecal\weakpoint.dm" #include "code\game\objects\effects\decals\turfdecal\weather.dm" -#include "code\game\objects\effects\effect_system\effect_shield.dm" #include "code\game\objects\effects\effect_system\effect_system.dm" #include "code\game\objects\effects\effect_system\effects_explosion.dm" -#include "code\game\objects\effects\effect_system\effects_other.dm" +#include "code\game\objects\effects\effect_system\effects_reagents_explosion.dm" #include "code\game\objects\effects\effect_system\effects_sparks.dm" +#include "code\game\objects\effects\effect_system\effects_trail.dm" #include "code\game\objects\effects\effect_system\effects_water.dm" #include "code\game\objects\effects\effect_system\fluid_spread\_fluid_spread.dm" #include "code\game\objects\effects\effect_system\fluid_spread\effects_foam.dm" @@ -6452,6 +6460,8 @@ #include "code\modules\spells\spell_types\touch\flesh_to_stone.dm" #include "code\modules\spells\spell_types\touch\scream_for_me.dm" #include "code\modules\spells\spell_types\touch\smite.dm" +#include "code\modules\sprite_editing\flood_fill.dm" +#include "code\modules\sprite_editing\workspace.dm" #include "code\modules\station_goals\bsa.dm" #include "code\modules\station_goals\dna_vault.dm" #include "code\modules\station_goals\generate_goals.dm" @@ -6946,6 +6956,7 @@ #include "modular_darkpack\master_files\code\modules\mob\living\carbon\carbon.dm" #include "modular_darkpack\master_files\code\modules\mob\living\carbon\carbon_defines.dm" #include "modular_darkpack\master_files\code\modules\mob\living\carbon\examine.dm" +#include "modular_darkpack\master_files\code\modules\mob\living\carbon\human\_species.dm" #include "modular_darkpack\master_files\code\modules\mob\living\carbon\human\death.dm" #include "modular_darkpack\master_files\code\modules\mob\living\carbon\human\human.dm" #include "modular_darkpack\master_files\code\modules\mob\living\carbon\human\human_defines.dm" @@ -6977,6 +6988,7 @@ #include "modular_darkpack\master_files\code\turfs\open\grass.dm" #include "modular_darkpack\master_files\code\turfs\open\planet.dm" #include "modular_darkpack\modules\aggravated_damage\code\damage_procs.dm" +#include "modular_darkpack\modules\aggravated_damage\code\generic_healing.dm" #include "modular_darkpack\modules\ambience\code\music_subsystem.dm" #include "modular_darkpack\modules\antediluvian_sarcophagus\code\sarcophagus.dm" #include "modular_darkpack\modules\antediluvian_sarcophagus\code\the_antediluvian.dm" @@ -7002,6 +7014,7 @@ #include "modular_darkpack\modules\beastmaster\code\beastmaster_commands.dm" #include "modular_darkpack\modules\beastmaster\code\beastmaster_minion_management.dm" #include "modular_darkpack\modules\beastmaster\code\beastmaster_signals.dm" +#include "modular_darkpack\modules\billiards\code\billiard.dm" #include "modular_darkpack\modules\bitcoinminer\code\bitcoinminer.dm" #include "modular_darkpack\modules\blood_drinking\code\bite_helper_procs.dm" #include "modular_darkpack\modules\blood_drinking\code\bite_keybinding.dm" @@ -7108,6 +7121,15 @@ #include "modular_darkpack\modules\electricity\code\fusebox.dm" #include "modular_darkpack\modules\electricity\code\generator.dm" #include "modular_darkpack\modules\elevators\code\elevator.dm" +#include "modular_darkpack\modules\ert\code\loadout.dm" +#include "modular_darkpack\modules\ert\code\national_guard.dm" +#include "modular_darkpack\modules\ert\code\pentex_ert_roles.dm" +#include "modular_darkpack\modules\ert\code\swat.dm" +#include "modular_darkpack\modules\ert\code\team.dm" +#include "modular_darkpack\modules\ert\code\items\first_team.dm" +#include "modular_darkpack\modules\ert\code\items\swat_items.dm" +#include "modular_darkpack\modules\ert\code\outfits\national_guard_outfits.dm" +#include "modular_darkpack\modules\ert\code\outfits\swat_outfits.dm" #include "modular_darkpack\modules\fire\code\fire.dm" #include "modular_darkpack\modules\fire\code\molotov.dm" #include "modular_darkpack\modules\fire\code\object_interactions.dm" @@ -7172,8 +7194,6 @@ #include "modular_darkpack\modules\jobs\code\camarilla\primogens\ventrue.dm" #include "modular_darkpack\modules\jobs\code\clinic\director.dm" #include "modular_darkpack\modules\jobs\code\clinic\doctor.dm" -#include "modular_darkpack\modules\jobs\code\event\national_guard.dm" -#include "modular_darkpack\modules\jobs\code\event\swat.dm" #include "modular_darkpack\modules\jobs\code\garou\councillor.dm" #include "modular_darkpack\modules\jobs\code\garou\guardian.dm" #include "modular_darkpack\modules\jobs\code\garou\truthcatcher.dm" @@ -7266,6 +7286,9 @@ #include "modular_darkpack\modules\masquerade\code\subsystem\masquerade.dm" #include "modular_darkpack\modules\matrix\code\job.dm" #include "modular_darkpack\modules\matrix\code\matrix.dm" +#include "modular_darkpack\modules\merits_flaws\code\_darkpack_quirk.dm" +#include "modular_darkpack\modules\merits_flaws\code\illegal_identity.dm" +#include "modular_darkpack\modules\merits_flaws\code\permanent_fangs.dm" #include "modular_darkpack\modules\mob_spawners\code\citizen.dm" #include "modular_darkpack\modules\movie_theatre\code\areas.dm" #include "modular_darkpack\modules\movie_theatre\code\fluff.dm" @@ -7340,6 +7363,7 @@ #include "modular_darkpack\modules\occult_artifacts\code\artifacts\weekapaug_thistle.dm" #include "modular_darkpack\modules\onfloor_icons\code\apply_onfloor_icon_element.dm" #include "modular_darkpack\modules\onfloor_icons\code\dynamic_item_icon.dm" +#include "modular_darkpack\modules\onfloor_icons\code\gags_configs.dm" #include "modular_darkpack\modules\paths\code\conjured_items.dm" #include "modular_darkpack\modules\paths\code\occult_research.dm" #include "modular_darkpack\modules\paths\code\path.dm" @@ -7351,6 +7375,7 @@ #include "modular_darkpack\modules\phones\code\_phone.dm" #include "modular_darkpack\modules\phones\code\phone_book.dm" #include "modular_darkpack\modules\phones\code\phone_contact.dm" +#include "modular_darkpack\modules\phones\code\phone_effects.dm" #include "modular_darkpack\modules\phones\code\phone_history.dm" #include "modular_darkpack\modules\phones\code\phone_procs.dm" #include "modular_darkpack\modules\phones\code\phone_subsystem.dm" @@ -7415,6 +7440,7 @@ #include "modular_darkpack\modules\powers\code\discipline\thaumaturgy\status_effects\blood_rage_status.dm" #include "modular_darkpack\modules\powers\code\discipline\vicissitude\fleshwalls.dm" #include "modular_darkpack\modules\powers\code\discipline\vicissitude\human_flesh.dm" +#include "modular_darkpack\modules\preferences\height_preference.dm" #include "modular_darkpack\modules\quirks\code\negative_quirks\derangement.dm" #include "modular_darkpack\modules\radios\code\admin_verb.dm" #include "modular_darkpack\modules\radios\code\radio.dm" @@ -7495,17 +7521,23 @@ #include "modular_darkpack\modules\sabbat\code\vaulderie_goblet.dm" #include "modular_darkpack\modules\splats\code\__splat.dm" #include "modular_darkpack\modules\splats\code\gaining_splats.dm" +#include "modular_darkpack\modules\splats\code\hud_managment.dm" #include "modular_darkpack\modules\splats\code\is_splat_helpers.dm" #include "modular_darkpack\modules\splats\code\losing_splats.dm" #include "modular_darkpack\modules\splats\code\splat_configs.dm" +#include "modular_darkpack\modules\splats\code\splat_life.dm" #include "modular_darkpack\modules\splats\code\splat_management.dm" -#include "modular_darkpack\modules\splats\code\st_power_management.dm" +#include "modular_darkpack\modules\splats\code\powers\_power.dm" +#include "modular_darkpack\modules\splats\code\powers\st_power_management.dm" #include "modular_darkpack\modules\splats\code\prefrences\pref_info.dm" #include "modular_darkpack\modules\splats\code\prefrences\splat_middleware.dm" #include "modular_darkpack\modules\splats\code\prefrences\splat_pref.dm" +#include "modular_darkpack\modules\splats\code\subsplat\_subsplat.dm" #include "modular_darkpack\modules\stock_market\code\stockexchange.dm" #include "modular_darkpack\modules\storage\code\firstaidkit.dm" +#include "modular_darkpack\modules\storyteller_dice\code\roll_datum.dm" #include "modular_darkpack\modules\storyteller_dice\code\roll_subsystem.dm" +#include "modular_darkpack\modules\storyteller_dice\code\roll_subtypes.dm" #include "modular_darkpack\modules\storyteller_dice\code\rolling_pref.dm" #include "modular_darkpack\modules\storyteller_dice\code\verbs.dm" #include "modular_darkpack\modules\storyteller_stats\code\_st_stats.dm" @@ -7634,9 +7666,44 @@ #include "modular_darkpack\modules\weapons\code\sheath.dm" #include "modular_darkpack\modules\weapons\code\stake.dm" #include "modular_darkpack\modules\weather\code\effects.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\emotes.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\garou_languages.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\glyphs.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\hud.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\silver_damage.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\totems.dm" #include "modular_darkpack\modules\werewolf_the_apocalypse\code\werewolf_globals.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\_gift.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\ahroun.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\galliard.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\philodox.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\ragabash.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\auspices\theurge.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\gifts\innate\howling.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\preferences\auspice.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\preferences\breed.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\preferences\fur.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\preferences\renown.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\preferences\tribe.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\species\garou_organs.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\examine_text.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\fera_species.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\fera_splat.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\is_werewolf_helpers.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\pref_lore.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\renown.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\transformation.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\varediting.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\splats\werewolf_gift_management.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\status_effects\delirium.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\status_effects\rage_heal.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\_subsplat.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\auspices\_auspice.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\auspices\garou.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\breeds\_breed.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\breeds\garou.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\tribes\_tribe.dm" +#include "modular_darkpack\modules\werewolf_the_apocalypse\code\subsplats\tribes\garou.dm" #include "modular_darkpack\modules\westfield_mall\code\mall_areas.dm" #include "modular_darkpack\modules\westfield_mall\code\modular_rooms.dm" #include "modular_darkpack\modules\z_travel\code\manhole.dm" diff --git a/tgui/bun.lock b/tgui/bun.lock index c182189e0411..6dd4971b730c 100644 --- a/tgui/bun.lock +++ b/tgui/bun.lock @@ -8,6 +8,7 @@ "@happy-dom/global-registrator": "^20.0.11", "@rspack/cli": "^1.6.8", "@rspack/core": "^1.6.8", + "@testing-library/react": "^16.3.2", "@types/bun": "^1.3.5", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", @@ -39,6 +40,7 @@ "name": "tgui", "version": "6.0.0", "dependencies": { + "color-blend": "4.0.0", "common": "workspace:*", "dateformat": "^5.0.3", "dompurify": "^3.2.5", @@ -102,6 +104,12 @@ }, }, "packages": { + "@babel/code-frame": ["@babel/code-frame@7.28.6", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.10.2", "", {}, "sha512-uFsRXwIGyu+r6AMdz+XijIIZJYpoWeYzILt5yZ2d3mCjQrWUTVpVD9WL/jZAbvp+Ed04rOhrsk7FiTcEDseB5A=="], "@discoveryjs/json-ext": ["@discoveryjs/json-ext@0.5.7", "", {}, "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="], @@ -236,10 +244,16 @@ "@rspack/lite-tapable": ["@rspack/lite-tapable@1.1.0", "", {}, "sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw=="], + "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], + + "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], + "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="], "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], + "@types/body-parser": ["@types/body-parser@1.19.6", "", { "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g=="], "@types/bonjour": ["@types/bonjour@3.5.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ=="], @@ -326,7 +340,7 @@ "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], @@ -334,6 +348,8 @@ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], "asap": ["asap@2.0.6", "", {}, "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA=="], @@ -398,6 +414,8 @@ "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], + "color-blend": ["color-blend@4.0.0", "", {}, "sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw=="], + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -470,6 +488,8 @@ "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], @@ -478,6 +498,8 @@ "dns-packet": ["dns-packet@5.6.1", "", { "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" } }, "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw=="], + "dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], @@ -690,6 +712,8 @@ "jotai": ["jotai@2.16.0", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-NmkwPBet0SHQ28GBfEb10sqnbVOYyn6DL4iazZgGRDUKxSWL0iqcm+IK4TqTSFC2ixGk+XX2e46Wbv364a3cKg=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -710,6 +734,8 @@ "lru-cache": ["lru-cache@11.2.4", "", {}, "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg=="], + "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], + "make-fetch-happen": ["make-fetch-happen@13.0.1", "", { "dependencies": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", "is-lambda": "^1.0.1", "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "proc-log": "^4.2.0", "promise-retry": "^2.0.1", "ssri": "^10.0.0" } }, "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA=="], "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], @@ -854,6 +880,8 @@ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], + "proc-log": ["proc-log@4.2.0", "", {}, "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA=="], "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], @@ -876,6 +904,8 @@ "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="], + "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], + "react-json-tree": ["react-json-tree@0.20.0", "", { "dependencies": { "@types/lodash": "^4.17.15", "react-base16-styling": "^0.10.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -1278,6 +1308,10 @@ "webpack-dev-middleware/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], diff --git a/tgui/bunfig.toml b/tgui/bunfig.toml index d20eff30d4cb..363990451190 100644 --- a/tgui/bunfig.toml +++ b/tgui/bunfig.toml @@ -1,2 +1,2 @@ [test] -preload = "./happydom.ts" +preload = ["./happydom.ts"] diff --git a/tgui/happydom.ts b/tgui/happydom.ts index 1512a7e634f6..18a7051a49ee 100644 --- a/tgui/happydom.ts +++ b/tgui/happydom.ts @@ -1,3 +1,5 @@ import { GlobalRegistrator } from '@happy-dom/global-registrator'; GlobalRegistrator.register(); + +import 'packages/tgui/__mocks__/setup.ts'; diff --git a/tgui/package.json b/tgui/package.json index 75ba56974c97..208b9001305c 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -5,6 +5,7 @@ "@happy-dom/global-registrator": "^20.0.11", "@rspack/cli": "^1.6.8", "@rspack/core": "^1.6.8", + "@testing-library/react": "^16.3.2", "@types/bun": "^1.3.5", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", diff --git a/tgui/packages/tgui/__mocks__/byond.ts b/tgui/packages/tgui/__mocks__/byond.ts new file mode 100644 index 000000000000..c0084511652f --- /dev/null +++ b/tgui/packages/tgui/__mocks__/byond.ts @@ -0,0 +1,30 @@ +const ByondMock = { + windowId: 'test-window', + IS_BYOND: true, + BLINK: true, + strictMode: true, + storageCdn: '', + + call: () => ({}), + callAsync: async () => ({}), + topic: () => {}, + command: () => {}, + + winget: async () => ({}), + winset: () => {}, + + parseJson: (text: string) => JSON.parse(text), + + sendMessage: () => {}, + subscribe: () => {}, + subscribeTo: () => {}, + + loadCss: () => {}, + loadJs: () => {}, + + iconRefMap: {}, + saveBlob: () => {}, +}; + +// @ts-expect-error +globalThis.Byond = ByondMock; diff --git a/tgui/packages/tgui/__mocks__/layouts.tsx b/tgui/packages/tgui/__mocks__/layouts.tsx new file mode 100644 index 000000000000..30f186f75668 --- /dev/null +++ b/tgui/packages/tgui/__mocks__/layouts.tsx @@ -0,0 +1,32 @@ +import { mock } from 'bun:test'; + +import type { PropsWithChildren } from 'react'; + +type WindowProps = Partial<{ + title: string; +}>; + +function Window(props: PropsWithChildren) { + const { title = 'Test UI', children } = props; + + return ( +
+
{title}
+ {children} +
+ ); +} + +function Content(props: PropsWithChildren) { + const { children } = props; + + return
{children}
; +} + +Window.Content = Content; + +mock.module('../layouts', () => ({ + Window, + Layout: Window, + Pane: Window, +})); diff --git a/tgui/packages/tgui/__mocks__/setup.ts b/tgui/packages/tgui/__mocks__/setup.ts new file mode 100644 index 000000000000..56c803d832e1 --- /dev/null +++ b/tgui/packages/tgui/__mocks__/setup.ts @@ -0,0 +1,21 @@ +import { mock } from 'bun:test'; + +import './byond'; +import './layouts'; + +const logger = { + debug: () => {}, + error: () => {}, + info: () => {}, + log: () => {}, + warn: () => {}, +}; + +mock.module('../logging', () => ({ + createLogger: () => logger, + logger, +})); + +mock.module('../events/act', () => ({ + sendAct: () => ({}), +})); diff --git a/tgui/packages/tgui/assets/transparency_checkerboard.svg b/tgui/packages/tgui/assets/transparency_checkerboard.svg new file mode 100644 index 000000000000..ecfd3a467d51 --- /dev/null +++ b/tgui/packages/tgui/assets/transparency_checkerboard.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tgui/packages/tgui/interfaces/AnomalousWeatherTower.tsx b/tgui/packages/tgui/interfaces/AnomalousWeatherTower.tsx new file mode 100644 index 000000000000..d2b02b147b6f --- /dev/null +++ b/tgui/packages/tgui/interfaces/AnomalousWeatherTower.tsx @@ -0,0 +1,165 @@ +import { useBackend, useSharedState } from 'tgui/backend'; +import { + Button, + Dropdown, + NoticeBox, + Section, + Stack, +} from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { Window } from '../layouts'; + +type Weather = { + id: string; + name: string; + desc: string; +}; + +type Data = { + core_charges: number; + max_core_charge: number; + weather_charge_cost: number; + can_summon_weather: BooleanLike; + can_clear_weather: BooleanLike; + summonable_weather_types: Weather[]; + active_weather_on_z: Weather[]; +}; + +function formatCharges(charges: number, max_charges: number) { + return `${(charges / max_charges) * 100}%`; +} + +export const AnomalousWeatherTower = () => { + const { act, data } = useBackend(); + const { + core_charges, + max_core_charge, + weather_charge_cost, + can_summon_weather, + can_clear_weather, + summonable_weather_types, + active_weather_on_z, + } = data; + + const [selectedWeather, setSelectedWeather] = useSharedState( + 'selectedWeather', + summonable_weather_types[0], + ); + + const winHeight = 420 + active_weather_on_z.length * 30; + + let chargeColor = 'green'; + if (core_charges / max_core_charge <= 0.25) { + chargeColor = 'red'; + } else if (core_charges / max_core_charge <= 0.5) { + chargeColor = 'yellow'; + } + + return ( + + + + +
+ + Core charge + + {formatCharges(core_charges, max_core_charge)} + + +
+
+ +
+ {active_weather_on_z.length > 0 ? ( + active_weather_on_z.map((weather) => ( + + + {weather.name} + +
+
+ +
+ + + weather.name, + )} + selected={selectedWeather.name} + onSelected={(weatherName) => { + const weather = summonable_weather_types.find( + (w) => w.name === weatherName, + ); + if (weather) setSelectedWeather(weather); + }} + /> + + +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Canvas.tsx b/tgui/packages/tgui/interfaces/Canvas.tsx index dadd0cc202a1..e1c8777f46fd 100644 --- a/tgui/packages/tgui/interfaces/Canvas.tsx +++ b/tgui/packages/tgui/interfaces/Canvas.tsx @@ -1,471 +1,346 @@ -import { Component, createRef, type RefObject, useState } from 'react'; -import { Color } from 'tgui-core/color'; -import { - Box, - Button, - Flex, - Icon, - KeyListener, - Tooltip, -} from 'tgui-core/components'; -import { KEY_F, KEY_G } from 'tgui-core/keycodes'; +import { type PropsWithChildren, type ReactNode, useState } from 'react'; +import { Box, Button, Stack } from 'tgui-core/components'; +import { clamp } from 'tgui-core/math'; +import type { BooleanLike } from 'tgui-core/react'; import { decodeHtmlEntities } from 'tgui-core/string'; import { useBackend } from '../backend'; import { Window } from '../layouts'; +import { SpriteEditor } from './common/SpriteEditor'; +import { + AdvancedCanvas, + type AdvancedCanvasPropsBase, +} from './common/SpriteEditor/Components/AdvancedCanvas'; +import { hasServerColorData } from './common/SpriteEditor/helpers'; +import { Dir, type SpriteEditorData } from './common/SpriteEditor/Types/types'; -const LEFT_CLICK = 0; - -type PaintCanvasProps = Partial<{ - onCanvasModifiedHandler: (data: PointData[]) => void; - onCanvasDropperHandler: (x: number, y: number) => void; - onCanvasFillHandler: (x: number, y: number) => void; - value: string[][]; - width: number; - height: number; - imageWidth: number; - imageHeight: number; - editable: boolean; - drawing_color: string | null; - has_palette: boolean; - show_grid: boolean; - zoom: number; - fillmode: boolean; -}>; - -type PointData = { - x: number; - y: number; +type CanvasMetadata = { + title: string; + author: string; + patron?: string; + medium: string; + date?: string; }; -const fromDM = (data: string[][]) => { - return data.map((inner) => inner.map((v) => Color.fromHex(v))); +type CanvasData = { + metadata: CanvasMetadata; + editorData: SpriteEditorData; + pixelsPerUnit: number; + finalized: BooleanLike; + editable: BooleanLike; + allowColorPicker: BooleanLike; + showPlaque: BooleanLike; + year_offset: number; }; -const toMassPaintFormat = (data: PointData[]) => { - return data.map((p) => ({ x: p.x + 1, y: p.y + 1 })); // 1-based index dm side +type ZoomProps = { + zoom: number; + setZoom: React.Dispatch>; + pixelsPerUnit: number; }; -const checkPointCoords = (x: number, y: number, p: PointData) => { - return p.x === x && p.y === y; +type CanvasCommonProps = ZoomProps & { + width: number; + height: number; }; -class PaintCanvas extends Component { - canvasRef: RefObject; - baseImageData: Color[][]; - is_grid_shown: boolean; - modifiedElements: PointData[]; - onCanvasModified: (data: PointData[]) => void; - onCanvasDropper: (x: number, y: number) => void; - onCanvasFill: (x: number, y: number) => void; - drawing: boolean; - drawing_color: string; - zoom: number; - - constructor(props) { - super(props); - this.canvasRef = createRef(); - this.modifiedElements = []; - this.is_grid_shown = false; - this.drawing = false; - this.zoom = props.zoom; +const ZoomButtons = ({ zoom, setZoom, pixelsPerUnit }: ZoomProps) => ( + + + + } + > + + + + act('change_volume', { + volume: value, + }) + } + /> + + + + + + ); +} diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx index 9b14a93a1af4..6846b21363cb 100644 --- a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx +++ b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleSequencer.jsx @@ -76,6 +76,17 @@ const GeneCycler = (props) => { ); }; +function isPairMatched(sequence, index) { + const gene1 = sequence.charAt(index); + const gene2 = sequence.charAt(index + 1); + return ( + (gene1 === 'A' && gene2 === 'T') || + (gene1 === 'T' && gene2 === 'A') || + (gene1 === 'G' && gene2 === 'C') || + (gene1 === 'C' && gene2 === 'G') + ); +} + const GenomeSequencer = (props) => { const { mutation } = props; if (!mutation) { @@ -120,9 +131,9 @@ const GenomeSequencer = (props) => { {buttons[i + 1]} diff --git a/tgui/packages/tgui/interfaces/ExaminePanel.tsx b/tgui/packages/tgui/interfaces/ExaminePanel.tsx index bce30335d072..f3bee77295f4 100644 --- a/tgui/packages/tgui/interfaces/ExaminePanel.tsx +++ b/tgui/packages/tgui/interfaces/ExaminePanel.tsx @@ -3,7 +3,7 @@ import { useState } from 'react'; import { resolveAsset } from '../assets'; import { useBackend } from '../backend'; -import { Section, Stack, Tabs } from 'tgui-core/components'; +import { Section, Stack, Tabs, Box } from 'tgui-core/components'; import type { BooleanLike } from 'tgui-core/react'; import { Window } from '../layouts'; @@ -63,122 +63,79 @@ export const ExaminePanel = (props) => { } = data; return ( - -
+ +
- - - setTabIndex(1)} - > -
- - {nsfw_content ? - setTabIndex(2)} - > -
- - : null} - - {tabIndex === 1 && ( -
- {formatURLs(flavor_text)} -
- )} - {tabIndex === 2 && ( + + + + setTabIndex(1)} + > + Flavor Text + + {nsfw_content ? + setTabIndex(2)} + > + Flavor Text (NSFW) + + : null} +
- {formatURLs(flavor_text_nsfw)} + {formatURLs(tabIndex === 1 ? flavor_text : flavor_text_nsfw)}
- )} - - setLowerTabIndex(1)} - > -
- - {nsfw_content ? - setLowerTabIndex(2)} - > -
- - : null} - - {lowerTabIndex === 1 && ( -
+ setLowerTabIndex(1)} > - {formatURLs(character_notes)} -
- )} - {lowerTabIndex === 2 && ( -
+ {nsfw_content ? + setLowerTabIndex(2)} > - {formatURLs(ooc_notes)} -
- )} + OOC Notes (NSFW) + + : null} + + +
{formatURLs(lowerTabIndex === 1 ? character_notes : ooc_notes)}
+
+ diff --git a/tgui/packages/tgui/interfaces/LootPanel/index.tsx b/tgui/packages/tgui/interfaces/LootPanel/index.tsx index cd7bb56853c5..f42955587252 100644 --- a/tgui/packages/tgui/interfaces/LootPanel/index.tsx +++ b/tgui/packages/tgui/interfaces/LootPanel/index.tsx @@ -1,9 +1,7 @@ -import { useState } from 'react'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { Button, Input, Section, Stack } from 'tgui-core/components'; import { isEscape } from 'tgui-core/keys'; import { clamp } from 'tgui-core/math'; -import type { BooleanLike } from 'tgui-core/react'; import { useBackend } from '../../backend'; import { Window } from '../../layouts'; @@ -13,12 +11,11 @@ import type { SearchItem } from './types'; type Data = { contents: SearchItem[]; - searching: BooleanLike; }; export function LootPanel(props) { - const { act, data } = useBackend(); - const { contents = [], searching } = data; + const { data } = useBackend(); + const { contents = [] } = data; // limitations: items with different stack counts, charges etc. const contentsByPathName = useMemo(() => { @@ -71,11 +68,6 @@ export function LootPanel(props) { onClick={() => setGrouping(!grouping)} tooltip="Toggle Grouping" /> - + ))} + +
+ + + {!!searchBarVisible && ( + + + + + + handleSelect(filteredItems[selected])} + onChange={handleSearch} + placeholder="Search..." + value={query} + style={{ + borderColor: invalidInput ? 'red' : undefined, + }} + /> + + + )} + + + + + ); +} - if (keyCode === KEY_ENTER) { - event.preventDefault(); - onSelected(filteredItems[selected]); - } +type AtomSpanProps = { + atomData: AtomPathData; + item: AtomTypeData; +}; - if (keyCode === KEY_ESCAPE) { - event.preventDefault(); - act('cancel'); - } +function ListItem(props: AtomSpanProps) { + const { atomData, item } = props; - if (keyCode === KEY_R && event.altKey) { - if (query.indexOf('re:') === 0) setQuery(query.slice(3)); - else setQuery(`re:${query}`); - } + const { data } = useBackend(); + const { fancyTypes } = data; + + const matchingKey = fancyTypes + ? Object.keys(atomData.fancyTypes).findLast( + (x: string) => item.typepath.indexOf(x) === 0, + ) + : undefined; - if (keyCode === KEY_N && event.altKey) - act('setNameSearch', { searchNames: !searchNames }); + const displayPath = matchingKey + ? item.typepath.replace(matchingKey, atomData.fancyTypes[matchingKey]) + : item.typepath; - if (keyCode === KEY_F && event.altKey) - act('setFancyTypes', { fancyTypes: !fancyTypes }); + return ( + <> + + {displayPath} + + + {item.name} + + {!!atomData.abstractTypes[item.typepath] && ( + - - -
- - - {filteredItems.map((item, index) => ( - - ))} - -
-
- {!!searchBarVisible && ( - onSelected(filteredItems[selected])} - onChange={onSearch} - placeholder="Search..." - value={query} - style={invalidInput ? { borderColor: 'red' } : {}} - /> - )} -
-
- - + Abstract + + )} + ); -}; +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/AdvancedCanvas.tsx b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/AdvancedCanvas.tsx new file mode 100644 index 000000000000..92eab6cfacae --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/AdvancedCanvas.tsx @@ -0,0 +1,191 @@ +import { useLayoutEffect, useRef, useState } from 'react'; +import transparency_checkerboard from 'tgui/assets/transparency_checkerboard.svg'; +import { + type BooleanStyleMap, + computeBoxProps, + type StringStyleMap, +} from 'tgui-core/ui'; +import { colorToCssString } from '../colorSpaces'; +import { useClickAndDragEventHandler, useDimensions } from '../helpers'; +import type { + BorderStyleProps, + EditorColor, + IncludeOrOmitEntireType, + InlineStyle, + Layer, + StringLayer, +} from '../Types/types'; + +type AdvancedCanvasMouseEventHandler = ( + event: MouseEvent, + ref: React.RefObject, +) => void; + +type AdvancedCanvasClickHandler = { + onClick: AdvancedCanvasMouseEventHandler; +}; +type AdvancedCanvasClickAndDragHandlers = Partial<{ + onMouseDown: AdvancedCanvasMouseEventHandler; + onMouseMove: AdvancedCanvasMouseEventHandler; + onMouseUp: AdvancedCanvasMouseEventHandler; +}>; + +type AdvancedCanvasEventHandlers = + | AdvancedCanvasClickHandler + | AdvancedCanvasClickAndDragHandlers; + +export type AdvancedCanvasPropsBase = { + data: Layer | StringLayer; + showGrid?: boolean; + border?: BorderStyleProps; + background?: string | string[]; + backdropColor?: string; +} & Partial; + +type AdvancedCanvasProps = IncludeOrOmitEntireType< + AdvancedCanvasEventHandlers, + AdvancedCanvasPropsBase +>; + +const propsHaveClickHandler = ( + props: AdvancedCanvasProps, +): props is AdvancedCanvasPropsBase & AdvancedCanvasClickHandler => + Object.keys(props).includes('onClick'); +const propsHaveClickAndDragHandlers = ( + props: AdvancedCanvasProps, +): props is AdvancedCanvasPropsBase & AdvancedCanvasClickAndDragHandlers => { + const keys = Object.keys(props); + return keys.includes('onMouseMove') || keys.includes('onMouseUp'); +}; + +const extractBaseProps = (props: AdvancedCanvasProps) => { + switch (true) { + case propsHaveClickHandler(props): { + const { onClick, ...rest } = props; + return { ...rest }; + } + case propsHaveClickAndDragHandlers(props): { + const { onMouseDown, onMouseMove, onMouseUp, ...rest } = props; + return { ...rest }; + } + default: + return props; + } +}; + +export const AdvancedCanvas = (props: AdvancedCanvasProps) => { + const { + data, + showGrid, + border: borderProps, + background, + backdropColor, + ...rest + } = extractBaseProps(props); + const { onClick } = propsHaveClickHandler(props) ? props : {}; + const imageHeight = data.length; + const imageWidth = data.at(0)?.length ?? 0; + const parentRef = useRef(null); + const canvasRef = useRef(null); + const [parentWidth, parentHeight] = useDimensions(parentRef); + const [[canvasWidth, canvasHeight], setCanvasDimensions] = useState< + [number, number] + >([0, 0]); + const mouseDownHandler = propsHaveClickAndDragHandlers(props) + ? useClickAndDragEventHandler( + canvasRef, + props.onMouseDown, + props.onMouseMove, + props.onMouseUp, + ) + : undefined; + useLayoutEffect(() => { + const parent = parentRef.current; + if (!parent) return; + const { width: parentWidth, height: parentHeight } = + parent.getBoundingClientRect(); + const scalingFactor = Math.floor( + Math.min(parentWidth / imageWidth, parentHeight / imageHeight), + ); + setCanvasDimensions([ + imageWidth * scalingFactor, + imageHeight * scalingFactor, + ]); + }, [imageWidth, imageHeight, parentWidth, parentHeight, parentRef]); + useLayoutEffect(() => { + const canvas = canvasRef.current; + if (!canvas) { + return; + } + const scalingFactor = canvasWidth / imageWidth; + const context = canvas.getContext('2d')!; + if (backdropColor) { + context.fillStyle = backdropColor; + context.fillRect(0, 0, canvasWidth, canvasHeight); + } + data.forEach((row: string[] | EditorColor[], y) => { + row.forEach((pixel: string | EditorColor, x) => { + context.fillStyle = + typeof pixel === 'string' ? pixel : colorToCssString(pixel); + context.fillRect( + x * scalingFactor, + y * scalingFactor, + scalingFactor, + scalingFactor, + ); + }); + }); + if (showGrid && scalingFactor >= 5) { + context.beginPath(); + context.strokeStyle = 'black'; + context.lineWidth = 2; + for (let y = 0; y <= canvasHeight; y += scalingFactor) { + context.moveTo(0, y); + context.lineTo(canvasWidth, y); + } + for (let x = 0; x <= canvasWidth; x += scalingFactor) { + context.moveTo(x, 0); + context.lineTo(x, canvasHeight); + } + context.stroke(); + } + }, [ + JSON.stringify(data), + canvasWidth, + canvasHeight, + canvasRef, + showGrid, + backdropColor, + ]); + return ( +
+ onClick(ev.nativeEvent, canvasRef))} + onMouseDown={mouseDownHandler} + style={{ + backgroundImage: [ + ...(Array.isArray(background) + ? background + : background + ? [background] + : []), + `url(${transparency_checkerboard})`, + ].join(','), + outline: '2px solid black', + ...borderProps, + }} + /> +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/ColorPicker.tsx b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/ColorPicker.tsx new file mode 100644 index 000000000000..baaed8cd7f5b --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/ColorPicker.tsx @@ -0,0 +1,575 @@ +import { type ReactNode, useCallback, useRef, useState } from 'react'; +import transparency_checkerboard from 'tgui/assets/transparency_checkerboard.svg'; +import { + Box, + Input, + LabeledList, + NumberInput, + Section, + Stack, +} from 'tgui-core/components'; +import { clamp01 } from 'tgui-core/math'; +import type { BooleanLike } from 'tgui-core/react'; +import { + type BooleanStyleMap, + computeBoxProps, + type StringStyleMap, +} from 'tgui-core/ui'; +import { + asBothSpaces, + hsva2hslString, + parseHexColorString, + rgb2hexstring, +} from '../colorSpaces'; +import { + invLerp, + lerp, + useClickAndDragEventHandler, + useDimensions, +} from '../helpers'; +import { + type EditorColor, + type InlineStyle, + SpriteEditorColorMode, +} from '../Types/types'; + +type TouchpadEventHandler = ( + event: MouseEvent, + ref: React.RefObject, +) => void; + +type TouchpadProps = { + onMouseDown: TouchpadEventHandler; + onMouseMove: TouchpadEventHandler; + onMouseUp: TouchpadEventHandler; + elementRef: React.RefObject; + children: ReactNode; +}; + +const Touchpad = (props: TouchpadProps) => { + const { onMouseDown, onMouseMove, onMouseUp, elementRef, children } = props; + const fallbackRef = useRef(null); + const usedRef = elementRef ?? fallbackRef; + const mouseDownHandler = useClickAndDragEventHandler( + usedRef, + onMouseDown, + onMouseMove, + onMouseUp, + ); + return ( +
+ {children} +
+ ); +}; + +type SliderMarkerProps = Partial<{ + length: string | BooleanLike; + crossLength: string | BooleanLike; + vertical: BooleanLike; +}>; + +type SliderCallback = (value: number) => void; + +type SliderProps = SliderMarkerProps & { + backgroundImage: string; + markerPosition?: number; + markerColor: string; + markerOutlineColor?: string; + onDrag: SliderCallback; + onRelease: SliderCallback; +}; + +const computeSliderValue = ( + event: MouseEvent, + ref: React.RefObject, + vertical: BooleanLike, +) => { + const current = ref.current; + if (!current) { + return 0; + } + const { clientX, clientY } = event; + const { top, left, width, height } = current.getBoundingClientRect(); + return clamp01( + vertical ? (clientY - top) / height : (clientX - left) / width, + ); +}; + +const Slider = (props: SliderProps) => { + const { + length, + crossLength, + vertical, + backgroundImage, + markerPosition = 0, + markerColor, + markerOutlineColor, + onDrag, + onRelease, + } = props; + const touchpadRef = useRef(null); + const dragHandler = useCallback( + (event, ref) => { + const value = computeSliderValue(event, ref, vertical); + onDrag(value); + }, + [onDrag], + ); + const releaseHandler = useCallback( + (event, ref) => { + const value = computeSliderValue(event, ref, vertical); + onRelease(value); + }, + [onRelease], + ); + const dimensions = vertical + ? { + height: length, + width: crossLength, + } + : { + width: length, + height: crossLength, + }; + const valueOffset = `calc(${markerPosition * 100}% - 0.25em)`; + return ( + + +
+
+ + + ); +}; + +type SatValPadCallback = (x: number, y: number) => void; + +type SatValPadProps = { + hue: number; + saturation: number; + value: number; + width?: string | BooleanLike; + onDrag: SatValPadCallback; + onRelease: SatValPadCallback; +}; + +const computeSatValTouchpadValue = ( + ev: MouseEvent, + ref: React.RefObject, +): [number, number] => { + const current = ref.current; + if (!current) { + return [0, 0]; + } + const { clientX, clientY } = ev; + const { left, top, width, height } = current.getBoundingClientRect(); + return [clamp01((clientX - left) / width), clamp01((clientY - top) / height)]; +}; + +const SatValPad = (props: SatValPadProps) => { + const { hue, saturation, value, width, onDrag, onRelease } = props; + const touchpadRef = useRef(null); + const outerRef = useRef(null); + const [dragging, setDragging] = useState(false); + const [touchpadWidth] = useDimensions(outerRef); + const dragHandler = useCallback( + (ev) => { + const [x, y] = computeSatValTouchpadValue(ev, touchpadRef); + onDrag(x, y); + }, + [touchpadRef, onDrag], + ); + const releaseHandler = useCallback( + (ev) => { + const [x, y] = computeSatValTouchpadValue(ev, touchpadRef); + onRelease(x, y); + }, + [touchpadRef, onRelease], + ); + return ( +
+ { + setDragging(true); + dragHandler(ev); + }} + onMouseMove={dragHandler} + onMouseUp={(ev) => { + setDragging(false); + releaseHandler(ev); + }} + elementRef={touchpadRef} + > +
+
= 0.5 ? 'black' : 'white'}`, + borderRadius: '50%', + }, + })} + /> + +
+ ); +}; + +type PickerComponentRowProps = { + markerColor: string; + whiteMarkerBorder?: boolean; + backgroundImage: string; + value: number; + max: number; + unit?: string; + numberInputMultiplier?: number; + numberInputFormat?: (value: number) => string; + onDrag: (number) => void; + onRelease: (number) => void; +}; + +const PickerComponentRow = (props: PickerComponentRowProps) => { + const { + markerColor, + whiteMarkerBorder = false, + backgroundImage, + value, + max, + unit, + numberInputMultiplier = 1, + numberInputFormat, + onDrag, + onRelease, + } = props; + + return ( + + + onDrag(lerp(0, max, value)), [onDrag])} + onRelease={useCallback( + (value) => onRelease(lerp(0, max, value)), + [onRelease], + )} + /> + + + { + onRelease(value / numberInputMultiplier); + }} + value={value * numberInputMultiplier} + format={numberInputFormat} + unit={unit} + /> + + + ); +}; + +type ColorPickerCallback = (color: EditorColor) => void; + +export type ColorPickerProps = { + initialColor: EditorColor; + onSelectColor: ColorPickerCallback; + hslWidth: string | BooleanLike; + colorMode?: SpriteEditorColorMode; +} & Partial; + +export const ColorPicker = (props: ColorPickerProps) => { + const { + initialColor, + onSelectColor, + hslWidth, + colorMode = SpriteEditorColorMode.Rgba, + ...rest + } = props; + const [color, setColor] = useState(null); + const { + r, + g, + b, + h = 0, + s = 0, + v, + a = 1, + } = asBothSpaces(color ?? initialColor); + const alpha = colorMode === SpriteEditorColorMode.Rgba; + switch (colorMode) { + case SpriteEditorColorMode.Rgba: + case SpriteEditorColorMode.Rgb: + return ( +
+ + + + + + setColor({ h, s: x, v: 1 - y, a })} + onRelease={(x, y) => { + setColor(null); + onSelectColor({ h, s: x, v: 1 - y, a }); + }} + /> + + + + setColor({ h: Math.round(value * 360), s, v, a }) + } + onRelease={(value) => { + setColor(null); + onSelectColor({ h: Math.round(value * 360), s, v, a }); + }} + /> + + + + + + + + { + if (!value.startsWith('#') || value.length < 7) return; + onSelectColor(parseHexColorString(value)); + }} + /> + + + + `${Math.round(value)}`} + onDrag={(value) => setColor({ h: value, s, v, a })} + onRelease={(value) => { + setColor(null); + onSelectColor({ h: value, s, v, a }); + }} + /> + + + + `${Math.round(value * 10) / 10}` + } + onDrag={(value) => setColor({ h, s: value, v, a })} + onRelease={(value) => { + setColor(null); + onSelectColor({ h, s: value, v, a }); + }} + /> + + + + `${Math.round(value * 10) / 10}` + } + onDrag={(value) => setColor({ h, s, v: value, a })} + onRelease={(value) => { + setColor(null); + onSelectColor({ h, s, v: value, a }); + }} + /> + + + + + setColor({ r: Math.round(value), g, b, a }) + } + onRelease={(value) => { + setColor(null); + onSelectColor({ r: Math.round(value), g, b, a }); + }} + /> + + + + setColor({ r, g: Math.round(value), b, a }) + } + onRelease={(value) => { + setColor(null); + onSelectColor({ r, g: Math.round(value), b, a }); + }} + /> + + + + setColor({ r, g, b: Math.round(value), a }) + } + onRelease={(value) => { + setColor(null); + onSelectColor({ r, g, b: Math.round(value), a }); + }} + /> + + {alpha && ( + <> + + + + setColor({ + ...(color ?? initialColor), + a: value, + }) + } + onRelease={(value) => { + setColor(null); + onSelectColor({ + ...(color ?? initialColor), + a: value, + }); + }} + /> + + + )} + + + +
+ ); + case SpriteEditorColorMode.Greyscale: + return ( +
+ + + setColor({ h, s, v: value, a })} + onRelease={(value) => { + setColor(null); + onSelectColor({ h, s, v: value, a }); + }} + /> + + +
+ ); + } +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/LayerManager.tsx b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/LayerManager.tsx new file mode 100644 index 000000000000..8f8f1cb26606 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/LayerManager.tsx @@ -0,0 +1,224 @@ +import type { Dispatch, SetStateAction } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, Icon, Input, Section, Stack } from 'tgui-core/components'; +import type { BooleanStyleMap, StringStyleMap } from 'tgui-core/ui'; +import { Dir, type InlineStyle, type SpriteData } from '../Types/types'; +import { AdvancedCanvas } from './AdvancedCanvas'; + +export type LayerManagerProps = { + data: SpriteData; + selectedDir: Dir; + setSelectedDir: Dispatch>; + selectedLayer: number; + setSelectedLayer: Dispatch>; +} & Partial; + +const dirs = [Dir.SOUTH, Dir.NORTH, Dir.EAST, Dir.WEST]; +const dirCellPrefixes = ['south', 'north', 'east', 'west']; +const dirIcons = ['arrow-down', 'arrow-up', 'arrow-right', 'arrow-left']; + +export const LayerManager = (props: LayerManagerProps) => { + const { act } = useBackend(); + const { + data, + selectedDir, + setSelectedDir, + selectedLayer, + setSelectedLayer, + ...rest + } = props; + const { width, height, dirs: iconDirs, layers } = data; + const layerCount = layers.length; + const cells = [ + `". ${dirCellPrefixes.slice(0, iconDirs).join(' ')} add"`, + ...Array.from( + { length: layerCount }, + (_, i) => + `"leftControls${i} ${dirCellPrefixes + .slice(0, iconDirs) + .map((dir) => `${dir}${i}`) + .join(' ')} rightControls${i}"`, + ).toReversed(), + ].join(' '); + return ( + +
+ + {iconDirs > 1 && + Array.from({ length: iconDirs }, (_, i) => ( + + ))} + +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/Palette.tsx b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/Palette.tsx new file mode 100644 index 000000000000..c4ee7cb5d5f0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Components/Palette.tsx @@ -0,0 +1,83 @@ +import transparency_checkerboard from 'tgui/assets/transparency_checkerboard.svg'; +import { Button, Section, Stack } from 'tgui-core/components'; +import { KEY_DELETE } from 'tgui-core/keycodes'; +import type { BooleanStyleMap, StringStyleMap } from 'tgui-core/ui'; + +import { colorsAreEqual, colorToCssString } from '../colorSpaces'; +import type { EditorColor } from '../Types/types'; + +export type PaletteProps = { + colors: EditorColor[]; + selectedColor: EditorColor; + onClickColor: (color: EditorColor, rightClick: boolean) => void; + onClickAddColor: () => void; + onRemoveColor: (index: number) => void; + paletteButtonProps?: Partial< + Exclude & StringStyleMap + >; + maxColors?: number; +} & Parameters[0]; + +export const Palette = (props: PaletteProps) => { + const { + colors, + selectedColor, + onClickColor, + onClickAddColor, + onRemoveColor, + paletteButtonProps, + maxColors = Infinity, + style, + ...rest + } = props; + return ( +
+ + {colors.map((color, i) => ( + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tool.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tool.ts new file mode 100644 index 000000000000..9ef7e5e36137 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tool.ts @@ -0,0 +1,26 @@ +import type { SpriteData, SpriteEditorToolContext } from './types'; + +export abstract class Tool { + abstract icon: string; + abstract name: string; + abstract onMouseDown( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + isRightClick?: boolean, + ): boolean | undefined; + onMouseMove?( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + ): void; + onMouseUp?( + context: SpriteEditorToolContext, + data: SpriteData, + x?: number, + y?: number, + ): void; + cancel?(); +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Bucket.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Bucket.ts new file mode 100644 index 000000000000..11743797228f --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Bucket.ts @@ -0,0 +1,35 @@ +import { sendAct as act } from 'tgui/events/act'; +import { colorToHexString } from '../../colorSpaces'; +import { constrainToIconGrid } from '../../helpers'; +import { Tool } from '../Tool'; +import type { SpriteData, SpriteEditorToolContext } from '../types'; + +export class Bucket extends Tool { + icon = 'fill-drip'; + name = 'Fill'; + + onMouseDown( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + isRightClick?: boolean, + ) { + if (isRightClick) return undefined; + const { selectedDir, selectedLayer, currentColor } = context; + const { width, height } = data; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds) return undefined; + act('spriteEditorCommand', { + command: 'transaction', + transaction: { + type: 'bucket', + name: 'Flood Fill', + layer: selectedLayer + 1, + dir: `${selectedDir}`, + color: colorToHexString(currentColor), + point: [px, py], + }, + }); + } +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eraser.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eraser.ts new file mode 100644 index 000000000000..4f0783f5ad12 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eraser.ts @@ -0,0 +1,125 @@ +import { sendAct as act } from 'tgui/events/act'; +import { parseHexColorString } from '../../colorSpaces'; +import { constrainToIconGrid, copyLayer, getDataPixel } from '../../helpers'; +import { Tool } from '../Tool'; +import type { LayerTransaction } from '../Transaction'; +import type { + Dir, + SpriteData, + SpriteEditorToolContext, + StringLayer, +} from '../types'; + +class EraserTransaction implements LayerTransaction { + name = 'Eraser'; + layer: number; + dir: Dir; + points: Map = new Map(); + + constructor(dir: Dir, layer: number) { + this.dir = dir; + this.layer = layer; + } + + addPoint(x: number, y: number, color: string) { + const hashKey = `${x},${y}`; + if (this.points.has(hashKey)) return; + if (parseHexColorString(color).a === 0) return; + this.points.set(`${x},${y}`, [x, y]); + } + + getPreviewLayer(layer: StringLayer) { + const outLayer = copyLayer(layer); + this.points.values().forEach(([x, y]) => { + outLayer[y][x] = '#00000000'; + }); + return outLayer; + } + + commit() { + act('spriteEditorCommand', { + command: 'transaction', + transaction: { + type: 'eraser', + name: 'Eraser', + layer: this.layer + 1, + dir: `${this.dir}`, + points: this.points.values().toArray(), + }, + }); + } +} + +export class Eraser extends Tool { + icon = 'eraser'; + name = 'Eraser'; + currentTransaction: EraserTransaction | null; + + onMouseDown( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + isRightClick: boolean, + ) { + const { selectedDir, selectedLayer, setPreviewLayer, setPreviewData } = + context; + const { width, height, layers } = data; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds || isRightClick) return; + this.currentTransaction = new EraserTransaction(selectedDir, selectedLayer); + this.currentTransaction.addPoint( + px, + py, + getDataPixel(data, selectedLayer, selectedDir, px, py), + ); + setPreviewLayer(selectedLayer); + setPreviewData( + this.currentTransaction.getPreviewLayer( + layers[selectedLayer].data[selectedDir]!, + ), + ); + return true; + } + + onMouseMove( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + ) { + const { currentTransaction } = this; + if (!currentTransaction) return; + const { selectedDir, selectedLayer, setPreviewData } = context; + const { width, height, layers } = data; + const { dir, layer } = currentTransaction; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds) return; + currentTransaction.addPoint( + px, + py, + getDataPixel(data, selectedLayer, selectedDir, px, py), + ); + setPreviewData( + currentTransaction.getPreviewLayer(layers[layer].data[dir]!), + ); + } + + onMouseUp( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + ) { + if (!this.currentTransaction) return; + const { setPreviewLayer, setPreviewData } = context; + setPreviewLayer(undefined); + setPreviewData(undefined); + this.currentTransaction.commit(); + this.currentTransaction = null; + } + + cancel() { + this.currentTransaction = null; + } +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eyedropper.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eyedropper.ts new file mode 100644 index 000000000000..a51502d0c500 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Eyedropper.ts @@ -0,0 +1,28 @@ +import { parseHexColorString } from '../../colorSpaces'; +import { constrainToIconGrid, getDataPixel } from '../../helpers'; +import { Tool } from '../Tool'; +import type { SpriteData, SpriteEditorToolContext } from '../types'; + +export class Eyedropper extends Tool { + icon = 'eye-dropper'; + name = 'Eyedropper'; + + onMouseDown( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + isRightClick?: boolean, + ) { + if (isRightClick) return undefined; + const { selectedDir, selectedLayer, setCurrentColor } = context; + const { width, height } = data; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds) return undefined; + setCurrentColor( + parseHexColorString( + getDataPixel(data, selectedLayer, selectedDir, px, py), + ), + ); + } +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Pencil.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Pencil.ts new file mode 100644 index 000000000000..fa3dc2d2df0f --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Tools/Pencil.ts @@ -0,0 +1,126 @@ +import { sendAct as act } from 'tgui/events/act'; +import { colorToHexString } from '../../colorSpaces'; +import { constrainToIconGrid, copyLayer } from '../../helpers'; +import { Tool } from '../Tool'; +import type { LayerTransaction } from '../Transaction'; +import type { + Dir, + SpriteData, + SpriteEditorToolContext, + StringLayer, +} from '../types'; + +class PencilTransaction implements LayerTransaction { + color: string; + layer: number; + dir: Dir; + points: Map = new Map(); + + constructor(dir: Dir, layer: number, color: string) { + this.dir = dir; + this.layer = layer; + this.color = color; + } + + addPoint(x: number, y: number) { + const hashKey = `${x},${y}`; + if (this.points.has(hashKey)) return; + this.points.set(`${x},${y}`, [x, y]); + } + + getPreviewLayer(layer: StringLayer) { + const outLayer = copyLayer(layer); + this.points.values().forEach(([x, y]) => { + outLayer[y][x] = this.color; + }); + return outLayer; + } + + commit() { + act('spriteEditorCommand', { + command: 'transaction', + transaction: { + type: 'pencil', + name: 'Pencil', + layer: this.layer + 1, + dir: `${this.dir}`, + color: this.color, + points: this.points.values().toArray(), + }, + }); + } +} + +export class Pencil extends Tool { + icon = 'pencil'; + name = 'Pencil'; + currentTransaction: PencilTransaction | null; + + onMouseDown( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + isRightClick: boolean, + ) { + const { + selectedDir, + selectedLayer, + currentColor, + setPreviewLayer, + setPreviewData, + } = context; + const { width, height, layers } = data; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds || isRightClick) return; + this.currentTransaction = new PencilTransaction( + selectedDir, + selectedLayer, + colorToHexString(currentColor), + ); + this.currentTransaction.addPoint(px, py); + setPreviewLayer(selectedLayer); + setPreviewData( + this.currentTransaction.getPreviewLayer( + layers[selectedLayer].data[selectedDir]!, + ), + ); + return true; + } + + onMouseMove( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + ) { + const { currentTransaction } = this; + if (!currentTransaction) return; + const { setPreviewData } = context; + const { width, height, layers } = data; + const { dir, layer } = currentTransaction; + const [px, py, inBounds] = constrainToIconGrid(x, y, width, height); + if (!inBounds) return; + currentTransaction.addPoint(px, py); + setPreviewData( + currentTransaction.getPreviewLayer(layers[layer].data[dir]!), + ); + } + + onMouseUp( + context: SpriteEditorToolContext, + data: SpriteData, + x: number, + y: number, + ) { + if (!this.currentTransaction) return; + const { setPreviewLayer, setPreviewData } = context; + setPreviewLayer(undefined); + setPreviewData(undefined); + this.currentTransaction.commit(); + this.currentTransaction = null; + } + cancel() { + this.currentTransaction = null; + } +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Transaction.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Transaction.ts new file mode 100644 index 000000000000..1810dea58f62 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/Transaction.ts @@ -0,0 +1,8 @@ +import type { Dir, StringLayer } from './types'; + +export type LayerTransaction = { + dir: Dir; + layer: number; + getPreviewLayer(baseLayer: StringLayer): StringLayer; + commit(): void; +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/types.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/types.ts new file mode 100644 index 000000000000..164c34b7d924 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/Types/types.ts @@ -0,0 +1,131 @@ +import type { Dispatch, SetStateAction, useSyncExternalStore } from 'react'; +import type { Box } from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; + +export type RGB = { + r: number; + g: number; + b: number; +}; + +export type RGBA = RGB & { a?: number }; + +export type HSV = { + h?: number; + s?: number; + v: number; +}; + +export type HSVA = HSV & { a?: number }; + +export type EditorColor = HSVA | RGBA; + +export enum Dir { + NORTH = 1, + SOUTH = 2, + EAST = 4, + WEST = 8, +} + +export type IconDirCount = 1 | 4; +export type Layer = EditorColor[][]; +export type LayerStack = Layer[]; +type IconData1Dir = { + dirs: 1; + data: LayerStack; +}; +type IconData4Dirs = { + dirs: 4; + data: Map; +}; +export type IconData = IconData1Dir | IconData4Dirs; + +export type WindowEventHandler = (event: MouseEvent) => void; + +export type ClickAndDragEventHandler = ( + event: MouseEvent, + ref: React.Ref, +) => void; + +export type IncludeOrOmitEntireType = V | (T & V); + +type PickByType = { + [K in keyof T as T[K] extends V ? K : never]: T[K]; +}; + +export type InlineStyle = Pick[0], 'style'>; + +export type BorderStyleProps = Omit< + React.CSSProperties, + keyof PickByType< + { + [K in keyof Required]: K extends `border${string}` + ? React.CSSProperties[K] + : never; + }, + never + > +>; + +export type SubscribeFn = Parameters[0]; + +export type StringLayer = string[][]; + +export type SpriteDataLayer = { + name: string; + visible: BooleanLike; + data: { + [key in Dir]: key extends Dir.SOUTH ? StringLayer : StringLayer | undefined; + }; +}; + +export type SpriteData = { + width: number; + height: number; + dirs: IconDirCount; + backdrop: string; + layers: SpriteDataLayer[]; +}; + +export enum SpriteEditorColorMode { + Rgba = 'rgba', + Rgb = 'rgb', + Greyscale = 'greyscale', +} + +export enum SpriteEditorToolFlags { + Pencil = 1 << 0, + Eraser = 1 << 1, + Dropper = 1 << 2, + Bucket = 1 << 3, + All = (1 << 4) - 1, +} + +export type ServerColorData = { + serverSelectedColor: string; + serverPalette: string[]; + maxServerColors: number; + onSelectServerColor?: string; + onAddServerColor?: string; + onRemoveServerColor?: string; +}; + +export type SpriteEditorData = IncludeOrOmitEntireType< + ServerColorData, + { + colorMode: SpriteEditorColorMode; + undoStack: string[]; + redoStack: string[]; + toolFlags?: SpriteEditorToolFlags; + sprite: SpriteData; + } +>; + +export type SpriteEditorToolContext = { + currentColor: EditorColor; + setCurrentColor: Dispatch>; + selectedDir: Dir; + selectedLayer: number; + setPreviewLayer: Dispatch>; + setPreviewData: Dispatch>; +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/atoms.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/atoms.ts new file mode 100644 index 000000000000..1cdd653ddffa --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/atoms.ts @@ -0,0 +1,40 @@ +import { atom } from 'jotai'; +import { sendAct as act } from 'tgui/events/act'; +import { colorToHexString } from './colorSpaces'; +import type { Tool } from './Types/Tool'; +import { Bucket } from './Types/Tools/Bucket'; +import { Eraser } from './Types/Tools/Eraser'; +import { Eyedropper } from './Types/Tools/Eyedropper'; +import { Pencil } from './Types/Tools/Pencil'; +import { Dir, type EditorColor, type StringLayer } from './Types/types'; + +export const colorsAtom = atom([]); +export const currentColorInternalAtom = atom({ + r: 255, + g: 255, + b: 255, +}); +export const onSelectServerColorAtom = atom(undefined); +export const currentColorAtom = atom( + (get) => get(currentColorInternalAtom), + (get, set, color) => { + const onSetServerColor = get(onSelectServerColorAtom); + if (onSetServerColor) { + act(onSetServerColor, { color: colorToHexString(color) }); + } + set(currentColorInternalAtom, color); + }, +); + +export const tools: Tool[] = [ + new Pencil(), + new Eraser(), + new Eyedropper(), + new Bucket(), +]; + +export const currentToolAtom = atom(tools[0]); +export const dirAtom = atom(Dir.SOUTH); +export const layerAtom = atom(0); +export const previewLayerAtom = atom(); +export const previewDataAtom = atom(); diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/colorSpaces.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/colorSpaces.ts new file mode 100644 index 000000000000..19d50ebc8c9a --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/colorSpaces.ts @@ -0,0 +1,131 @@ +import type { EditorColor, HSV, HSVA, RGB, RGBA } from './Types/types'; + +const rem = (n: number, d: number) => ((n % d) + d) % d; + +export function rgb2hsv(rgb: T): Omit & HSV { + let { r, g, b, ...rest } = rgb; + r /= 255; + g /= 255; + b /= 255; + const v = Math.max(r, g, b); + const min = Math.min(r, g, b); + const delta = v - min; + const s = v ? delta / v : undefined; + let h: number | undefined; + if (delta) { + switch (v) { + case r: { + h = (g - b) / delta; + break; + } + case g: { + h = (b - r) / delta + 2; + break; + } + case b: { + h = (r - g) / delta + 4; + break; + } + } + } + return { + h: h && Math.round(rem(h, 6) * 60), + s: s, + v: v, + ...rest, + }; +} + +export function hsv2rgb(hsv: T): Omit & RGB { + let { h, s, v, ...rest } = hsv; + if (h) { + h = rem(h / 60, 6); + } + v *= 255; + s ??= 0; + let c = s * v; + const x = Math.round(c * (1 - Math.abs(rem(h ?? 0, 2) - 1))); + const m = Math.round(v - c); + c = Math.round(c); + v = Math.round(v); + if (h === undefined || s === 0) { + return { r: v, g: v, b: v, ...rest }; + } + if (h >= 0 && h < 1) { + return { r: c + m, g: x + m, b: m, ...rest }; + } + if (h >= 1 && h < 2) { + return { r: x + m, g: c + m, b: m, ...rest }; + } + if (h >= 2 && h < 3) { + return { r: m, g: c + m, b: x + m, ...rest }; + } + if (h >= 3 && h < 4) { + return { r: m, g: x + m, b: c + m, ...rest }; + } + if (h >= 4 && h < 5) { + return { r: x + m, g: m, b: c + m, ...rest }; + } + if (h >= 5 && h < 6) { + return { r: c + m, g: m, b: x + m, ...rest }; + } + throw new Error( + 'Unreachable code - h is outside the range [0,6], which should not be possible.', + ); +} + +export const hsva2hslString = (hsv: HSVA) => { + const { h = 0, s, v, a } = hsv; + const l = v * (1 - (s ?? 0) / 2); + const sL = l === 0 || l === 1 ? 0 : (v - l) / Math.min(l, 1 - l); + return `hsla(${h}, ${sL * 100}%, ${l * 100}%, ${a ?? 1})`; +}; + +export const isRgb = (color: EditorColor): color is RGB => + Object.keys(color).includes('r'); +export const isHsv = (color: EditorColor): color is HSV => + Object.keys(color).includes('v'); + +export const parseHexColorString = (color: string): EditorColor => ({ + r: Number.parseInt(color.substring(1, 3), 16), + g: Number.parseInt(color.substring(3, 5), 16), + b: Number.parseInt(color.substring(5, 7), 16), + a: color.length >= 9 ? Number.parseInt(color.substring(7, 9), 16) / 255 : 1, +}); + +type BothSpaces = RGBA & HSVA; + +export const asBothSpaces = (color: EditorColor): BothSpaces => + isRgb(color) + ? { ...color, ...rgb2hsv(color) } + : { ...color, ...hsv2rgb(color) }; + +export const rgb2hexstring = (color: RGBA, alwaysIncludeAlpha = true) => { + const { r, g, b, a = 1 } = color; + return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}${ + alwaysIncludeAlpha || a !== 1 + ? Math.round(a * 255) + .toString(16) + .padStart(2, '0') + : '' + }`; +}; + +export const colorToCssString = (color: EditorColor) => + isRgb(color) ? rgb2hexstring(color, false) : hsva2hslString(color); + +export const colorToHexString = (color: EditorColor) => + rgb2hexstring(isRgb(color) ? color : hsv2rgb(color)); + +export const colorsAreEqual = (a: EditorColor, b: EditorColor) => { + if (isHsv(a) && isHsv(b)) { + return a.h === b.h && a.s === b.s && a.v === b.v && a.a === b.a; + } + if (isHsv(a)) { + a = hsv2rgb(a); + } + if (isHsv(b)) { + b = hsv2rgb(b); + } + return a.r === b.r && a.g === b.g && a.b === b.b && a.a === b.a; +}; diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/helpers.ts b/tgui/packages/tgui/interfaces/common/SpriteEditor/helpers.ts new file mode 100644 index 000000000000..6d22cab6d1a4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/helpers.ts @@ -0,0 +1,157 @@ +import { normal } from 'color-blend'; +import { useCallback, useEffect, useState } from 'react'; + +import { hsv2rgb, isRgb, parseHexColorString } from './colorSpaces'; +import type { + ClickAndDragEventHandler, + Dir, + EditorColor, + Layer, + RGBA, + ServerColorData, + SpriteData, + SpriteEditorData, + StringLayer, +} from './Types/types'; + +export function matrix(initializer: () => T, ...dimensions: number[]) { + return dimensions.reduce( + (generator: () => any, dimension) => () => + Array.from({ length: dimension }, generator), + () => initializer(), + ); +} + +export const lerp = (a: number, b: number, t: number) => a + (b - a) * t; + +export const invLerp = (a: number, b: number, x: number) => (x - a) / (b - a); + +export function useResizeObserver( + ref: React.RefObject, + observerCallback: (element: ResizeObserverEntry) => void, +) { + useEffect(() => { + const observer = new ResizeObserver((entries) => { + if (entries.length) observerCallback(entries[0]); + }); + const current = ref.current; + if (!current) return; + observer.observe(current); + return () => observer.unobserve(current); + }, [ref, observerCallback]); +} + +export const useDimensions = ( + ref: React.RefObject, +): [number, number] => { + const [dimensions, setDimensions] = useState<[number, number]>([0, 0]); + useResizeObserver( + ref, + useCallback( + (element) => { + const { width, height } = element.contentRect; + setDimensions([width, height]); + }, + [setDimensions], + ), + ); + return dimensions; +}; + +export function useClickAndDragEventHandler( + ref: React.Ref, + onMouseDown?: ClickAndDragEventHandler, + onMouseMove?: ClickAndDragEventHandler, + onMouseUp?: ClickAndDragEventHandler, +): (MouseEvent) => void { + const moveHandler = (ev: MouseEvent) => onMouseMove?.(ev, ref); + const upHandler = (ev: MouseEvent) => { + onMouseUp?.(ev, ref); + ev.preventDefault(); + window.removeEventListener('mousemove', moveHandler); + }; + return (ev: MouseEvent) => { + onMouseDown?.(ev, ref); + if (ev.defaultPrevented) return; + ev.preventDefault(); + window.addEventListener('mousemove', moveHandler); + window.addEventListener('mouseup', upHandler, { once: true }); + }; +} + +export const constrainToIconGrid = ( + x: number, + y: number, + width: number, + height: number, +): [number, number, boolean] => { + return [ + Math.floor(x), + Math.floor(y), + x >= 0 && x < width && y >= 0 && y < height, + ]; +}; + +export const localizeCoords = ( + ev: MouseEvent, + ref: React.RefObject, + imageWidth: number, + imageHeight: number, +) => { + const { clientX, clientY } = ev; + const { top, left, width, height } = ref.current!.getBoundingClientRect(); + return [ + lerp(0, imageWidth, invLerp(0, width, clientX - left)), + lerp(0, imageHeight, invLerp(0, height, clientY - top)), + ]; +}; + +export const getDataPixel = ( + data: SpriteData, + layer: number, + dir: Dir, + x: number, + y: number, +) => data.layers[layer].data[dir]![y][x] ?? '#00000000'; + +export const getFlattenedSpriteDir = ( + data: SpriteData, + dir: Dir, + selectedLayer: number, + previewLayer?: number, + previewData?: StringLayer, + backdrop: EditorColor = { r: 0, g: 0, b: 0, a: 0 }, +) => { + const { width, height, layers } = data; + const output = matrix( + () => Object.assign({}, backdrop), + width, + height, + )() as Layer; + layers.forEach(({ data: layer, visible }, i) => { + if (!visible && i !== selectedLayer) return; + (previewLayer === i ? previewData : layer[dir])!.forEach((row, y) => { + row.forEach((frontPixelstring, x) => { + const frontPixel = parseHexColorString(frontPixelstring); + const backPixel = output[y][x]; + const outPixel: RGBA = normal( + { a: 1, ...(isRgb(backPixel) ? backPixel : hsv2rgb(backPixel)) }, + { a: 1, ...(isRgb(frontPixel) ? frontPixel : hsv2rgb(frontPixel)) }, + ); + if (outPixel.a === 1) delete outPixel.a; + output[y][x] = outPixel; + }); + }); + }); + return output; +}; + +export function copyLayer(layer: T[][]) { + return [...layer.map((row) => [...row])]; +} + +export function hasServerColorData( + data: SpriteEditorData, +): data is ServerColorData & SpriteEditorData { + return Object.hasOwn(data, 'serverPalette'); +} diff --git a/tgui/packages/tgui/interfaces/common/SpriteEditor/index.tsx b/tgui/packages/tgui/interfaces/common/SpriteEditor/index.tsx new file mode 100644 index 000000000000..cbcd1f8d0227 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/SpriteEditor/index.tsx @@ -0,0 +1,362 @@ +import { useAtom, useAtomValue, useSetAtom } from 'jotai'; +import { useEffect, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Box, Button, Floating, Stack } from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import { capitalize } from 'tgui-core/string'; +import { + colorsAtom, + currentColorAtom, + currentColorInternalAtom, + currentToolAtom, + dirAtom, + layerAtom, + onSelectServerColorAtom, + previewDataAtom, + previewLayerAtom, + tools, +} from './atoms'; +import { + AdvancedCanvas, + type AdvancedCanvasPropsBase, +} from './Components/AdvancedCanvas'; +import { + ColorPicker as BaseColorPicker, + type ColorPickerProps as BaseColorPickerProps, +} from './Components/ColorPicker'; +import { + LayerManager as BaseLayerManager, + type LayerManagerProps as BaseLayerManagerProps, +} from './Components/LayerManager'; +import { + Palette as BasePalette, + type PaletteProps as BasePaletteProps, +} from './Components/Palette'; +import { + colorsAreEqual, + colorToHexString, + parseHexColorString, +} from './colorSpaces'; +import { getFlattenedSpriteDir, localizeCoords } from './helpers'; +import type { Tool } from './Types/Tool'; +import { + type IncludeOrOmitEntireType, + type SpriteData, + SpriteEditorToolFlags, +} from './Types/types'; + +type ToolbarButtonProps = Omit< + Parameters[0], + 'icon' | 'onClick' | 'selected' | 'ellipsis' +>; + +type ToolbarProps = { + toolButtonProps?: ToolbarButtonProps; + perButtonProps?: (tool: Tool, i: number) => ToolbarButtonProps; + toolFlags?: SpriteEditorToolFlags; +} & Parameters[0]; + +type TransactionType = 'undo' | 'redo'; + +type HistoryButtonProps = { + stack: string[]; + type: TransactionType; +}; + +const HistoryButton = (props: HistoryButtonProps) => { + const { stack, type } = props; + const { act } = useBackend(); + const [historyOpen, setHistoryOpen] = useState(false); + const stackEmpty = stack.length < 1; + const action = (count = 1) => + act(`spriteEditorCommand`, { command: type, count }); + return ( + + + {stack.map((transaction, i) => ( + + + + ))} + + + } + > +