diff --git a/.tgs.yml b/.tgs.yml index 76a53577b505b..dd18c3b8a2577 100644 --- a/.tgs.yml +++ b/.tgs.yml @@ -3,7 +3,7 @@ version: 1 # The BYOND version to use (kept in sync with dependencies.sh by the "TGS Test Suite" CI job) # Must be interpreted as a string, keep quoted -byond: "515.1633" +byond: "515.1637" # Folders to create in "/Configuration/GameStaticFiles/" static_files: # Config directory should be static diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm index df0bf1c088073..d596a823a6a23 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm @@ -75,7 +75,7 @@ /turf/open/floor/grass/fairy, /area/ruin/powered/hermit) "wf" = ( -/obj/item/gun/ballistic/rifle/boltaction/pipegun/prime, +/obj/item/gun/energy/laser/musket/prime, /obj/structure/table/wood, /obj/item/flashlight/lantern, /turf/open/floor/wood, diff --git a/_maps/RandomRuins/SpaceRuins/dangerous_research.dmm b/_maps/RandomRuins/SpaceRuins/dangerous_research.dmm index 84a3a2658d3e5..a7eac1fe1a8b1 100644 --- a/_maps/RandomRuins/SpaceRuins/dangerous_research.dmm +++ b/_maps/RandomRuins/SpaceRuins/dangerous_research.dmm @@ -1192,7 +1192,7 @@ }, /obj/effect/decal/cleanable/blood/gibs, /obj/item/organ/internal/brain, -/obj/item/skillchip/job/research_director, +/obj/item/skillchip/research_director, /obj/effect/turf_decal/tile/neutral/half{ dir = 4 }, diff --git a/_maps/deathmatch/maint_mania.dmm b/_maps/deathmatch/maint_mania.dmm index 8bb2522bc5dfa..5b23ac38feb8f 100644 --- a/_maps/deathmatch/maint_mania.dmm +++ b/_maps/deathmatch/maint_mania.dmm @@ -193,7 +193,7 @@ /turf/open/indestructible, /area/deathmatch) "zF" = ( -/obj/item/ammo_casing/shotgun/improvised, +/obj/effect/spawner/random/junk_shell, /turf/open/indestructible, /area/deathmatch) "AG" = ( diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index acef74e76e77a..811ea64f9b4c1 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -49319,7 +49319,7 @@ /obj/machinery/atmospherics/pipe/smart/simple/supply/visible/layer2{ dir = 6 }, -/obj/machinery/meter, +/obj/machinery/meter/layer2, /turf/open/floor/iron, /area/station/engineering/atmos/pumproom) "qQg" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 4510475fe7bc9..c8f317683df64 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -22987,6 +22987,10 @@ dir = 1 }, /area/station/security/prison) +"gZR" = ( +/obj/machinery/power/smes/engineering, +/turf/open/floor/plating, +/area/station/maintenance/department/electrical) "gZT" = ( /obj/structure/cable, /obj/effect/turf_decal/trimline/yellow/filled/line{ @@ -58482,11 +58486,6 @@ /obj/structure/fake_stairs/wood/directional/north, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) -"rPu" = ( -/obj/structure/cable, -/obj/machinery/power/smes/engineering, -/turf/open/floor/plating, -/area/station/maintenance/department/electrical) "rPL" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/rack, @@ -256513,7 +256512,7 @@ qAS fUc jaX glI -rPu +gZR kKL kKL mbG @@ -257541,7 +257540,7 @@ dvS pwd glI tsR -rPu +gZR lUC tXV kvX diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index a646c19a1defb..0b5e99f7613b9 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -5720,7 +5720,12 @@ /turf/open/floor/circuit, /area/station/maintenance/port/aft) "bYN" = ( -/obj/effect/turf_decal/tile/neutral/anticorner/contrasted, +/obj/effect/turf_decal/siding/wideplating_new{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 4 + }, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "bZb" = ( @@ -14895,6 +14900,15 @@ /obj/structure/sign/warning/pods, /turf/closed/wall, /area/station/commons/locker) +"ftQ" = ( +/obj/effect/turf_decal/siding/white{ + dir = 4 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water, +/area/station/service/hydroponics/garden) "fuc" = ( /obj/structure/frame/machine{ anchored = 1 @@ -17135,14 +17149,15 @@ /area/station/service/theater) "gpk" = ( /obj/structure/table, -/obj/item/hatchet, -/obj/item/cultivator, -/obj/item/crowbar, -/obj/item/reagent_containers/cup/watering_can, -/obj/item/plant_analyzer, /obj/effect/turf_decal/stripes/line{ dir = 1 }, +/obj/item/storage/bag/plants/portaseeder, +/obj/item/crowbar, +/obj/item/reagent_containers/cup/watering_can, +/obj/item/cultivator, +/obj/item/hatchet, +/obj/item/plant_analyzer, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "gpv" = ( @@ -23125,9 +23140,19 @@ /turf/open/floor/plating, /area/station/maintenance/port/greater) "ivb" = ( -/obj/effect/turf_decal/tile/neutral/half/contrasted{ - dir = 4 +/obj/machinery/disposal/bin, +/obj/machinery/camera/directional/east{ + c_tag = "Garden" }, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/obj/structure/cable, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/siding/wideplating_new, +/obj/structure/railing, +/obj/effect/turf_decal/tile/green/half/contrasted, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "ivc" = ( @@ -26930,11 +26955,6 @@ /obj/structure/table/wood/poker, /turf/open/floor/wood, /area/station/commons/lounge) -"jGE" = ( -/obj/structure/window/spawner/directional/west, -/obj/structure/window/spawner/directional/north, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "jGG" = ( /obj/machinery/holopad, /obj/effect/turf_decal/box/white{ @@ -28028,11 +28048,10 @@ /turf/open/floor/iron/dark, /area/station/command/gateway) "jYu" = ( -/mob/living/basic/cow{ - name = "Betsy"; - real_name = "Betsy" +/obj/effect/turf_decal/siding/white{ + dir = 9 }, -/turf/open/floor/grass, +/turf/open/water, /area/station/service/hydroponics/garden) "jYv" = ( /obj/machinery/meter, @@ -29001,13 +29020,21 @@ /turf/open/floor/iron/white, /area/station/security/prison) "krt" = ( -/obj/item/storage/bag/plants/portaseeder, -/obj/structure/table, -/obj/item/plant_analyzer, /obj/effect/turf_decal/stripes/line{ dir = 1 }, /obj/machinery/light/directional/south, +/obj/structure/table/wood, +/obj/effect/turf_decal/siding/wideplating_new{ + dir = 4 + }, +/obj/item/storage/toolbox/fishing, +/obj/item/storage/toolbox/fishing, +/obj/item/fishing_rod, +/obj/item/fishing_rod, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 4 + }, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "krL" = ( @@ -30933,6 +30960,7 @@ name = "Featherbottom"; real_name = "Featherbottom" }, +/obj/structure/flora/bush/fullgrass, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "lbL" = ( @@ -31000,10 +31028,12 @@ }, /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/neutral/half/contrasted{ +/obj/effect/landmark/event_spawn, +/obj/effect/turf_decal/siding/wideplating_new/corner, +/obj/effect/turf_decal/tile/green, +/obj/effect/turf_decal/tile/neutral{ dir = 4 }, -/obj/effect/landmark/event_spawn, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "lds" = ( @@ -32381,9 +32411,6 @@ /obj/structure/chair/comfy/brown, /turf/open/floor/engine/cult, /area/station/service/library) -"lJa" = ( -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "lJh" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -33744,7 +33771,7 @@ /turf/open/floor/plating, /area/station/maintenance/department/engine) "mie" = ( -/obj/structure/window/spawner/directional/south, +/obj/structure/flora/bush/flowers_yw, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "mig" = ( @@ -40169,8 +40196,13 @@ /area/station/science/lab) "otG" = ( /obj/item/radio/intercom/directional/east, -/obj/structure/window/spawner/directional/north, -/turf/open/floor/grass, +/obj/effect/turf_decal/siding/white{ + dir = 5 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water, /area/station/service/hydroponics/garden) "otI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -40641,6 +40673,7 @@ name = "Kentucky"; real_name = "Kentucky" }, +/obj/structure/flora/grass/jungle/b/style_random, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "oCR" = ( @@ -44326,17 +44359,12 @@ /turf/open/floor/iron, /area/station/security/execution/transfer) "pRM" = ( -/obj/machinery/disposal/bin, -/obj/machinery/camera/directional/east{ - c_tag = "Garden" - }, -/obj/structure/disposalpipe/trunk{ - dir = 8 +/obj/structure/window/spawner/directional/south, +/mob/living/basic/cow{ + name = "Betsy"; + real_name = "Betsy" }, -/obj/structure/cable, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/turf/open/floor/iron, +/turf/open/floor/grass, /area/station/service/hydroponics/garden) "pSa" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ @@ -59819,6 +59847,12 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/engineering/atmospherics_engine) +"vmU" = ( +/obj/effect/turf_decal/siding/white{ + dir = 8 + }, +/turf/open/water, +/area/station/service/hydroponics/garden) "vmX" = ( /obj/machinery/light/directional/west, /obj/structure/disposalpipe/segment, @@ -64474,6 +64508,7 @@ /area/station/science/robotics/lab) "wRF" = ( /obj/structure/window/spawner/directional/west, +/obj/structure/flora/rock/pile, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "wRL" = ( @@ -66110,6 +66145,15 @@ "xww" = ( /turf/closed/wall/r_wall, /area/station/engineering/storage/tech) +"xwB" = ( +/obj/effect/turf_decal/siding/white{ + dir = 6 + }, +/obj/structure/railing{ + dir = 4 + }, +/turf/open/water, +/area/station/service/hydroponics/garden) "xwD" = ( /obj/effect/turf_decal/trimline/yellow/filled/corner, /turf/open/floor/iron/white, @@ -66293,10 +66337,14 @@ /turf/open/floor/iron/white, /area/station/medical/chemistry) "xyI" = ( -/obj/machinery/door/window/left/directional/west{ - name = "Animal Pen B" +/obj/effect/turf_decal/siding/white{ + dir = 10 }, -/turf/open/floor/grass, +/obj/structure/railing{ + dir = 8 + }, +/obj/structure/sign/clock/directional/south, +/turf/open/water, /area/station/service/hydroponics/garden) "xyM" = ( /obj/structure/cable, @@ -67394,11 +67442,11 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, /obj/structure/cable, /obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/turf_decal/siding/wideplating_new, +/obj/structure/railing, +/obj/effect/turf_decal/tile/green/half/contrasted, /turf/open/floor/iron, /area/station/service/hydroponics/garden) "xUH" = ( @@ -105544,7 +105592,7 @@ fzr iCJ qgy ldg -ivb +bYN bYN krt qXB @@ -105801,9 +105849,9 @@ wRF jzC oCO xUE -jGE +jYu +vmU xyI -wRF qXB kbo qXB @@ -106055,12 +106103,12 @@ cUP mil lqQ lbH -lJa mie pRM +ivb otG -jYu -lJa +ftQ +xwB qXB psZ qXB diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 47c00870306f2..606e8de016c39 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -68078,7 +68078,7 @@ /area/station/maintenance/floor1/starboard/fore) "rxZ" = ( /obj/item/storage/toolbox/maint_kit, -/obj/item/ammo_casing/shotgun/improvised, +/obj/effect/spawner/random/junk_shell, /turf/open/floor/pod/light, /area/station/maintenance/floor4/port) "ryh" = ( diff --git a/_maps/virtual_domains/pirates.dmm b/_maps/virtual_domains/pirates.dmm index 1d330adcc4ddf..28e64519e5738 100644 --- a/_maps/virtual_domains/pirates.dmm +++ b/_maps/virtual_domains/pirates.dmm @@ -2,11 +2,14 @@ "af" = ( /obj/structure/flora/rock/pile/style_2, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "al" = ( /obj/structure/flora/bush/sunny, /turf/open/misc/grass, /area/virtual_domain/fullbright) +"au" = ( +/turf/open/water/beach, +/area/virtual_domain/protected_space/fullbright) "bb" = ( /obj/effect/turf_decal/weather/sand, /turf/open/floor/wood, @@ -20,7 +23,7 @@ /obj/effect/turf_decal/weather/dirt, /obj/structure/flora/rock/pile, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "bI" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -36,7 +39,7 @@ pixel_y = -3 }, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "cr" = ( /obj/item/stack/cannonball/shellball{ pixel_x = 13; @@ -48,7 +51,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "cX" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/item/claymore/cutlass, @@ -70,7 +73,7 @@ }, /obj/effect/turf_decal/weather/dirt, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "dc" = ( /turf/open/misc/beach/coast/corner, /area/virtual_domain/fullbright) @@ -84,7 +87,7 @@ "eO" = ( /obj/structure/flora/rock/pile, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "eP" = ( /obj/effect/turf_decal/weather/dirt{ dir = 4 @@ -92,14 +95,14 @@ /turf/open/misc/beach/coast{ dir = 6 }, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "eS" = ( /turf/open/misc/beach/sand, /area/virtual_domain/fullbright) "fd" = ( /obj/structure/flora/grass/jungle/b, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "fh" = ( /obj/structure/flora/bush/sparsegrass, /obj/structure/flora/bush/lavendergrass, @@ -113,7 +116,7 @@ /obj/effect/turf_decal/siding/wood, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "fR" = ( /obj/effect/baseturf_helper/virtual_domain, /turf/closed/indestructible/binary, @@ -156,12 +159,12 @@ dir = 4 }, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "hq" = ( /obj/structure/closet/crate/grave, /obj/structure/flora/grass/jungle/b, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "jp" = ( /obj/effect/landmark/bitrunning/cache_spawn, /turf/open/misc/beach/sand, @@ -218,14 +221,14 @@ /turf/open/misc/beach/sand, /area/virtual_domain/fullbright) "nb" = ( -/obj/effect/turf_decal/weather/sand{ - dir = 9 - }, /obj/structure/fermenting_barrel{ pixel_x = 6; pixel_y = 11 }, -/obj/effect/mob_spawn/ghost_role/human/pirate/skeleton, +/obj/effect/turf_decal/weather/sand{ + dir = 9 + }, +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate, /turf/open/floor/wood{ icon_state = "wood_large" }, @@ -237,11 +240,14 @@ "nN" = ( /obj/structure/flora/rock, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) +"nQ" = ( +/turf/closed/wall/mineral/wood/nonmetal, +/area/virtual_domain/protected_space/fullbright) "oB" = ( /obj/structure/flora/grass/jungle, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "oL" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/structure/bed/maint, @@ -252,7 +258,7 @@ dir = 1 }, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "pi" = ( /obj/structure/flora/rock/style_3, /turf/open/water/beach, @@ -261,9 +267,12 @@ /obj/structure/flora/bush/flowers_br/style_random, /turf/open/misc/grass, /area/virtual_domain/fullbright) +"py" = ( +/turf/closed/indestructible/binary, +/area/virtual_domain/protected_space/fullbright) "qk" = ( /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "qE" = ( /obj/structure/table/wood, /obj/item/book/manual/wiki/ordnance, @@ -284,7 +293,7 @@ "rm" = ( /obj/structure/closet/crate/goldcrate, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "rn" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -303,7 +312,7 @@ pixel_x = -5 }, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "ru" = ( /obj/effect/mapping_helpers/broken_floor, /obj/effect/decal/cleanable/dirt/dust, @@ -315,6 +324,9 @@ /obj/item/toy/plush/beeplushie, /turf/open/floor/wood, /area/virtual_domain/fullbright) +"sb" = ( +/turf/open/water/beach, +/area/virtual_domain/protected_space) "sj" = ( /obj/effect/mine/explosive/light, /turf/open/misc/beach/sand, @@ -382,7 +394,7 @@ dir = 10 }, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "wj" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -401,7 +413,7 @@ pixel_y = -1 }, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "wH" = ( /obj/structure/fluff/beach_umbrella{ pixel_x = -7; @@ -430,7 +442,7 @@ pixel_y = 10 }, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "zj" = ( /obj/effect/mapping_helpers/burnt_floor, /obj/effect/decal/cleanable/garbage, @@ -445,7 +457,7 @@ /obj/item/gun/energy/laser/retro, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "zR" = ( /obj/effect/baseturf_helper/virtual_domain, /turf/template_noop, @@ -508,7 +520,7 @@ pixel_y = 22 }, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Ci" = ( /turf/closed/wall/mineral/wood/nonmetal, /area/virtual_domain/fullbright) @@ -524,12 +536,12 @@ "CL" = ( /obj/structure/flora/rock/pile/jungle/large, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Dd" = ( /obj/effect/turf_decal/siding/wood, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "DM" = ( /turf/closed/mineral/random/jungle, /area/virtual_domain/fullbright) @@ -567,7 +579,7 @@ "Gl" = ( /obj/structure/flora/rock/pile/style_3, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Gy" = ( /obj/structure/flora/rock/style_2, /turf/open/water/beach, @@ -583,16 +595,20 @@ }, /turf/open/floor/carpet/blue, /area/virtual_domain) +"Iv" = ( +/obj/structure/flora/rock/style_3, +/turf/open/water/beach, +/area/virtual_domain/protected_space/fullbright) "ID" = ( /obj/effect/landmark/bitrunning/cache_spawn, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "IW" = ( /obj/effect/turf_decal/weather/dirt{ dir = 5 }, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Jf" = ( /obj/structure/flora/bush/sparsegrass, /turf/open/misc/grass, @@ -637,7 +653,10 @@ pixel_y = -4 }, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) +"Li" = ( +/turf/closed/mineral/random/jungle, +/area/virtual_domain/protected_space/fullbright) "LC" = ( /mob/living/basic/trooper/pirate/melee, /turf/open/misc/grass, @@ -649,7 +668,7 @@ }, /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "LP" = ( /obj/effect/turf_decal/weather/sand{ dir = 5 @@ -694,11 +713,11 @@ pixel_y = 18 }, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Oi" = ( /obj/effect/mob_spawn/corpse/human/pirate, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Ov" = ( /turf/open/misc/beach/coast{ dir = 6 @@ -714,7 +733,7 @@ "Oz" = ( /obj/structure/flora/rock/pile/jungle/style_2, /turf/open/misc/dirt/jungle, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "OD" = ( /obj/effect/turf_decal/weather/sand{ dir = 6 @@ -726,7 +745,7 @@ dir = 1 }, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "Qc" = ( /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood{ @@ -736,12 +755,12 @@ "QF" = ( /obj/effect/turf_decal/weather/dirt, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "QG" = ( /obj/effect/turf_decal/weather/dirt, /obj/effect/turf_decal/weather/dirt, /turf/open/water/beach, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space) "QN" = ( /obj/structure/barricade/sandbags, /obj/effect/turf_decal/weather/sand{ @@ -789,7 +808,7 @@ }, /obj/structure/closet/cabinet, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "RJ" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -861,7 +880,7 @@ dir = 1 }, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "Vx" = ( /obj/structure/flora/rock/style_4, /turf/open/water/beach, @@ -869,7 +888,7 @@ "VE" = ( /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "VG" = ( /obj/effect/turf_decal/siding/wood{ dir = 8 @@ -901,7 +920,7 @@ "Wx" = ( /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "WW" = ( /turf/template_noop, /area/virtual_domain/fullbright) @@ -932,7 +951,7 @@ /area/virtual_domain/fullbright) "Yy" = ( /turf/open/floor/wood, -/area/virtual_domain/fullbright) +/area/virtual_domain/protected_space/fullbright) "YJ" = ( /obj/structure/table/wood, /obj/effect/turf_decal/siding/wood{ @@ -1226,10 +1245,10 @@ DM hb hb hb -hb -hb -hb -hb +py +py +py +py xg xg xg @@ -1270,10 +1289,10 @@ DM hb hb DM -DM -DM -DM -hb +Li +Li +Li +py xg xg xg @@ -1314,10 +1333,10 @@ sj hb hb DM -DM -DM -DM -hb +Li +Li +Li +py xg xg xg @@ -1358,13 +1377,13 @@ mp Ov xp xp -xp -xp -xp -hb -hb -hb -hb +au +au +au +py +py +py +py xg xg xg @@ -1402,15 +1421,15 @@ xp xp xp NE -xp -xp -xp -xp -xp -Ci -hb -hb -hb +au +au +au +au +au +nQ +py +py +py xg xg xg @@ -1446,15 +1465,15 @@ xp xp xp Gy -xp -xp -xp -xp -xp -Ci +au +au +au +au +au +nQ bI VE -hb +py hb hb hb @@ -1490,12 +1509,12 @@ xp xp xp xp -xp -xp -xp -xp +au +au +au +au nN -Ci +nQ rn VE fw @@ -1534,12 +1553,12 @@ xp xp xp xp -xp -xp -xp -xp -pi -Ci +au +au +au +au +Iv +nQ QX Yy zk @@ -1578,13 +1597,13 @@ xp xp xp xp -xp -xp -xp -xp -xp -Ci -Ci +au +au +au +au +au +nQ +nQ Yy Dd ub @@ -1622,14 +1641,14 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au Wx ub ub @@ -1666,15 +1685,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au ub ub ub @@ -1710,15 +1729,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au ub ub ub @@ -1754,15 +1773,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au Wx -xp +au ub ub ub @@ -1798,15 +1817,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -Ci +au +au +au +au +au +au +nQ fw -xp +au xp xp xp @@ -1842,12 +1861,12 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au Uw cr vR @@ -1886,13 +1905,13 @@ xp xp xp xp -xp -xp -xp -xp -xp +au +au +au +au +au af -Ci +nQ LK fw xp @@ -1930,12 +1949,12 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au oM VE eO @@ -1974,15 +1993,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au xp xp xp @@ -2018,15 +2037,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au xp xp xp @@ -2062,15 +2081,15 @@ xp xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au xp xp DM @@ -2098,7 +2117,7 @@ DM DM DM eP -xp +sb DM DM hb @@ -2106,15 +2125,15 @@ hb xp xp xp -xp -xp -xp -xp -xp -xp -xp -xp -xp +au +au +au +au +au +au +au +au +au hb DM DM @@ -2150,15 +2169,15 @@ hb hb xp xp -xp -xp -xp -xp -xp -xp -xp -xp -hb +au +au +au +au +au +au +au +au +py hb DM DM @@ -2194,15 +2213,15 @@ DM hb hb hb -hb -xp -xp -xp -xp -xp -xp -hb -hb +py +au +au +au +au +au +au +py +py hb hb hb @@ -2231,21 +2250,21 @@ DM DM qk IW -xp +sb DM DM DM DM DM DM -hb -hb -hb -hb -hb -hb -hb -hb +py +py +py +py +py +py +py +py xg xg xg @@ -2277,7 +2296,7 @@ Gl qk IW hn -xp +sb DM DM DM @@ -2322,7 +2341,7 @@ qk qk Oi OW -xp +sb be Oz DM @@ -2366,7 +2385,7 @@ DM qk CL IW -xp +sb QG qk rm diff --git a/_maps/virtual_domains/psyker_zombies.dmm b/_maps/virtual_domains/psyker_zombies.dmm index a20e260bbcf04..1d4307ebdcef8 100644 --- a/_maps/virtual_domains/psyker_zombies.dmm +++ b/_maps/virtual_domains/psyker_zombies.dmm @@ -19,7 +19,7 @@ "h" = ( /obj/structure/rack, /turf/open/indestructible/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "i" = ( /obj/structure/sign/warning/directional/east, /turf/open/chasm, @@ -61,10 +61,20 @@ /obj/effect/mapping_helpers/airlock/abandoned, /turf/open/indestructible/dark, /area/ruin/space/has_grav/powered/virtual_domain) +"D" = ( +/turf/open/indestructible/dark, +/area/virtual_domain/protected_space) "F" = ( /obj/structure/mystery_box/guns, /turf/open/indestructible/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) +"I" = ( +/turf/closed/indestructible/binary, +/area/virtual_domain/protected_space) +"J" = ( +/obj/machinery/door/airlock/abductor, +/turf/open/indestructible/dark, +/area/virtual_domain/protected_space) "K" = ( /obj/effect/baseturf_helper/virtual_domain, /turf/closed/indestructible/binary, @@ -530,10 +540,10 @@ Y Y a Y -a -a -a -a +I +I +I +I a a a @@ -557,10 +567,10 @@ Y Y Y Y -a +I h -Y -Y +D +D t t t @@ -584,10 +594,10 @@ X Y Y Y -a +I F -Y -Y +D +D t t t @@ -611,10 +621,10 @@ a Y Y Y -a -Y -Y -Y +I +D +D +D t t t @@ -638,10 +648,10 @@ Y Y Y Y -T -Y -Y -Y +J +D +D +D t t t @@ -665,10 +675,10 @@ Y R Y Y -a -Y -Y -Y +I +D +D +D t t t @@ -692,10 +702,10 @@ Y Y Y Y -a +I F -Y -Y +D +D t t t @@ -719,10 +729,10 @@ a a Y Y -a +I h -Y -Y +D +D t t t @@ -746,10 +756,10 @@ a a a Y -a -a -a -a +I +I +I +I a a a diff --git a/_maps/virtual_domains/syndicate_assault.dmm b/_maps/virtual_domains/syndicate_assault.dmm index d3cb42a8eeb56..81bb35a257a75 100644 --- a/_maps/virtual_domains/syndicate_assault.dmm +++ b/_maps/virtual_domains/syndicate_assault.dmm @@ -26,7 +26,7 @@ /obj/item/stack/sheet/glass/fifty, /obj/item/stack/rods/fifty, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "aO" = ( /obj/machinery/recharge_station, /turf/open/floor/mineral/plastitanium, @@ -44,7 +44,7 @@ /area/ruin/space/has_grav/powered/virtual_domain) "bG" = ( /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "cc" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -55,7 +55,7 @@ "cj" = ( /obj/structure/transit_tube/crossing, /turf/closed/wall/r_wall/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "ct" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -115,7 +115,7 @@ /obj/item/gun/ballistic/automatic/l6_saw/unrestricted, /obj/item/ammo_box/magazine/m7mm, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "da" = ( /obj/machinery/stasis, /turf/open/floor/plastic, @@ -132,6 +132,9 @@ /obj/item/paper/fluff/ruins/forgottenship/powerissues, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) +"dp" = ( +/turf/open/floor/mineral/plastitanium, +/area/virtual_domain/protected_space) "dw" = ( /obj/machinery/light/small/directional/south, /turf/open/floor/mineral/plastitanium, @@ -164,11 +167,11 @@ /obj/item/card/id/advanced/black/syndicate_command/crew_id, /obj/item/card/id/advanced/black/syndicate_command/crew_id, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "fd" = ( /obj/structure/transit_tube/crossing, /turf/open/space/basic, -/area/space) +/area/virtual_domain/protected_space) "fG" = ( /obj/structure/toilet{ dir = 1 @@ -185,7 +188,7 @@ /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) "gD" = ( -/obj/effect/mob_spawn/ghost_role/human/syndicatespace, +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/syndie, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/powered/virtual_domain) "hg" = ( @@ -221,7 +224,7 @@ "iL" = ( /obj/structure/sign/departments/cargo, /turf/closed/wall/r_wall/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "iU" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -229,7 +232,7 @@ /obj/item/melee/energy/sword/saber/red, /obj/machinery/light/small/directional/north, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "iW" = ( /obj/structure/table/reinforced, /obj/machinery/button/door{ @@ -319,7 +322,7 @@ dir = 4 }, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "ln" = ( /obj/machinery/turretid{ control_area = "/area/ruin/space/has_grav/syndicate_forgotten_ship"; @@ -350,6 +353,10 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) +"mA" = ( +/obj/machinery/light/small/directional/south, +/turf/open/floor/mineral/plastitanium, +/area/virtual_domain/protected_space) "mD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 10 @@ -357,12 +364,19 @@ /obj/item/wrench, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) +"mL" = ( +/obj/structure/tank_dispenser/oxygen, +/turf/open/floor/mineral/plastitanium, +/area/virtual_domain/protected_space) "nk" = ( /obj/machinery/power/apc/auto_name/directional/north, /obj/effect/mapping_helpers/apc/syndicate_access, /obj/structure/cable, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/powered/virtual_domain) +"nn" = ( +/turf/closed/mineral/random, +/area/virtual_domain/protected_space) "nB" = ( /turf/closed/mineral/random, /area/space) @@ -428,7 +442,7 @@ "qU" = ( /obj/structure/sign/poster/contraband/c20r, /turf/closed/wall/r_wall/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "qY" = ( /obj/machinery/light/small/directional/south, /obj/effect/landmark/bitrunning/cache_spawn, @@ -462,14 +476,14 @@ /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) "rP" = ( -/obj/effect/mob_spawn/ghost_role/human/syndicatespace, /obj/machinery/light/small/directional/south, +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/syndie, /turf/open/floor/mineral/plastitanium, /area/ruin/space/has_grav/powered/virtual_domain) "sg" = ( /obj/machinery/ore_silo, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "sq" = ( /obj/machinery/door/window/left/directional/south{ name = "Control Room"; @@ -501,7 +515,7 @@ amount = 15 }, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "sL" = ( /obj/structure/chair/comfy, /turf/open/floor/mineral/plastitanium, @@ -573,7 +587,7 @@ /obj/item/storage/medkit/regular, /obj/machinery/light/small/directional/north, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "xJ" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -638,6 +652,9 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) +"zN" = ( +/turf/closed/wall/r_wall/syndicate, +/area/virtual_domain/protected_space) "Aa" = ( /obj/structure/chair/comfy/shuttle, /turf/open/floor/mineral/plastitanium, @@ -645,7 +662,7 @@ "Bm" = ( /obj/effect/baseturf_helper/virtual_domain, /turf/closed/indestructible/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "BK" = ( /obj/structure/lattice/catwalk, /obj/structure/cable, @@ -717,7 +734,7 @@ /obj/item/ammo_box/magazine/smgm45, /obj/item/gun/ballistic/automatic/c20r/unrestricted, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "DA" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -790,7 +807,7 @@ /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) "Ia" = ( -/obj/effect/mob_spawn/ghost_role/human/syndicatespace/captain, +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/syndie, /turf/open/floor/carpet/royalblack, /area/ruin/space/has_grav/powered/virtual_domain) "Id" = ( @@ -841,7 +858,7 @@ /obj/structure/cable, /obj/structure/fans/tiny, /turf/open/floor/plating, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "IV" = ( /obj/machinery/door/airlock/grunge{ name = "Syndicate Ship Airlock" @@ -885,7 +902,7 @@ "Lk" = ( /obj/structure/transit_tube/crossing, /turf/closed/mineral/random, -/area/space) +/area/virtual_domain/protected_space) "Lo" = ( /obj/structure/filingcabinet, /obj/machinery/door/window/left/directional/west{ @@ -894,6 +911,10 @@ }, /turf/open/floor/mineral/plastitanium/red, /area/ruin/space/has_grav/powered/virtual_domain) +"LB" = ( +/obj/structure/cable, +/turf/open/floor/mineral/plastitanium, +/area/virtual_domain/protected_space) "Mc" = ( /obj/structure/closet/syndicate{ anchored = 1; @@ -938,7 +959,7 @@ req_access = list("syndicate") }, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Nr" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -951,14 +972,14 @@ }, /obj/machinery/light/small/directional/south, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Of" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") }, /obj/item/disk/surgery/forgottenship, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Ox" = ( /obj/machinery/atmospherics/components/unary/vent_pump, /turf/open/floor/mineral/plastitanium, @@ -969,7 +990,7 @@ /obj/item/storage/toolbox/syndicate, /obj/item/storage/toolbox/syndicate, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "OI" = ( /obj/structure/chair/comfy/shuttle{ dir = 1 @@ -988,7 +1009,7 @@ }, /obj/effect/mapping_helpers/airlock/access/all/syndicate/general, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Qg" = ( /obj/machinery/suit_storage_unit/syndicate{ helmet_type = /obj/item/clothing/head/helmet/space/syndicate/black; @@ -1009,7 +1030,7 @@ /obj/item/dualsaber/green, /obj/machinery/light/small/directional/east, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "QG" = ( /obj/structure/tank_dispenser/oxygen, /turf/closed/mineral/random, @@ -1033,7 +1054,7 @@ "RU" = ( /obj/machinery/suit_storage_unit/syndicate, /turf/open/floor/mineral/plastitanium, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Sc" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -1078,11 +1099,14 @@ /area/ruin/space/has_grav/powered/virtual_domain) "TB" = ( /turf/closed/indestructible/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "UQ" = ( /obj/structure/sign/poster/contraband/syndicate_recruitment, /turf/closed/wall/r_wall/syndicate, /area/ruin/space/has_grav/powered/virtual_domain) +"Vg" = ( +/turf/open/space/basic, +/area/virtual_domain/protected_space) "Vk" = ( /obj/machinery/porta_turret/syndicate/energy{ dir = 4; @@ -1101,7 +1125,7 @@ "Wd" = ( /obj/structure/sign/poster/contraband/tools, /turf/closed/wall/r_wall/syndicate, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Wy" = ( /obj/structure/closet/crate/secure/gear{ req_access = list("syndicate") @@ -1132,7 +1156,7 @@ /obj/item/clothing/head/helmet/space/syndicate/black/engie, /obj/item/clothing/suit/space/syndicate/black/engie, /turf/open/floor/pod/dark, -/area/ruin/space/has_grav/powered/virtual_domain) +/area/virtual_domain/protected_space) "Yi" = ( /obj/effect/landmark/bitrunning/cache_spawn, /turf/open/floor/plastic, @@ -2611,21 +2635,21 @@ qx nB nB we -ru -ru -uP +zN +zN +dp sg -ru -ru -nB -nB -nB -nB -qx -qx -qx -qx -qx +zN +zN +nn +nn +nn +nn +Vg +Vg +Vg +Vg +Vg sM sM sM @@ -2670,9 +2694,9 @@ qx nB nB qU -Fp -uP -uP +mL +dp +dp li cj Lk @@ -2727,21 +2751,21 @@ qx qx nB nB -ru +zN eB -uP -uP -uP -ru -nB -qx -qx -qx -qx -qx -qx -qx -qx +dp +dp +dp +zN +nn +Vg +Vg +Vg +Vg +Vg +Vg +Vg +Vg sM sM sM @@ -2785,12 +2809,12 @@ qx qx qx nB -ru +zN wL -oM -uP -dw -ru +LB +dp +mA +zN yZ nB nB @@ -2845,10 +2869,10 @@ qx qx Wd OH -oM -uP +LB +dp RU -ru +zN we nB nB @@ -2902,11 +2926,11 @@ BK BK BK IH -oM -oM -uP +LB +LB +dp RU -ru +zN nB nB qx @@ -2961,10 +2985,10 @@ qx qx iL cZ -uP -uP +dp +dp RU -ru +zN nB nB qx @@ -3017,12 +3041,12 @@ qx qx qx nB -ru -ru +zN +zN Dj QF -ru -ru +zN +zN nB nB qx @@ -3076,10 +3100,10 @@ qx nB nB nB -ru -ru -ru -ru +zN +zN +zN +zN nB nB we diff --git a/code/__DEFINES/_flags.dm b/code/__DEFINES/_flags.dm index 74af498376ef6..6d034fb0f4092 100644 --- a/code/__DEFINES/_flags.dm +++ b/code/__DEFINES/_flags.dm @@ -129,6 +129,8 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define EVENT_PROTECTED (1<<15) /// This Area Doesn't have Flood or Bomb Admin Messages, but will still log #define QUIET_LOGS (1<<16) +/// This area does not allow virtual entities to enter. +#define VIRTUAL_SAFE_AREA (1<<17) /* These defines are used specifically with the atom/pass_flags bitmask diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 2397a6c14c666..c865c578d6324 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -238,6 +238,8 @@ GLOBAL_LIST_INIT(ai_employers, list( #define IS_LUNATIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/lunatic)) /// Checks if the given mob is either a heretic, heretic monster or a lunatic. #define IS_HERETIC_OR_MONSTER(mob) (IS_HERETIC(mob) || IS_HERETIC_MONSTER(mob) || IS_LUNATIC(mob)) +/// CHecks if the given mob is in the mansus realm +#define IS_IN_MANSUS(mob) (istype(get_area(mob), /area/centcom/heretic_sacrifice)) /// Checks if the given mob is a wizard #define IS_WIZARD(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/wizard)) @@ -251,6 +253,9 @@ GLOBAL_LIST_INIT(ai_employers, list( /// Checks if the given mob is a malf ai. #define IS_MALF_AI(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/malf_ai)) +/// Checks if the given mob is a spy! +#define IS_SPY(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/spy)) + /// List of human antagonist types which don't spawn directly on the space station GLOBAL_LIST_INIT(human_invader_antagonists, list( /datum/antagonist/abductor, diff --git a/code/__DEFINES/dcs/signals/signals_bitrunning.dm b/code/__DEFINES/dcs/signals/signals_bitrunning.dm index 74d418182d597..150c26080221c 100644 --- a/code/__DEFINES/dcs/signals/signals_bitrunning.dm +++ b/code/__DEFINES/dcs/signals/signals_bitrunning.dm @@ -44,3 +44,6 @@ // Ladder /// from /obj/structure/hololadder/disconnect() #define COMSIG_BITRUNNER_LADDER_SEVER "bitrunner_ladder_sever" + +/// Sent when a server console is emagged +#define COMSIG_BITRUNNER_SERVER_EMAGGED "bitrunner_server_emagged" diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index eb6216e7806d8..94e9105f4a387 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -16,6 +16,10 @@ /// it represents the sides of our directional border object that have a neighbor /// Is incompatible with SMOOTH_CORNERS because border objects don't have corners #define SMOOTH_BORDER_OBJECT (1<<6) +/// Has a smooth broken sprite, used to decide whether to apply an offset to the broken overlay or not. For /turf/open only. +#define SMOOTH_BROKEN_TURF (1<<7) +/// Has a smooth burnt sprite, used to decide whether to apply an offset to the burnt overlay or not. For /turf/open only. +#define SMOOTH_BURNT_TURF (1<<8) DEFINE_BITFIELD(smoothing_flags, list( "SMOOTH_CORNERS" = SMOOTH_CORNERS, @@ -25,6 +29,8 @@ DEFINE_BITFIELD(smoothing_flags, list( "SMOOTH_QUEUED" = SMOOTH_QUEUED, "SMOOTH_OBJ" = SMOOTH_OBJ, "SMOOTH_BORDER_OBJECT" = SMOOTH_BORDER_OBJECT, + "SMOOTH_BROKEN_TURF" = SMOOTH_BROKEN_TURF, + "SMOOTH_BURNT_TURF" = SMOOTH_BURNT_TURF, )) /// Components of a smoothing junction diff --git a/code/__DEFINES/projectiles.dm b/code/__DEFINES/projectiles.dm index e471fe2324d93..ae61c5e50b7f0 100644 --- a/code/__DEFINES/projectiles.dm +++ b/code/__DEFINES/projectiles.dm @@ -64,6 +64,8 @@ #define CALIBER_HOOK "hook" /// The caliber used by the changeling tentacle mutation. #define CALIBER_TENTACLE "tentacle" +/// The caliber used by pipeguns and pipe pistols +#define CALIBER_JUNK "junk" /// For gunpoints, how many tiles around the target the shooter can roam without losing their shot #define GUNPOINT_SHOOTER_STRAY_RANGE 2 diff --git a/code/__DEFINES/pronouns.dm b/code/__DEFINES/pronouns.dm index c0515426e353a..d137970f217eb 100644 --- a/code/__DEFINES/pronouns.dm +++ b/code/__DEFINES/pronouns.dm @@ -17,6 +17,8 @@ /// s, null (she looks, they look) "%PRONOUN_s" = "p_s" /// es, null (she goes, they go) "%PRONOUN_es" = "p_es" +/// Don't forget to update the grep if you add more! tools/ci/check_grep.sh -> pronoun helper spellcheck + /// A list for all the pronoun procs, if you need to iterate or search through it or something. #define ALL_PRONOUNS list( \ "%PRONOUN_they" = TYPE_PROC_REF(/datum, p_they), \ diff --git a/code/__DEFINES/skills.dm b/code/__DEFINES/skills.dm index f6ecde7c2e10b..5a945d7ec5790 100644 --- a/code/__DEFINES/skills.dm +++ b/code/__DEFINES/skills.dm @@ -46,3 +46,9 @@ ///The base modifier a boulder's size grants to the mining skill. #define MINING_SKILL_BOULDER_SIZE_XP 10 + +// Skillchip categories +//Various skillchip categories. Use these when setting which categories a skillchip restricts being paired with +//while using the SKILLCHIP_RESTRICTED_CATEGORIES flag +#define SKILLCHIP_CATEGORY_GENERAL "general" +#define SKILLCHIP_CATEGORY_JOB "job" diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 295c09f6d465e..cbd73aff89952 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -104,3 +104,8 @@ * Finds the midpoint of two given turfs. */ #define TURF_MIDPOINT(a, b) (locate(((a.x + b.x) * 0.5), (a.y + b.y) * 0.5, (a.z + b.z) * 0.5)) + +/// Defines the x offset to apply to larger smoothing turfs (such as grass). +#define LARGE_TURF_SMOOTHING_X_OFFSET -9 +/// Defines the y offset to apply to larger smoothing turfs (such as grass). +#define LARGE_TURF_SMOOTHING_Y_OFFSET -9 diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm index b5f84176d594b..28789d938d381 100644 --- a/code/controllers/subsystem/dynamic/dynamic_rulesets.dm +++ b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm @@ -140,28 +140,41 @@ /// This function is here to ensure the antag ratio is kept under control while scaling up. /// Returns how much threat to actually spend in the end. /datum/dynamic_ruleset/proc/scale_up(population, max_scale) + SHOULD_NOT_OVERRIDE(TRUE) if (!scaling_cost) return 0 var/antag_fraction = 0 - for(var/_ruleset in (SSdynamic.executed_rules + list(src))) // we care about the antags we *will* assign, too - var/datum/dynamic_ruleset/ruleset = _ruleset - antag_fraction += ((1 + ruleset.scaled_times) * ruleset.get_antag_cap(population)) / SSdynamic.roundstart_pop_ready + for(var/datum/dynamic_ruleset/ruleset as anything in (SSdynamic.executed_rules + list(src))) // we care about the antags we *will* assign, too + antag_fraction += ruleset.get_antag_cap_scaling_included(population) / SSdynamic.roundstart_pop_ready for(var/i in 1 to max_scale) if(antag_fraction < 0.25) scaled_times += 1 - antag_fraction += get_antag_cap(population) / SSdynamic.roundstart_pop_ready // we added new antags, gotta update the % + antag_fraction += get_scaling_antag_cap(population) / SSdynamic.roundstart_pop_ready // we added new antags, gotta update the % return scaled_times * scaling_cost +/// Returns how many more antags to add while scaling with a given population. +/// By default rulesets scale linearly, but you can override this to make them scale differently. +/datum/dynamic_ruleset/proc/get_scaling_antag_cap(population) + return get_antag_cap(population) + /// Returns what the antag cap with the given population is. /datum/dynamic_ruleset/proc/get_antag_cap(population) + SHOULD_NOT_OVERRIDE(TRUE) if (isnum(antag_cap)) return antag_cap return CEILING(population / antag_cap["denominator"], 1) + (antag_cap["offset"] || 0) +/// Gets the 'final' antag cap for this ruleset, which is the base cap plus the scaled cap. +/datum/dynamic_ruleset/proc/get_antag_cap_scaling_included(population) + SHOULD_NOT_OVERRIDE(TRUE) + var/base_cap = get_antag_cap(population) + var/modded_cap = scaled_times * get_scaling_antag_cap(population) + return base_cap + modded_cap + /// This is called if persistent variable is true everytime SSTicker ticks. /datum/dynamic_ruleset/proc/rule_process() return diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm index 1f315391a8f2c..12ec28c707212 100644 --- a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm @@ -33,8 +33,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE) /datum/dynamic_ruleset/roundstart/traitor/pre_execute(population) . = ..() - var/num_traitors = get_antag_cap(population) * (scaled_times + 1) - for (var/i = 1 to num_traitors) + for (var/i in 1 to get_antag_cap_scaling_included(population)) if(candidates.len <= 0) break var/mob/M = pick_n_take(candidates) @@ -121,7 +120,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE) /datum/dynamic_ruleset/roundstart/traitorbro/pre_execute(population) . = ..() - for (var/_ in 1 to get_antag_cap(population) * (scaled_times + 1)) + for (var/i in 1 to get_antag_cap_scaling_included(population)) var/mob/candidate = pick_n_take(candidates) if (isnull(candidate)) break @@ -171,8 +170,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE) /datum/dynamic_ruleset/roundstart/changeling/pre_execute(population) . = ..() - var/num_changelings = get_antag_cap(population) * (scaled_times + 1) - for (var/i = 1 to num_changelings) + for (var/i in 1 to get_antag_cap_scaling_included(population)) if(candidates.len <= 0) break var/mob/M = pick_n_take(candidates) @@ -717,13 +715,15 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE) required_candidates = 3 // lives or dies by there being a few spies weight = 5 cost = 8 - scaling_cost = 101 // see below - minimum_players = 8 - antag_cap = list("denominator" = 8, "offset" = 1) // should have quite a few spies to work against each other + scaling_cost = 4 + minimum_players = 10 + antag_cap = list("denominator" = 20, "offset" = 1) requirements = list(8, 8, 8, 8, 8, 8, 8, 8, 8, 8) + /// What fraction is added to the antag cap for each additional scale + var/fraction_per_scale = 0.2 /datum/dynamic_ruleset/roundstart/spies/pre_execute(population) - for(var/i in 1 to get_antag_cap(population) * (scaled_times + 1)) + for(var/i in 1 to get_antag_cap_scaling_included(population)) if(length(candidates) <= 0) break var/mob/picked_player = pick_n_take(candidates) @@ -733,7 +733,6 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE) GLOB.pre_setup_antags += picked_player.mind return TRUE -/datum/dynamic_ruleset/roundstart/spies/scale_up(population, max_scale) - // Disabled (at least until dynamic can handle scaling this better) - // Because spies have a very low demoninator, this can easily spawn like 30 of them - return 0 +// Scaling adds a fraction of the amount of additional spies rather than the full amount. +/datum/dynamic_ruleset/roundstart/spies/get_scaling_antag_cap(population) + return ceil(..() * fraction_per_scale) diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index 35615c6704c42..1a981f90aedbd 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -235,9 +235,10 @@ multiple modular subtrees with behaviors ///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is. /datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents) SIGNAL_HANDLER - var/mob/mob_pawn = pawn - if((mob_pawn?.client && !continue_processing_when_client)) - return + if (ismob(pawn)) + var/mob/mob_pawn = pawn + if((mob_pawn?.client && !continue_processing_when_client)) + return if(old_turf) SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src if(new_turf) diff --git a/code/datums/components/crafting/ranged_weapon.dm b/code/datums/components/crafting/ranged_weapon.dm index 0cf681ac2b107..88b721d3cb639 100644 --- a/code/datums/components/crafting/ranged_weapon.dm +++ b/code/datums/components/crafting/ranged_weapon.dm @@ -181,11 +181,29 @@ /datum/crafting_recipe/pipegun name = "Pipegun" result = /obj/item/gun/ballistic/rifle/boltaction/pipegun - reqs = list(/obj/item/weaponcrafting/receiver = 1, - /obj/item/pipe = 1, + reqs = list( + /obj/item/weaponcrafting/receiver = 1, + /obj/item/pipe = 2, /obj/item/weaponcrafting/stock = 1, + /obj/item/storage/toolbox = 1, // for the screws + /obj/item/stack/sticky_tape = 1, + ) + tool_behaviors = list(TOOL_SCREWDRIVER) + time = 5 SECONDS + category = CAT_WEAPON_RANGED + +/datum/crafting_recipe/pipepistol + name = "Pipe Pistol" + result = /obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol + reqs = list( + /obj/item/weaponcrafting/receiver = 1, + /obj/item/pipe = 1, + /obj/item/stock_parts/servo = 2, + /obj/item/stack/sheet/mineral/wood = 4, + /obj/item/storage/toolbox = 1, // for the screws /obj/item/stack/sticky_tape = 1, ) + tool_paths = list(/obj/item/hatchet) tool_behaviors = list(TOOL_SCREWDRIVER) time = 5 SECONDS category = CAT_WEAPON_RANGED @@ -229,10 +247,11 @@ /datum/reagent/consumable/grey_bull = 20, /obj/item/spear = 1, /obj/item/storage/toolbox = 1, + /obj/item/clothing/head/costume/crown = 1, // Any ol' crown will do ) tool_behaviors = list(TOOL_SCREWDRIVER) tool_paths = list(/obj/item/clothing/gloves/color/yellow, /obj/item/clothing/mask/gas, /obj/item/melee/baton/security/cattleprod) - time = 30 SECONDS //contemplate for a bit + time = 15 SECONDS //contemplate for a bit category = CAT_WEAPON_RANGED crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_MUST_BE_LEARNED diff --git a/code/datums/components/crafting/weapon_ammo.dm b/code/datums/components/crafting/weapon_ammo.dm index 55701dbde5349..32f34f2ae751c 100644 --- a/code/datums/components/crafting/weapon_ammo.dm +++ b/code/datums/components/crafting/weapon_ammo.dm @@ -71,8 +71,8 @@ category = CAT_WEAPON_AMMO /datum/crafting_recipe/improvisedslug - name = "Improvised Shotgun Shell" - result = /obj/item/ammo_casing/shotgun/improvised + name = "Junk Shell" + result = /obj/effect/spawner/random/junk_shell reqs = list( /obj/item/stack/sheet/iron = 2, /obj/item/stack/cable_coil = 1, diff --git a/code/datums/components/shell.dm b/code/datums/components/shell.dm index 5b6361b9ee673..20fbeb739276e 100644 --- a/code/datums/components/shell.dm +++ b/code/datums/components/shell.dm @@ -173,7 +173,7 @@ if(istype(item, /obj/item/inducer)) var/obj/item/inducer/inducer = item - INVOKE_ASYNC(inducer, TYPE_PROC_REF(/obj/item, attack_atom), attached_circuit, attacker, list()) + INVOKE_ASYNC(inducer, TYPE_PROC_REF(/obj/item, attack_atom), attached_circuit || parent, attacker, list()) return COMPONENT_NO_AFTERATTACK if(attached_circuit) diff --git a/code/datums/components/toggle_suit.dm b/code/datums/components/toggle_suit.dm index c4a378a16de27..aee7522745e77 100644 --- a/code/datums/components/toggle_suit.dm +++ b/code/datums/components/toggle_suit.dm @@ -15,6 +15,7 @@ return COMPONENT_INCOMPATIBLE var/atom/atom_parent = parent + atom_parent.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1 src.toggle_noun = toggle_noun src.base_icon_state = atom_parent.base_icon_state || atom_parent.icon_state @@ -22,9 +23,10 @@ /datum/component/toggle_icon/RegisterWithParent() RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_click_alt)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_adding_context)) /datum/component/toggle_icon/UnregisterFromParent() - UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_ATOM_EXAMINE)) + UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_ATOM_EXAMINE, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)) /* * Signal proc for COMSIG_CLICK_ALT. @@ -69,6 +71,21 @@ examine_list += span_notice("Alt-click on [source] to toggle the [toggle_noun].") +/* + * Signal proc for COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM. + * Adds usage context for toggling the parent open or closed. + * + * source - the atom context is requested from (parent) + * context - the list of usage contexts set + * held_item - the item held by the requesting mob + * user - the mob requesting context + */ +/datum/component/toggle_icon/proc/on_adding_context(atom/source, list/context, obj/item/held_item, mob/user) + SIGNAL_HANDLER + + context[SCREENTIP_CONTEXT_ALT_LMB] = "Toggle [toggle_noun]" + return CONTEXTUAL_SCREENTIP_SET + /* * Actually do the toggle of the icon. * Swaps the icon from [base_icon_state] to [base_icon_state]_t. diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 8f5844aa48093..1f978e5bf0abb 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -856,7 +856,7 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) dna.remove_all_mutations() dna.stability = 100 if(prob(max(70-instability,0))) - switch(rand(0,10)) //not complete and utter death + switch(rand(0,11)) //not complete and utter death if(0) monkeyize() if(1) @@ -893,6 +893,9 @@ GLOBAL_LIST_INIT(total_uf_len_by_block, populate_total_uf_len_by_block()) if(9 to 10) ForceContractDisease(new/datum/disease/gastrolosis()) to_chat(src, span_notice("Oh, I actually feel quite alright!")) + if(11) + to_chat(src, span_notice("Your DNA mutates into the ultimate biological form!")) + crabize() else switch(rand(0,6)) if(0) diff --git a/code/datums/elements/corrupted_organ.dm b/code/datums/elements/corrupted_organ.dm new file mode 100644 index 0000000000000..e7f8524b8517a --- /dev/null +++ b/code/datums/elements/corrupted_organ.dm @@ -0,0 +1,66 @@ +/// Component applying shared behaviour by cursed organs granted when sacrificed by a heretic +/// Mostly just does something spooky when it is removed +/datum/element/corrupted_organ + +/datum/element/corrupted_organ/Attach(datum/target) + . = ..() + if (!isinternalorgan(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ORGAN_SURGICALLY_REMOVED, PROC_REF(on_removed)) + + var/atom/atom_parent = target + atom_parent.color = COLOR_VOID_PURPLE + + atom_parent.add_filter(name = "ray", priority = 1, params = list( + type = "rays", + size = 12, + color = COLOR_VOID_PURPLE, + density = 12 + )) + var/ray_filter = atom_parent.get_filter("ray") + animate(ray_filter, offset = 100, time = 2 MINUTES, loop = -1, flags = ANIMATION_PARALLEL) // Absurdly long animate so nobody notices it hitching when it loops + animate(offset = 0, time = 2 MINUTES) // I sure hope duration of animate doesnt have any performance effect + +/datum/element/corrupted_organ/Detach(datum/source) + UnregisterSignal(source, list(COMSIG_ORGAN_SURGICALLY_REMOVED)) + return ..() + +/// When we're taken out of someone, do something spooky +/datum/element/corrupted_organ/proc/on_removed(atom/organ, mob/living/carbon/loser) + SIGNAL_HANDLER + if (loser.has_reagent(/datum/reagent/water/holywater) || loser.can_block_magic(MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) || prob(20)) + return + if (prob(75)) + organ.AddComponent(\ + /datum/component/haunted_item,\ + haunt_color = "#00000000", \ + aggro_radius = 4, \ + spawn_message = span_revenwarning("[organ] hovers ominously into the air, pulsating with unnatural vigour!"), \ + despawn_message = span_revenwarning("[organ] falls motionless to the ground."), \ + ) + return + var/turf/origin_turf = get_turf(organ) + playsound(organ, 'sound/magic/forcewall.ogg', vol = 100) + new /obj/effect/temp_visual/curse_blast(origin_turf) + organ.visible_message(span_revenwarning("[organ] explodes in a burst of dark energy!")) + for(var/mob/living/target in range(1, origin_turf)) + var/armor = target.run_armor_check(attack_flag = BOMB) + target.apply_damage(30, damagetype = BURN, blocked = armor, spread_damage = TRUE) + qdel(organ) + +/obj/effect/temp_visual/curse_blast + icon = 'icons/effects/64x64.dmi' + pixel_x = -16 + pixel_y = -16 + icon_state = "curse" + duration = 0.3 SECONDS + +/obj/effect/temp_visual/curse_blast/Initialize(mapload) + . = ..() + animate(src, transform = matrix() * 0.2, time = 0, flags = ANIMATION_PARALLEL) + animate(transform = matrix() * 2, time = duration, easing = EASE_IN) + + animate(src, alpha = 255, time = 0, flags = ANIMATION_PARALLEL) + animate(alpha = 255, time = 0.2 SECONDS) + animate(alpha = 0, time = 0.1 SECONDS) diff --git a/code/datums/elements/relay_attackers.dm b/code/datums/elements/relay_attackers.dm index a6c319d7bc9b8..5f5bf7ed5893f 100644 --- a/code/datums/elements/relay_attackers.dm +++ b/code/datums/elements/relay_attackers.dm @@ -7,14 +7,15 @@ /datum/element/relay_attackers/Attach(datum/target) . = ..() - // Boy this sure is a lot of ways to tell us that someone tried to attack us - RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby)) - RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic)) - RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc)) - RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act)) - RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby)) - RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) - RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech)) + if (!HAS_TRAIT(target, TRAIT_RELAYING_ATTACKER)) // Little bit gross but we want to just apply this shit from a bunch of places + // Boy this sure is a lot of ways to tell us that someone tried to attack us + RegisterSignal(target, COMSIG_ATOM_AFTER_ATTACKEDBY, PROC_REF(after_attackby)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_HAND, COMSIG_ATOM_ATTACK_PAW, COMSIG_MOB_ATTACK_ALIEN), PROC_REF(on_attack_generic)) + RegisterSignals(target, list(COMSIG_ATOM_ATTACK_BASIC_MOB, COMSIG_ATOM_ATTACK_ANIMAL), PROC_REF(on_attack_npc)) + RegisterSignal(target, COMSIG_PROJECTILE_PREHIT, PROC_REF(on_bullet_act)) + RegisterSignal(target, COMSIG_ATOM_PREHITBY, PROC_REF(on_hitby)) + RegisterSignal(target, COMSIG_ATOM_HULK_ATTACK, PROC_REF(on_attack_hulk)) + RegisterSignal(target, COMSIG_ATOM_ATTACK_MECH, PROC_REF(on_attack_mech)) ADD_TRAIT(target, TRAIT_RELAYING_ATTACKER, REF(src)) /datum/element/relay_attackers/Detach(datum/source, ...) diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 9b661688258f1..34c3038460e59 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -443,11 +443,12 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) * * mob/user - (optional) the user who is inserting the item. * * override - see item_insertion_feedback() * * force - bypass locked storage up to a certain level. See [code/__DEFINES/storage.dm] + * * messages - if TRUE, we will create balloon alerts for the user. */ -/datum/storage/proc/attempt_insert(obj/item/to_insert, mob/user, override = FALSE, force = STORAGE_NOT_LOCKED) +/datum/storage/proc/attempt_insert(obj/item/to_insert, mob/user, override = FALSE, force = STORAGE_NOT_LOCKED, messages = TRUE) SHOULD_NOT_SLEEP(TRUE) - if(!can_insert(to_insert, user, force = force)) + if(!can_insert(to_insert, user, messages = messages, force = force)) return FALSE SEND_SIGNAL(parent, COMSIG_STORAGE_STORED_ITEM, to_insert, user, force) diff --git a/code/datums/storage/subtypes/bag_of_holding.dm b/code/datums/storage/subtypes/bag_of_holding.dm index 5abc171e2cb59..8a812d7064d51 100644 --- a/code/datums/storage/subtypes/bag_of_holding.dm +++ b/code/datums/storage/subtypes/bag_of_holding.dm @@ -4,7 +4,7 @@ max_slots = 30 allow_big_nesting = TRUE -/datum/storage/bag_of_holding/attempt_insert(obj/item/to_insert, mob/user, override, force) +/datum/storage/bag_of_holding/attempt_insert(obj/item/to_insert, mob/user, override, force, messages) var/list/obj/item/storage/backpack/holding/matching = typecache_filter_list(to_insert.get_all_contents(), typecacheof(/obj/item/storage/backpack/holding)) matching -= parent matching -= real_location diff --git a/code/datums/storage/subtypes/pockets.dm b/code/datums/storage/subtypes/pockets.dm index 2b100d5d3232b..d441c6fdc5ff6 100644 --- a/code/datums/storage/subtypes/pockets.dm +++ b/code/datums/storage/subtypes/pockets.dm @@ -4,7 +4,7 @@ max_total_storage = 50 rustle_sound = FALSE -/datum/storage/pockets/attempt_insert(obj/item/to_insert, mob/user, override, force) +/datum/storage/pockets/attempt_insert(obj/item/to_insert, mob/user, override, force, messages) . = ..() if(!.) return diff --git a/code/datums/wires/_wires.dm b/code/datums/wires/_wires.dm index 4b7bfd2d08f5a..c3c8a1c0e93e1 100644 --- a/code/datums/wires/_wires.dm +++ b/code/datums/wires/_wires.dm @@ -41,6 +41,9 @@ /// If every instance of these wires should be random. Prevents wires from showing up in station blueprints. var/randomize = FALSE + /// Lazy assoc list of refs to mobs to refs to photos they have studied for wires + var/list/studied_photos + /datum/wires/New(atom/holder) ..() if(!istype(holder, holder_type)) @@ -261,10 +264,10 @@ if(!randomize) if(user.is_holding_item_of_type(/obj/item/blueprints)) return TRUE - for(var/obj/item/photo/photo in user.held_items) - if(!photo.picture || !photo.picture.has_blueprints) - continue - return TRUE + if(!isnull(user.mind)) + for(var/obj/item/photo/photo in user.held_items) + if(LAZYACCESS(studied_photos, REF(user.mind)) == REF(photo)) + return TRUE return FALSE @@ -279,6 +282,37 @@ /datum/wires/proc/always_reveal_wire(color) return FALSE +#define STUDY_INTERACTION_KEY "studying_photo" + +/** + * Attempts to study a photo for blueprints. + */ +/datum/wires/proc/try_study_photo(mob/user) + if(randomize) + return + if(isnull(user.mind)) + return + if(DOING_INTERACTION(user, STUDY_INTERACTION_KEY)) + return + if(LAZYACCESS(studied_photos, REF(user.mind))) + return + for(var/obj/item/photo/photo in user.held_items) + if(!photo.picture?.has_blueprints) + continue + + var/study_length = 1 SECONDS * floor(min(photo.picture.psize_x, photo.picture.psize_y) / 32) + if(study_length >= 4 SECONDS) + to_chat(user, span_notice("You squint [photo]... Hey, there's blueprints in the frame! Really wish the photo was zoomed in, though. \ + It's rather difficult to make out the wires.")) + else + to_chat(user, span_notice("You glance at [photo], looking for wires in the pictured blueprints.")) + + if(do_after(user, study_length, holder, interaction_key = STUDY_INTERACTION_KEY, hidden = TRUE)) + LAZYSET(studied_photos, REF(user.mind), REF(photo)) + return + +#undef STUDY_INTERACTION_KEY + /datum/wires/ui_host() return holder @@ -295,6 +329,7 @@ if (!ui) ui = new(user, src, "Wires", "[holder.name] Wires") ui.open() + try_study_photo(user) /datum/wires/ui_data(mob/user) var/list/data = list() diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index fda5485484ebb..a4d59d668ae11 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -207,7 +207,7 @@ Medical HUD! Basic mode needs suit sensors on. if(HAS_TRAIT(src, TRAIT_XENO_HOST)) holder.icon_state = "hudxeno" else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH))) - if(can_defib_client()) + if(HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || can_defib_client()) holder.icon_state = "huddefib" else holder.icon_state = "huddead" diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index 5baf549785330..d00c5824d8bd3 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -1,3 +1,6 @@ +#define REGIME_TELEPORTER "Teleporter" +#define REGIME_GATE "Gate" + /obj/machinery/computer/teleporter name = "teleporter control console" desc = "Used to control a linked teleportation Hub and Station." @@ -5,10 +8,13 @@ icon_keyboard = "teleport_key" light_color = LIGHT_COLOR_BLUE circuit = /obj/item/circuitboard/computer/teleporter - - var/regime_set = "Teleporter" + /// Currently can be "Teleporter" or "Gate" + var/regime_set = REGIME_TELEPORTER + /// The ID of this teleporter, used for linking to power stations var/id + /// The power station this teleporter is linked to var/obj/machinery/teleport/station/power_station + /// Whether the teleporter is currently calibrating var/calibrating ///Weakref to the target atom we're pointed at currently var/datum/weakref/target_ref @@ -63,7 +69,7 @@ data["power_station"] = power_station ? TRUE : FALSE data["teleporter_hub"] = power_station?.teleporter_hub ? TRUE : FALSE data["regime_set"] = regime_set - data["target"] = !target ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]" + data["target"] = !target ? "None" : "[get_area(target)] [(regime_set != REGIME_GATE) ? "" : REGIME_TELEPORTER]" data["calibrating"] = calibrating if(power_station?.teleporter_hub?.calibrated || power_station?.teleporter_hub?.accuracy >= 3) @@ -145,10 +151,10 @@ /obj/machinery/computer/teleporter/proc/reset_regime() set_teleport_target(null) - if(regime_set == "Teleporter") - regime_set = "Gate" + if(regime_set == REGIME_TELEPORTER) + regime_set = REGIME_GATE else - regime_set = "Teleporter" + regime_set = REGIME_TELEPORTER /// Gets a list of targets to teleport to. /// List is an assoc list of descriptors to locations. @@ -156,7 +162,7 @@ var/list/targets = list() var/list/area_index = list() - if (regime_set == "Teleporter") + if (regime_set == REGIME_TELEPORTER) for (var/obj/item/beacon/beacon as anything in GLOB.teleportbeacons) if (!is_eligible(beacon)) continue @@ -199,9 +205,9 @@ /obj/machinery/computer/teleporter/proc/set_target(mob/user) var/list/targets = get_targets() - if (regime_set == "Teleporter") + if (regime_set == REGIME_TELEPORTER) var/desc = tgui_input_list(usr, "Select a location to lock in", "Locking Computer", sort_list(targets)) - if(isnull(desc) || !user.can_perform_action(src)) + if(isnull(desc) || !user.can_perform_action(src, ALLOW_SILICON_REACH)) return set_teleport_target(targets[desc]) user.log_message("set the teleporter target to [targets[desc]].]", LOG_GAME) @@ -211,7 +217,7 @@ return var/desc = tgui_input_list(usr, "Select a station to lock in", "Locking Computer", sort_list(targets)) - if(isnull(desc)|| !user.can_perform_action(src)) + if(isnull(desc)|| !user.can_perform_action(src, ALLOW_SILICON_REACH)) return var/obj/machinery/teleport/station/target_station = targets[desc] if(!target_station || !target_station.teleporter_hub) @@ -232,6 +238,10 @@ return FALSE return TRUE + +#undef REGIME_TELEPORTER +#undef REGIME_GATE + /obj/item/circuit_component/teleporter_control_console display_name = "Teleporter Control Console" desc = "Used to control a linked teleportation Hub and Station." diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm index 7b5fcb501a046..7e5c58ef94c78 100644 --- a/code/game/machinery/dna_infuser/dna_infuser.dm +++ b/code/game/machinery/dna_infuser/dna_infuser.dm @@ -99,7 +99,7 @@ /obj/machinery/dna_infuser/proc/end_infuse(fail_explanation, fail_title) var/mob/living/carbon/human/human_occupant = occupant if(human_occupant.infuse_organ(infusing_into)) - check_tier_progression(src) + check_tier_progression(human_occupant) to_chat(occupant, span_danger("You feel yourself becoming more... [infusing_into.infusion_desc]?")) infusing = FALSE infusing_into = null @@ -123,7 +123,7 @@ && target.has_status_effect(infusing_into.status_effect_type) \ ) max_tier_allowed++ - playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE) + playsound(src, 'sound/machines/ding.ogg', 50, TRUE) visible_message(span_notice("[src] dings as it records the results of the full infusion.")) /obj/machinery/dna_infuser/update_icon_state() diff --git a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm index f44de87e92e8e..725d1b9ce8582 100644 --- a/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/carp_organs.dm @@ -113,7 +113,7 @@ /obj/item/organ/internal/brain/carp/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp) - AddElement(/datum/element/noticable_organ, "%PRONOUN_They seem%PRONOUN_S unable to stay still.") + AddElement(/datum/element/noticable_organ, "%PRONOUN_They seem%PRONOUN_s unable to stay still.") /obj/item/organ/internal/brain/carp/on_mob_insert(mob/living/carbon/brain_owner) . = ..() diff --git a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm index a36ebc1d3c3a9..3f1983f883648 100644 --- a/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/gondola_organs.dm @@ -31,7 +31,7 @@ Fluoride Stare: After someone says 5 words, blah blah blah... /obj/item/organ/internal/heart/gondola/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/gondola) - AddElement(/datum/element/noticable_organ, "%PRONOUN_They radiate%PRONOUN_S an aura of serenity.") + AddElement(/datum/element/noticable_organ, "%PRONOUN_They radiate%PRONOUN_s an aura of serenity.") /obj/item/organ/internal/heart/gondola/Insert(mob/living/carbon/receiver, special, movement_flags) . = ..() diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm index 56b147ffbee11..2da7a4fd982ec 100644 --- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm +++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm @@ -61,7 +61,7 @@ /obj/item/organ/internal/heart/rat/Initialize(mapload) . = ..() AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/rat) - AddElement(/datum/element/noticable_organ, "%PRONOUN_They hunch%PRONOUN_ES over unnaturally!") + AddElement(/datum/element/noticable_organ, "%PRONOUN_They hunch%PRONOUN_es over unnaturally!") /obj/item/organ/internal/heart/rat/on_mob_insert(mob/living/carbon/receiver) . = ..() diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 972497da9b316..3f94f9b0cbe20 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -73,22 +73,24 @@ com.target_ref = null visible_message(span_alert("Cannot authenticate locked on coordinates. Please reinstate coordinate matrix.")) return - if (ismovable(M)) - if(do_teleport(M, target, channel = TELEPORT_CHANNEL_BLUESPACE)) - use_energy(active_power_usage) - if(!calibrated && prob(30 - ((accuracy) * 10))) //oh dear a problem - if(ishuman(M))//don't remove people from the round randomly you jerks - var/mob/living/carbon/human/human = M - if(!(human.mob_biotypes & (MOB_ROBOTIC|MOB_MINERAL|MOB_UNDEAD|MOB_SPIRIT))) - var/datum/species/species_to_transform = /datum/species/fly - if(check_holidays(MOTH_WEEK)) - species_to_transform = /datum/species/moth - if(human.dna && human.dna.species.id != initial(species_to_transform.id)) - to_chat(M, span_hear("You hear a buzzing in your ears.")) - human.set_species(species_to_transform) - human.log_message("was turned into a [initial(species_to_transform.name)] through [src].", LOG_GAME) - calibrated = FALSE - return + if(!ismovable(M)) + return + var/turf/start_turf = get_turf(M) + if(!do_teleport(M, target, channel = TELEPORT_CHANNEL_BLUESPACE)) + return + use_energy(active_power_usage) + new /obj/effect/temp_visual/portal_animation(start_turf, src, M) + if(!calibrated && ishuman(M) && prob(30 - ((accuracy) * 10))) //oh dear a problem + var/mob/living/carbon/human/human = M + if(!(human.mob_biotypes & (MOB_ROBOTIC|MOB_MINERAL|MOB_UNDEAD|MOB_SPIRIT))) + var/datum/species/species_to_transform = /datum/species/fly + if(check_holidays(MOTH_WEEK)) + species_to_transform = /datum/species/moth + if(human.dna && human.dna.species.id != initial(species_to_transform.id)) + to_chat(M, span_hear("You hear a buzzing in your ears.")) + human.set_species(species_to_transform) + human.log_message("was turned into a [initial(species_to_transform.name)] through [src].", LOG_GAME) + calibrated = FALSE /obj/machinery/teleport/hub/update_icon_state() icon_state = "[base_icon_state][panel_open ? "-o" : (is_ready() ? 1 : 0)]" diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index 0af1e402ee1dd..d977605b2436f 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -219,6 +219,14 @@ . = ..() . += emissive_appearance(icon, icon_state, src, alpha = src.alpha) +/// Nebula vomit with extra guests +/obj/effect/decal/cleanable/vomit/nebula/worms + +/obj/effect/decal/cleanable/vomit/nebula/worms/Initialize(mapload, list/datum/disease/diseases) + . = ..() + for (var/i in 1 to rand(2, 3)) + new /mob/living/basic/hivelord_brood(loc) + /obj/effect/decal/cleanable/vomit/old name = "crusty dried vomit" desc = "You try not to look at the chunks, and fail." diff --git a/code/game/objects/effects/phased_mob.dm b/code/game/objects/effects/phased_mob.dm index 1456fa350bfab..dcd4e39189c87 100644 --- a/code/game/objects/effects/phased_mob.dm +++ b/code/game/objects/effects/phased_mob.dm @@ -2,7 +2,7 @@ name = "water" anchored = TRUE flags_1 = PREVENT_CONTENTS_EXPLOSION_1 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | SHUTTLE_CRUSH_PROOF invisibility = INVISIBILITY_OBSERVER movement_type = FLOATING /// The movable which's jaunting in this dummy diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index 8c52a76dbc0de..2b4c9a987b15a 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -140,10 +140,12 @@ no_effect = TRUE else last_effect = world.time + var/turf/start_turf = get_turf(M) if(do_teleport(M, real_target, innate_accuracy_penalty, no_effects = no_effect, channel = teleport_channel, forced = force_teleport)) if(isprojectile(M)) var/obj/projectile/P = M P.ignore_source_check = TRUE + new /obj/effect/temp_visual/portal_animation(start_turf, src, M) return TRUE return FALSE @@ -206,3 +208,22 @@ . = ..() if (. && !isdead(M)) qdel(src) + +/** + * Animation used for transitioning atoms which are teleporting somewhere via a portal + * + * To use, pass it the atom doing the teleporting and the atom that is being teleported in init. + */ +/obj/effect/temp_visual/portal_animation + duration = 0.25 SECONDS + +/obj/effect/temp_visual/portal_animation/Initialize(mapload, atom/portal, atom/movable/teleporting) + . = ..() + if(isnull(portal) || isnull(teleporting)) + return + + appearance = teleporting.appearance + dir = teleporting.dir + layer = portal.layer + 0.01 + alpha = teleporting.alpha + animate(src, pixel_x = (portal.x * 32) - (x * 32), pixel_y = (portal.y * 32) - (y * 32), alpha = 0, time = duration) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index febc0d82fcaae..497d1fa611dc6 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -264,9 +264,7 @@ if(LAZYLEN(embedding)) updateEmbedding() - if(unique_reskin) - RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) - register_context() + setup_reskinning() /obj/item/Destroy(force) diff --git a/code/game/objects/items/granters/crafting/pipegun.dm b/code/game/objects/items/granters/crafting/pipegun.dm index 8d331b2286d00..e54439350dab0 100644 --- a/code/game/objects/items/granters/crafting/pipegun.dm +++ b/code/game/objects/items/granters/crafting/pipegun.dm @@ -11,6 +11,7 @@ "Did he drop this into a moisture trap? Yuck.", "Toolboxing techniques, huh? I kinda just want to know how to make the gun.", "What the hell does he mean by 'ancient warrior tradition'?", + "...the true masters of this place are not those who merely inhabit it...", ) /obj/item/book/granter/crafting_recipe/dusting/laser_musket_prime diff --git a/code/game/objects/items/stacks/sheets/runed_metal.dm b/code/game/objects/items/stacks/sheets/runed_metal.dm index b60cd67d389dc..44094a0bf789b 100644 --- a/code/game/objects/items/stacks/sheets/runed_metal.dm +++ b/code/game/objects/items/stacks/sheets/runed_metal.dm @@ -6,8 +6,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/pylon, \ req_amount = 4, \ time = 4 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ desc = span_cult_bold("Pylon: Heals and regenerates the blood of nearby blood cultists and constructs, and also \ converts nearby floor tiles into engraved flooring, which allows blood cultists to scribe runes faster."), \ required_noun = "runed metal sheet", \ @@ -18,8 +17,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/altar, \ req_amount = 3, \ time = 4 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ desc = span_cult_bold("Altar: Can make Eldritch Whetstones, Construct Shells, and Flasks of Unholy Water."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ @@ -29,8 +27,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/archives, \ req_amount = 3, \ time = 4 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ desc = span_cult_bold("Archives: Can make Zealot's Blindfolds, Shuttle Curse Orbs, \ and Veil Walker equipment. Emits Light."), \ required_noun = "runed metal sheet", \ @@ -41,8 +38,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ result_type = /obj/structure/destructible/cult/item_dispenser/forge, \ req_amount = 3, \ time = 4 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ desc = span_cult_bold("Daemon Forge: Can make Nar'Sien Hardened Armor, Flagellant's Robes, \ and Eldritch Longswords. Emits Light."), \ required_noun = "runed metal sheet", \ @@ -52,8 +48,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ title = "runed door", \ result_type = /obj/machinery/door/airlock/cult, \ time = 5 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ desc = span_cult_bold("Runed Door: A weak door which stuns non-blood cultists who touch it."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ @@ -62,9 +57,9 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list( \ title = "runed girder", \ result_type = /obj/structure/girder/cult, \ time = 5 SECONDS, \ - one_per_turf = TRUE, \ - on_solid_ground = TRUE, \ - desc = span_cult_bold("Runed Girder: A weak girder that can be instantly destroyed by ritual daggers. Not a recommended usage of runed metal."), \ + crafting_flags = CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, \ + desc = span_cult_bold("Runed Girder: A weak girder that can be instantly destroyed by ritual daggers. \ + Not a recommended usage of runed metal."), \ required_noun = "runed metal sheet", \ category = CAT_CULT, \ ), \ diff --git a/code/game/objects/items/stacks/stack_recipe.dm b/code/game/objects/items/stacks/stack_recipe.dm index eabaa706d8879..a11feee8c685a 100644 --- a/code/game/objects/items/stacks/stack_recipe.dm +++ b/code/game/objects/items/stacks/stack_recipe.dm @@ -64,15 +64,13 @@ res_amount = 1, max_res_amount = 1, time = 0, - one_per_turf = FALSE, - on_solid_ground = FALSE, - window_checks = FALSE, + crafting_flags = CRAFT_CHECK_DENSITY, placement_checks = NONE, trait_booster, trait_modifier = 1, + category, desc, required_noun, - category, ) if(category) src.category = category diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index c9ee26ad44ac7..deb72b84c705d 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -286,6 +286,7 @@ /obj/item/surgicaldrill, /obj/item/tank/internals/emergency_oxygen, /obj/item/wrench/medical, + /obj/item/knife/ritual, )) /obj/item/storage/belt/medical/paramedic @@ -730,6 +731,7 @@ /obj/item/ammo_casing/strilka310, /obj/item/ammo_casing/shotgun, /obj/item/ammo_casing/a357, + /obj/item/ammo_casing/junk, )) /obj/item/storage/belt/fannypack diff --git a/code/game/objects/items_reskin.dm b/code/game/objects/items_reskin.dm index 1a7c27e098d32..9fa3b91d0e198 100644 --- a/code/game/objects/items_reskin.dm +++ b/code/game/objects/items_reskin.dm @@ -11,12 +11,41 @@ INVOKE_ASYNC(src, PROC_REF(reskin_obj), user) return CLICK_ACTION_SUCCESS +/** + * Checks if we should set up reskinning, + * by default if unique_reskin is set. + * + * Called on setup_reskinning(). + * Inheritors should override this to add their own checks. + */ +/obj/item/proc/check_setup_reskinning() + SHOULD_CALL_PARENT(TRUE) + if(unique_reskin) + return TRUE + + return FALSE + +/** + * Registers signals and context for reskinning, + * if check_setup_reskinning() passes. + * + * Called on Initialize(...). + * Inheritors should override this to add their own setup steps, + * or to avoid double calling register_context(). + */ +/obj/item/proc/setup_reskinning() + SHOULD_CALL_PARENT(FALSE) + if(!check_setup_reskinning()) + return + + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) + register_context() /** * Reskins object based on a user's choice * * Arguments: - * * M The mob choosing a reskin option + * * user The mob choosing a reskin option */ /obj/item/proc/reskin_obj(mob/user) if(!LAZYLEN(unique_reskin)) diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm index ac4b35678c9b1..7b03348288abb 100644 --- a/code/game/objects/structures/deployable_turret.dm +++ b/code/game/objects/structures/deployable_turret.dm @@ -51,19 +51,21 @@ /// Undeploying, for when you want to move your big dakka around /obj/machinery/deployable_turret/wrench_act(mob/living/user, obj/item/wrench/used_wrench) - . = ..() if(!can_be_undeployed) - return + return ITEM_INTERACT_SKIP_TO_ATTACK if(!ishuman(user)) - return + return ITEM_INTERACT_SKIP_TO_ATTACK used_wrench.play_tool_sound(user) user.balloon_alert(user, "undeploying...") if(!do_after(user, undeploy_time)) - return - var/obj/undeployed_object = new spawned_on_undeploy(src) + return ITEM_INTERACT_BLOCKING + var/obj/undeployed_object = new spawned_on_undeploy() //Keeps the health the same even if you redeploy the gun undeployed_object.modify_max_integrity(max_integrity) + if(!user.put_in_hands(undeployed_object)) + undeployed_object.forceMove(loc) qdel(src) + return ITEM_INTERACT_SUCCESS //BUCKLE HOOKS diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 4ac1a62a63f98..d52a4df28cbc2 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -45,6 +45,8 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an var/locked = FALSE ///Cooldown between breakout msesages. COOLDOWN_DECLARE(breakout_message_cooldown) + /// Cooldown between being able to slide the tray in or out. + COOLDOWN_DECLARE(open_close_cd) /obj/structure/bodycontainer/Initialize(mapload) . = ..() @@ -131,30 +133,90 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) /obj/structure/bodycontainer/proc/open() - playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + if(!COOLDOWN_FINISHED(src, open_close_cd)) + return FALSE + + COOLDOWN_START(src, open_close_cd, 0.25 SECONDS) + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) playsound(src, 'sound/effects/roll.ogg', 5, TRUE) - var/turf/T = get_step(src, dir) - if (connected) - connected.setDir(dir) - for(var/atom/movable/AM in src) - AM.forceMove(T) + var/turf/dump_turf = get_step(src, dir) + connected?.setDir(dir) + for(var/atom/movable/moving in src) + moving.forceMove(dump_turf) + animate_slide_out(moving) update_appearance() + return TRUE /obj/structure/bodycontainer/proc/close() + if(!COOLDOWN_FINISHED(src, open_close_cd)) + return FALSE + + COOLDOWN_START(src, open_close_cd, 0.5 SECONDS) playsound(src, 'sound/effects/roll.ogg', 5, TRUE) playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - for(var/atom/movable/AM in connected.loc) - if(!AM.anchored || AM == connected) - if(isliving(AM)) - var/mob/living/living_mob = AM - if(living_mob.incorporeal_move) - continue - else if(istype(AM, /obj/effect/dummy/phased_mob)) + var/turf/close_loc = connected.loc + for(var/atom/movable/entering in close_loc) + if(entering.anchored && entering != connected) + continue + if(isliving(entering)) + var/mob/living/living_mob = entering + if(living_mob.incorporeal_move) continue - else if(isdead(AM)) - continue - AM.forceMove(src) + else if(istype(entering, /obj/effect/dummy/phased_mob) || isdead(entering)) + continue + animate_slide_in(entering, close_loc) + entering.forceMove(src) update_appearance() + return TRUE + +#define SLIDE_LENGTH (0.3 SECONDS) + +/// Slides the passed object out of the morgue tray. +/obj/structure/bodycontainer/proc/animate_slide_out(atom/movable/animated) + var/old_layer = animated.layer + animated.layer = layer - (animated == connected ? 0.03 : 0.01) + animated.pixel_x = animated.base_pixel_x + (x * 32) - (animated.x * 32) + animated.pixel_y = animated.base_pixel_y + (y * 32) - (animated.y * 32) + animate( + animated, + pixel_x = animated.base_pixel_x, + pixel_y = animated.base_pixel_y, + time = SLIDE_LENGTH, + easing = CUBIC_EASING|EASE_OUT, + flags = ANIMATION_PARALLEL, + ) + addtimer(VARSET_CALLBACK(animated, layer, old_layer), SLIDE_LENGTH) + +/// Slides the passed object into the morgue tray from the passed turf. +/obj/structure/bodycontainer/proc/animate_slide_in(atom/movable/animated, turf/from_loc) + // It's easier to just make a visual for entering than to animate the object itself + var/obj/effect/temp_visual/morgue_content/visual = new(from_loc, animated) + visual.layer = layer - (animated == connected ? 0.03 : 0.01) + animate( + visual, + pixel_x = visual.base_pixel_x + (x * 32) - (visual.x * 32), + pixel_y = visual.base_pixel_y + (y * 32) - (visual.y * 32), + time = SLIDE_LENGTH, + easing = CUBIC_EASING|EASE_IN, + flags = ANIMATION_PARALLEL, + ) + +/// Used to mimic the appearance of an object sliding into a morgue tray. +/obj/effect/temp_visual/morgue_content + duration = SLIDE_LENGTH + +/obj/effect/temp_visual/morgue_content/Initialize(mapload, atom/movable/sliding_in) + . = ..() + if(isnull(sliding_in)) + return + + appearance = sliding_in.appearance + dir = sliding_in.dir + alpha = sliding_in.alpha + base_pixel_x = sliding_in.base_pixel_x + base_pixel_y = sliding_in.base_pixel_y + +#undef SLIDE_LENGTH #define MORGUE_EMPTY 1 #define MORGUE_NO_MOBS 2 @@ -526,6 +588,7 @@ GLOBAL_LIST_EMPTY(crematoriums) name = "crematorium tray" desc = "Apply body before burning." icon_state = "cremat" + layer = /obj/structure/bodycontainer/crematorium::layer - 0.03 /* * Morgue tray @@ -536,6 +599,7 @@ GLOBAL_LIST_EMPTY(crematoriums) icon = 'icons/obj/structures.dmi' icon_state = "morguet" pass_flags_self = PASSTABLE | LETPASSTHROW + layer = /obj/structure/bodycontainer/morgue::layer - 0.03 /obj/structure/tray/m_tray/CanAllowThrough(atom/movable/mover, border_dir) . = ..() diff --git a/code/game/sound.dm b/code/game/sound.dm index 5e1fa8242c0c2..bd4dba990d278 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -26,17 +26,18 @@ /** * playsound is a proc used to play a 3D sound in a specific range. This uses SOUND_RANGE + extra_range to determine that. * - * source - Origin of sound. - * soundin - Either a file, or a string that can be used to get an SFX. - * vol - The volume of the sound, excluding falloff and pressure affection. - * vary - bool that determines if the sound changes pitch every time it plays. - * extrarange - modifier for sound range. This gets added on top of SOUND_RANGE. - * falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. - * frequency - playback speed of audio. - * channel - The channel the sound is played at. - * pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space). - * ignore_walls - Whether or not the sound can pass through walls. - * falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. + * Arguments: + * * source - Origin of sound. + * * soundin - Either a file, or a string that can be used to get an SFX. + * * vol - The volume of the sound, excluding falloff and pressure affection. + * * vary - bool that determines if the sound changes pitch every time it plays. + * * extrarange - modifier for sound range. This gets added on top of SOUND_RANGE. + * * falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. + * * frequency - playback speed of audio. + * * channel - The channel the sound is played at. + * * pressure_affected - Whether or not difference in pressure affects the sound (E.g. if you can hear in space). + * * ignore_walls - Whether or not the sound can pass through walls. + * * falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. */ /proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff_exponent = SOUND_FALLOFF_EXPONENT, frequency = null, channel = 0, pressure_affected = TRUE, ignore_walls = TRUE, falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, use_reverb = TRUE) if(isarea(source)) @@ -82,6 +83,25 @@ listening_mob.playsound_local(turf_source, soundin, vol, vary, frequency, falloff_exponent, channel, pressure_affected, S, maxdistance, falloff_distance, 1, use_reverb) . += listening_mob +/** + * Plays a sound with a specific point of origin for src mob + * Affected by pressure, distance, terrain and environment (see arguments) + * + * Arguments: + * * turf_source - The turf our sound originates from, if this is not a turf, the sound is played with no spatial audio + * * soundin - Either a file, or a string that can be used to get an SFX. + * * vol - The volume of the sound, excluding falloff and pressure affection. + * * vary - bool that determines if the sound changes pitch every time it plays. + * * frequency - playback speed of audio. + * * falloff_exponent - Rate of falloff for the audio. Higher means quicker drop to low volume. Should generally be over 1 to indicate a quick dive to 0 rather than a slow dive. + * * channel - Optional: The channel the sound is played at. + * * pressure_affected - bool Whether or not difference in pressure affects the sound (E.g. if you can hear in space). + * * sound_to_use - Optional: Will default to soundin when absent + * * max_distance - number, determines the maximum distance of our sound + * * falloff_distance - Distance at which falloff begins. Sound is at peak volume (in regards to falloff) aslong as it is in this range. + * * distance_multiplier - Default 1, multiplies the maximum distance of our sound + * * use_reverb - bool default TRUE, determines if our sound has reverb + */ /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, wait = FALSE) // BANDASTATION EDIT: Add wait arg if(!client || !can_hear()) return @@ -181,253 +201,255 @@ /proc/get_rand_frequency() return rand(32000, 55000) //Frequency stuff only works with 45kbps oggs. +///Used to convert a SFX define into a .ogg so we can add some variance to sounds. If soundin is already a .ogg, we simply return it /proc/get_sfx(soundin) - if(istext(soundin)) - switch(soundin) - if(SFX_SHATTER) - soundin = pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg') - if(SFX_EXPLOSION) - soundin = pick('sound/effects/explosion1.ogg','sound/effects/explosion2.ogg') - if(SFX_EXPLOSION_CREAKING) - soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg') - if(SFX_HULL_CREAKING) - soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg') - if(SFX_SPARKS) - soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks4.ogg') - if(SFX_RUSTLE) - soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') - if(SFX_BODYFALL) - soundin = pick('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') - if(SFX_PUNCH) - soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') - if(SFX_CLOWN_STEP) - soundin = pick('sound/effects/footstep/clownstep1.ogg','sound/effects/footstep/clownstep2.ogg') - if(SFX_SUIT_STEP) - soundin = pick('sound/effects/suitstep1.ogg','sound/effects/suitstep2.ogg') - if(SFX_SWING_HIT) - soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') - if(SFX_HISS) - soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') - if(SFX_PAGE_TURN) - soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') - if(SFX_RICOCHET) - soundin = pick( 'sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg','sound/weapons/effects/ric3.ogg','sound/weapons/effects/ric4.ogg','sound/weapons/effects/ric5.ogg') - if(SFX_TERMINAL_TYPE) - soundin = pick(list( - 'sound/machines/terminal_button01.ogg', - 'sound/machines/terminal_button02.ogg', - 'sound/machines/terminal_button03.ogg', - 'sound/machines/terminal_button04.ogg', - 'sound/machines/terminal_button05.ogg', - 'sound/machines/terminal_button06.ogg', - 'sound/machines/terminal_button07.ogg', - 'sound/machines/terminal_button08.ogg', - )) - if(SFX_DESECRATION) - soundin = pick('sound/misc/desecration-01.ogg', 'sound/misc/desecration-02.ogg', 'sound/misc/desecration-03.ogg') - if(SFX_IM_HERE) - soundin = pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg') - if(SFX_CAN_OPEN) - soundin = pick('sound/effects/can_open1.ogg', 'sound/effects/can_open2.ogg', 'sound/effects/can_open3.ogg') - if(SFX_BULLET_MISS) - soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') - if(SFX_REVOLVER_SPIN) - soundin = pick('sound/weapons/gun/revolver/spin1.ogg', 'sound/weapons/gun/revolver/spin2.ogg', 'sound/weapons/gun/revolver/spin3.ogg') - if(SFX_LAW) - soundin = pick(list( - 'sound/voice/beepsky/creep.ogg', - 'sound/voice/beepsky/god.ogg', - 'sound/voice/beepsky/iamthelaw.ogg', - 'sound/voice/beepsky/insult.ogg', - 'sound/voice/beepsky/radio.ogg', - 'sound/voice/beepsky/secureday.ogg', - )) - if(SFX_HONKBOT_E) - soundin = pick(list( - 'sound/effects/pray.ogg', - 'sound/effects/reee.ogg', - 'sound/items/AirHorn.ogg', - 'sound/items/AirHorn2.ogg', - 'sound/items/bikehorn.ogg', - 'sound/items/WEEOO1.ogg', - 'sound/machines/buzz-sigh.ogg', - 'sound/machines/ping.ogg', - 'sound/magic/Fireball.ogg', - 'sound/misc/sadtrombone.ogg', - 'sound/voice/beepsky/creep.ogg', - 'sound/voice/beepsky/iamthelaw.ogg', - 'sound/voice/hiss1.ogg', - 'sound/weapons/bladeslice.ogg', - 'sound/weapons/flashbang.ogg', - )) - if(SFX_GOOSE) - soundin = pick('sound/creatures/goose1.ogg', 'sound/creatures/goose2.ogg', 'sound/creatures/goose3.ogg', 'sound/creatures/goose4.ogg') - if(SFX_WARPSPEED) - soundin = 'sound/runtime/hyperspace/hyperspace_begin.ogg' - if(SFX_SM_CALM) - soundin = pick(list( - 'sound/machines/sm/accent/normal/1.ogg', - 'sound/machines/sm/accent/normal/2.ogg', - 'sound/machines/sm/accent/normal/3.ogg', - 'sound/machines/sm/accent/normal/4.ogg', - 'sound/machines/sm/accent/normal/5.ogg', - 'sound/machines/sm/accent/normal/6.ogg', - 'sound/machines/sm/accent/normal/7.ogg', - 'sound/machines/sm/accent/normal/8.ogg', - 'sound/machines/sm/accent/normal/9.ogg', - 'sound/machines/sm/accent/normal/10.ogg', - 'sound/machines/sm/accent/normal/11.ogg', - 'sound/machines/sm/accent/normal/12.ogg', - 'sound/machines/sm/accent/normal/13.ogg', - 'sound/machines/sm/accent/normal/14.ogg', - 'sound/machines/sm/accent/normal/15.ogg', - 'sound/machines/sm/accent/normal/16.ogg', - 'sound/machines/sm/accent/normal/17.ogg', - 'sound/machines/sm/accent/normal/18.ogg', - 'sound/machines/sm/accent/normal/19.ogg', - 'sound/machines/sm/accent/normal/20.ogg', - 'sound/machines/sm/accent/normal/21.ogg', - 'sound/machines/sm/accent/normal/22.ogg', - 'sound/machines/sm/accent/normal/23.ogg', - 'sound/machines/sm/accent/normal/24.ogg', - 'sound/machines/sm/accent/normal/25.ogg', - 'sound/machines/sm/accent/normal/26.ogg', - 'sound/machines/sm/accent/normal/27.ogg', - 'sound/machines/sm/accent/normal/28.ogg', - 'sound/machines/sm/accent/normal/29.ogg', - 'sound/machines/sm/accent/normal/30.ogg', - 'sound/machines/sm/accent/normal/31.ogg', - 'sound/machines/sm/accent/normal/32.ogg', - 'sound/machines/sm/accent/normal/33.ogg', - )) - if(SFX_SM_DELAM) - soundin = pick(list( - 'sound/machines/sm/accent/delam/1.ogg', - 'sound/machines/sm/accent/delam/2.ogg', - 'sound/machines/sm/accent/delam/3.ogg', - 'sound/machines/sm/accent/delam/4.ogg', - 'sound/machines/sm/accent/delam/5.ogg', - 'sound/machines/sm/accent/delam/6.ogg', - 'sound/machines/sm/accent/delam/7.ogg', - 'sound/machines/sm/accent/delam/8.ogg', - 'sound/machines/sm/accent/delam/9.ogg', - 'sound/machines/sm/accent/delam/10.ogg', - 'sound/machines/sm/accent/delam/11.ogg', - 'sound/machines/sm/accent/delam/12.ogg', - 'sound/machines/sm/accent/delam/13.ogg', - 'sound/machines/sm/accent/delam/14.ogg', - 'sound/machines/sm/accent/delam/15.ogg', - 'sound/machines/sm/accent/delam/16.ogg', - 'sound/machines/sm/accent/delam/17.ogg', - 'sound/machines/sm/accent/delam/18.ogg', - 'sound/machines/sm/accent/delam/19.ogg', - 'sound/machines/sm/accent/delam/20.ogg', - 'sound/machines/sm/accent/delam/21.ogg', - 'sound/machines/sm/accent/delam/22.ogg', - 'sound/machines/sm/accent/delam/23.ogg', - 'sound/machines/sm/accent/delam/24.ogg', - 'sound/machines/sm/accent/delam/25.ogg', - 'sound/machines/sm/accent/delam/26.ogg', - 'sound/machines/sm/accent/delam/27.ogg', - 'sound/machines/sm/accent/delam/28.ogg', - 'sound/machines/sm/accent/delam/29.ogg', - 'sound/machines/sm/accent/delam/30.ogg', - 'sound/machines/sm/accent/delam/31.ogg', - 'sound/machines/sm/accent/delam/32.ogg', - 'sound/machines/sm/accent/delam/33.ogg', - )) - if(SFX_HYPERTORUS_CALM) - soundin = pick(list( - 'sound/machines/sm/accent/normal/1.ogg', - 'sound/machines/sm/accent/normal/2.ogg', - 'sound/machines/sm/accent/normal/3.ogg', - 'sound/machines/sm/accent/normal/4.ogg', - 'sound/machines/sm/accent/normal/5.ogg', - 'sound/machines/sm/accent/normal/6.ogg', - 'sound/machines/sm/accent/normal/7.ogg', - 'sound/machines/sm/accent/normal/8.ogg', - 'sound/machines/sm/accent/normal/9.ogg', - 'sound/machines/sm/accent/normal/10.ogg', - 'sound/machines/sm/accent/normal/11.ogg', - 'sound/machines/sm/accent/normal/12.ogg', - 'sound/machines/sm/accent/normal/13.ogg', - 'sound/machines/sm/accent/normal/14.ogg', - 'sound/machines/sm/accent/normal/15.ogg', - 'sound/machines/sm/accent/normal/16.ogg', - 'sound/machines/sm/accent/normal/17.ogg', - 'sound/machines/sm/accent/normal/18.ogg', - 'sound/machines/sm/accent/normal/19.ogg', - 'sound/machines/sm/accent/normal/20.ogg', - 'sound/machines/sm/accent/normal/21.ogg', - 'sound/machines/sm/accent/normal/22.ogg', - 'sound/machines/sm/accent/normal/23.ogg', - 'sound/machines/sm/accent/normal/24.ogg', - 'sound/machines/sm/accent/normal/25.ogg', - 'sound/machines/sm/accent/normal/26.ogg', - 'sound/machines/sm/accent/normal/27.ogg', - 'sound/machines/sm/accent/normal/28.ogg', - 'sound/machines/sm/accent/normal/29.ogg', - 'sound/machines/sm/accent/normal/30.ogg', - 'sound/machines/sm/accent/normal/31.ogg', - 'sound/machines/sm/accent/normal/32.ogg', - 'sound/machines/sm/accent/normal/33.ogg', - )) - if(SFX_HYPERTORUS_MELTING) - soundin = pick(list( - 'sound/machines/sm/accent/delam/1.ogg', - 'sound/machines/sm/accent/delam/2.ogg', - 'sound/machines/sm/accent/delam/3.ogg', - 'sound/machines/sm/accent/delam/4.ogg', - 'sound/machines/sm/accent/delam/5.ogg', - 'sound/machines/sm/accent/delam/6.ogg', - 'sound/machines/sm/accent/delam/7.ogg', - 'sound/machines/sm/accent/delam/8.ogg', - 'sound/machines/sm/accent/delam/9.ogg', - 'sound/machines/sm/accent/delam/10.ogg', - 'sound/machines/sm/accent/delam/11.ogg', - 'sound/machines/sm/accent/delam/12.ogg', - 'sound/machines/sm/accent/delam/13.ogg', - 'sound/machines/sm/accent/delam/14.ogg', - 'sound/machines/sm/accent/delam/15.ogg', - 'sound/machines/sm/accent/delam/16.ogg', - 'sound/machines/sm/accent/delam/17.ogg', - 'sound/machines/sm/accent/delam/18.ogg', - 'sound/machines/sm/accent/delam/19.ogg', - 'sound/machines/sm/accent/delam/20.ogg', - 'sound/machines/sm/accent/delam/21.ogg', - 'sound/machines/sm/accent/delam/22.ogg', - 'sound/machines/sm/accent/delam/23.ogg', - 'sound/machines/sm/accent/delam/24.ogg', - 'sound/machines/sm/accent/delam/25.ogg', - 'sound/machines/sm/accent/delam/26.ogg', - 'sound/machines/sm/accent/delam/27.ogg', - 'sound/machines/sm/accent/delam/28.ogg', - 'sound/machines/sm/accent/delam/29.ogg', - 'sound/machines/sm/accent/delam/30.ogg', - 'sound/machines/sm/accent/delam/31.ogg', - 'sound/machines/sm/accent/delam/32.ogg', - 'sound/machines/sm/accent/delam/33.ogg', - )) - if(SFX_CRUNCHY_BUSH_WHACK) - soundin = pick('sound/effects/crunchybushwhack1.ogg', 'sound/effects/crunchybushwhack2.ogg', 'sound/effects/crunchybushwhack3.ogg') - if(SFX_TREE_CHOP) - soundin = pick('sound/effects/treechop1.ogg', 'sound/effects/treechop2.ogg', 'sound/effects/treechop3.ogg') - if(SFX_ROCK_TAP) - soundin = pick('sound/effects/rocktap1.ogg', 'sound/effects/rocktap2.ogg', 'sound/effects/rocktap3.ogg') - if(SFX_SEAR) - soundin = 'sound/weapons/sear.ogg' - if(SFX_REEL) - soundin = pick( - 'sound/items/reel1.ogg', - 'sound/items/reel2.ogg', - 'sound/items/reel3.ogg', - 'sound/items/reel4.ogg', - 'sound/items/reel5.ogg', - ) - if(SFX_RATTLE) - soundin = pick( - 'sound/items/rattle1.ogg', - 'sound/items/rattle2.ogg', - 'sound/items/rattle3.ogg', - ) + if(!istext(soundin)) + return soundin + switch(soundin) + if(SFX_SHATTER) + soundin = pick('sound/effects/glassbr1.ogg','sound/effects/glassbr2.ogg','sound/effects/glassbr3.ogg') + if(SFX_EXPLOSION) + soundin = pick('sound/effects/explosion1.ogg','sound/effects/explosion2.ogg') + if(SFX_EXPLOSION_CREAKING) + soundin = pick('sound/effects/explosioncreak1.ogg', 'sound/effects/explosioncreak2.ogg') + if(SFX_HULL_CREAKING) + soundin = pick('sound/effects/creak1.ogg', 'sound/effects/creak2.ogg', 'sound/effects/creak3.ogg') + if(SFX_SPARKS) + soundin = pick('sound/effects/sparks1.ogg','sound/effects/sparks2.ogg','sound/effects/sparks3.ogg','sound/effects/sparks4.ogg') + if(SFX_RUSTLE) + soundin = pick('sound/effects/rustle1.ogg','sound/effects/rustle2.ogg','sound/effects/rustle3.ogg','sound/effects/rustle4.ogg','sound/effects/rustle5.ogg') + if(SFX_BODYFALL) + soundin = pick('sound/effects/bodyfall1.ogg','sound/effects/bodyfall2.ogg','sound/effects/bodyfall3.ogg','sound/effects/bodyfall4.ogg') + if(SFX_PUNCH) + soundin = pick('sound/weapons/punch1.ogg','sound/weapons/punch2.ogg','sound/weapons/punch3.ogg','sound/weapons/punch4.ogg') + if(SFX_CLOWN_STEP) + soundin = pick('sound/effects/footstep/clownstep1.ogg','sound/effects/footstep/clownstep2.ogg') + if(SFX_SUIT_STEP) + soundin = pick('sound/effects/suitstep1.ogg','sound/effects/suitstep2.ogg') + if(SFX_SWING_HIT) + soundin = pick('sound/weapons/genhit1.ogg', 'sound/weapons/genhit2.ogg', 'sound/weapons/genhit3.ogg') + if(SFX_HISS) + soundin = pick('sound/voice/hiss1.ogg','sound/voice/hiss2.ogg','sound/voice/hiss3.ogg','sound/voice/hiss4.ogg') + if(SFX_PAGE_TURN) + soundin = pick('sound/effects/pageturn1.ogg', 'sound/effects/pageturn2.ogg','sound/effects/pageturn3.ogg') + if(SFX_RICOCHET) + soundin = pick( 'sound/weapons/effects/ric1.ogg', 'sound/weapons/effects/ric2.ogg','sound/weapons/effects/ric3.ogg','sound/weapons/effects/ric4.ogg','sound/weapons/effects/ric5.ogg') + if(SFX_TERMINAL_TYPE) + soundin = pick(list( + 'sound/machines/terminal_button01.ogg', + 'sound/machines/terminal_button02.ogg', + 'sound/machines/terminal_button03.ogg', + 'sound/machines/terminal_button04.ogg', + 'sound/machines/terminal_button05.ogg', + 'sound/machines/terminal_button06.ogg', + 'sound/machines/terminal_button07.ogg', + 'sound/machines/terminal_button08.ogg', + )) + if(SFX_DESECRATION) + soundin = pick('sound/misc/desecration-01.ogg', 'sound/misc/desecration-02.ogg', 'sound/misc/desecration-03.ogg') + if(SFX_IM_HERE) + soundin = pick('sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg') + if(SFX_CAN_OPEN) + soundin = pick('sound/effects/can_open1.ogg', 'sound/effects/can_open2.ogg', 'sound/effects/can_open3.ogg') + if(SFX_BULLET_MISS) + soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') + if(SFX_REVOLVER_SPIN) + soundin = pick('sound/weapons/gun/revolver/spin1.ogg', 'sound/weapons/gun/revolver/spin2.ogg', 'sound/weapons/gun/revolver/spin3.ogg') + if(SFX_LAW) + soundin = pick(list( + 'sound/voice/beepsky/creep.ogg', + 'sound/voice/beepsky/god.ogg', + 'sound/voice/beepsky/iamthelaw.ogg', + 'sound/voice/beepsky/insult.ogg', + 'sound/voice/beepsky/radio.ogg', + 'sound/voice/beepsky/secureday.ogg', + )) + if(SFX_HONKBOT_E) + soundin = pick(list( + 'sound/effects/pray.ogg', + 'sound/effects/reee.ogg', + 'sound/items/AirHorn.ogg', + 'sound/items/AirHorn2.ogg', + 'sound/items/bikehorn.ogg', + 'sound/items/WEEOO1.ogg', + 'sound/machines/buzz-sigh.ogg', + 'sound/machines/ping.ogg', + 'sound/magic/Fireball.ogg', + 'sound/misc/sadtrombone.ogg', + 'sound/voice/beepsky/creep.ogg', + 'sound/voice/beepsky/iamthelaw.ogg', + 'sound/voice/hiss1.ogg', + 'sound/weapons/bladeslice.ogg', + 'sound/weapons/flashbang.ogg', + )) + if(SFX_GOOSE) + soundin = pick('sound/creatures/goose1.ogg', 'sound/creatures/goose2.ogg', 'sound/creatures/goose3.ogg', 'sound/creatures/goose4.ogg') + if(SFX_WARPSPEED) + soundin = 'sound/runtime/hyperspace/hyperspace_begin.ogg' + if(SFX_SM_CALM) + soundin = pick(list( + 'sound/machines/sm/accent/normal/1.ogg', + 'sound/machines/sm/accent/normal/2.ogg', + 'sound/machines/sm/accent/normal/3.ogg', + 'sound/machines/sm/accent/normal/4.ogg', + 'sound/machines/sm/accent/normal/5.ogg', + 'sound/machines/sm/accent/normal/6.ogg', + 'sound/machines/sm/accent/normal/7.ogg', + 'sound/machines/sm/accent/normal/8.ogg', + 'sound/machines/sm/accent/normal/9.ogg', + 'sound/machines/sm/accent/normal/10.ogg', + 'sound/machines/sm/accent/normal/11.ogg', + 'sound/machines/sm/accent/normal/12.ogg', + 'sound/machines/sm/accent/normal/13.ogg', + 'sound/machines/sm/accent/normal/14.ogg', + 'sound/machines/sm/accent/normal/15.ogg', + 'sound/machines/sm/accent/normal/16.ogg', + 'sound/machines/sm/accent/normal/17.ogg', + 'sound/machines/sm/accent/normal/18.ogg', + 'sound/machines/sm/accent/normal/19.ogg', + 'sound/machines/sm/accent/normal/20.ogg', + 'sound/machines/sm/accent/normal/21.ogg', + 'sound/machines/sm/accent/normal/22.ogg', + 'sound/machines/sm/accent/normal/23.ogg', + 'sound/machines/sm/accent/normal/24.ogg', + 'sound/machines/sm/accent/normal/25.ogg', + 'sound/machines/sm/accent/normal/26.ogg', + 'sound/machines/sm/accent/normal/27.ogg', + 'sound/machines/sm/accent/normal/28.ogg', + 'sound/machines/sm/accent/normal/29.ogg', + 'sound/machines/sm/accent/normal/30.ogg', + 'sound/machines/sm/accent/normal/31.ogg', + 'sound/machines/sm/accent/normal/32.ogg', + 'sound/machines/sm/accent/normal/33.ogg', + )) + if(SFX_SM_DELAM) + soundin = pick(list( + 'sound/machines/sm/accent/delam/1.ogg', + 'sound/machines/sm/accent/delam/2.ogg', + 'sound/machines/sm/accent/delam/3.ogg', + 'sound/machines/sm/accent/delam/4.ogg', + 'sound/machines/sm/accent/delam/5.ogg', + 'sound/machines/sm/accent/delam/6.ogg', + 'sound/machines/sm/accent/delam/7.ogg', + 'sound/machines/sm/accent/delam/8.ogg', + 'sound/machines/sm/accent/delam/9.ogg', + 'sound/machines/sm/accent/delam/10.ogg', + 'sound/machines/sm/accent/delam/11.ogg', + 'sound/machines/sm/accent/delam/12.ogg', + 'sound/machines/sm/accent/delam/13.ogg', + 'sound/machines/sm/accent/delam/14.ogg', + 'sound/machines/sm/accent/delam/15.ogg', + 'sound/machines/sm/accent/delam/16.ogg', + 'sound/machines/sm/accent/delam/17.ogg', + 'sound/machines/sm/accent/delam/18.ogg', + 'sound/machines/sm/accent/delam/19.ogg', + 'sound/machines/sm/accent/delam/20.ogg', + 'sound/machines/sm/accent/delam/21.ogg', + 'sound/machines/sm/accent/delam/22.ogg', + 'sound/machines/sm/accent/delam/23.ogg', + 'sound/machines/sm/accent/delam/24.ogg', + 'sound/machines/sm/accent/delam/25.ogg', + 'sound/machines/sm/accent/delam/26.ogg', + 'sound/machines/sm/accent/delam/27.ogg', + 'sound/machines/sm/accent/delam/28.ogg', + 'sound/machines/sm/accent/delam/29.ogg', + 'sound/machines/sm/accent/delam/30.ogg', + 'sound/machines/sm/accent/delam/31.ogg', + 'sound/machines/sm/accent/delam/32.ogg', + 'sound/machines/sm/accent/delam/33.ogg', + )) + if(SFX_HYPERTORUS_CALM) + soundin = pick(list( + 'sound/machines/sm/accent/normal/1.ogg', + 'sound/machines/sm/accent/normal/2.ogg', + 'sound/machines/sm/accent/normal/3.ogg', + 'sound/machines/sm/accent/normal/4.ogg', + 'sound/machines/sm/accent/normal/5.ogg', + 'sound/machines/sm/accent/normal/6.ogg', + 'sound/machines/sm/accent/normal/7.ogg', + 'sound/machines/sm/accent/normal/8.ogg', + 'sound/machines/sm/accent/normal/9.ogg', + 'sound/machines/sm/accent/normal/10.ogg', + 'sound/machines/sm/accent/normal/11.ogg', + 'sound/machines/sm/accent/normal/12.ogg', + 'sound/machines/sm/accent/normal/13.ogg', + 'sound/machines/sm/accent/normal/14.ogg', + 'sound/machines/sm/accent/normal/15.ogg', + 'sound/machines/sm/accent/normal/16.ogg', + 'sound/machines/sm/accent/normal/17.ogg', + 'sound/machines/sm/accent/normal/18.ogg', + 'sound/machines/sm/accent/normal/19.ogg', + 'sound/machines/sm/accent/normal/20.ogg', + 'sound/machines/sm/accent/normal/21.ogg', + 'sound/machines/sm/accent/normal/22.ogg', + 'sound/machines/sm/accent/normal/23.ogg', + 'sound/machines/sm/accent/normal/24.ogg', + 'sound/machines/sm/accent/normal/25.ogg', + 'sound/machines/sm/accent/normal/26.ogg', + 'sound/machines/sm/accent/normal/27.ogg', + 'sound/machines/sm/accent/normal/28.ogg', + 'sound/machines/sm/accent/normal/29.ogg', + 'sound/machines/sm/accent/normal/30.ogg', + 'sound/machines/sm/accent/normal/31.ogg', + 'sound/machines/sm/accent/normal/32.ogg', + 'sound/machines/sm/accent/normal/33.ogg', + )) + if(SFX_HYPERTORUS_MELTING) + soundin = pick(list( + 'sound/machines/sm/accent/delam/1.ogg', + 'sound/machines/sm/accent/delam/2.ogg', + 'sound/machines/sm/accent/delam/3.ogg', + 'sound/machines/sm/accent/delam/4.ogg', + 'sound/machines/sm/accent/delam/5.ogg', + 'sound/machines/sm/accent/delam/6.ogg', + 'sound/machines/sm/accent/delam/7.ogg', + 'sound/machines/sm/accent/delam/8.ogg', + 'sound/machines/sm/accent/delam/9.ogg', + 'sound/machines/sm/accent/delam/10.ogg', + 'sound/machines/sm/accent/delam/11.ogg', + 'sound/machines/sm/accent/delam/12.ogg', + 'sound/machines/sm/accent/delam/13.ogg', + 'sound/machines/sm/accent/delam/14.ogg', + 'sound/machines/sm/accent/delam/15.ogg', + 'sound/machines/sm/accent/delam/16.ogg', + 'sound/machines/sm/accent/delam/17.ogg', + 'sound/machines/sm/accent/delam/18.ogg', + 'sound/machines/sm/accent/delam/19.ogg', + 'sound/machines/sm/accent/delam/20.ogg', + 'sound/machines/sm/accent/delam/21.ogg', + 'sound/machines/sm/accent/delam/22.ogg', + 'sound/machines/sm/accent/delam/23.ogg', + 'sound/machines/sm/accent/delam/24.ogg', + 'sound/machines/sm/accent/delam/25.ogg', + 'sound/machines/sm/accent/delam/26.ogg', + 'sound/machines/sm/accent/delam/27.ogg', + 'sound/machines/sm/accent/delam/28.ogg', + 'sound/machines/sm/accent/delam/29.ogg', + 'sound/machines/sm/accent/delam/30.ogg', + 'sound/machines/sm/accent/delam/31.ogg', + 'sound/machines/sm/accent/delam/32.ogg', + 'sound/machines/sm/accent/delam/33.ogg', + )) + if(SFX_CRUNCHY_BUSH_WHACK) + soundin = pick('sound/effects/crunchybushwhack1.ogg', 'sound/effects/crunchybushwhack2.ogg', 'sound/effects/crunchybushwhack3.ogg') + if(SFX_TREE_CHOP) + soundin = pick('sound/effects/treechop1.ogg', 'sound/effects/treechop2.ogg', 'sound/effects/treechop3.ogg') + if(SFX_ROCK_TAP) + soundin = pick('sound/effects/rocktap1.ogg', 'sound/effects/rocktap2.ogg', 'sound/effects/rocktap3.ogg') + if(SFX_SEAR) + soundin = 'sound/weapons/sear.ogg' + if(SFX_REEL) + soundin = pick( + 'sound/items/reel1.ogg', + 'sound/items/reel2.ogg', + 'sound/items/reel3.ogg', + 'sound/items/reel4.ogg', + 'sound/items/reel5.ogg', + ) + if(SFX_RATTLE) + soundin = pick( + 'sound/items/rattle1.ogg', + 'sound/items/rattle2.ogg', + 'sound/items/rattle3.ogg', + ) return soundin diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm index c64dd309d5264..862faeb071ab6 100644 --- a/code/game/turfs/open/_open.dm +++ b/code/game/turfs/open/_open.dm @@ -41,15 +41,33 @@ /turf/open/update_overlays() if(isnull(damaged_dmi)) return ..() + . = ..() + if(broken) - . += mutable_appearance(damaged_dmi, pick(broken_states())) + var/mutable_appearance/broken_appearance = mutable_appearance(damaged_dmi, pick(broken_states())) + + if(smoothing_flags && !(smoothing_flags & SMOOTH_BROKEN_TURF)) + var/matrix/translation = new + translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET) + broken_appearance.transform = translation + + . += broken_appearance + else if(burnt) var/list/burnt_states = burnt_states() + var/mutable_appearance/burnt_appearance if(burnt_states.len) - . += mutable_appearance(damaged_dmi, pick(burnt_states)) + burnt_appearance = mutable_appearance(damaged_dmi, pick(burnt_states)) else - . += mutable_appearance(damaged_dmi, pick(broken_states())) + burnt_appearance = mutable_appearance(damaged_dmi, pick(broken_states())) + + if(smoothing_flags && !(smoothing_flags & SMOOTH_BURNT_TURF)) + var/matrix/translation = new + translation.Translate(-LARGE_TURF_SMOOTHING_X_OFFSET, -LARGE_TURF_SMOOTHING_Y_OFFSET) + burnt_appearance.transform = translation + + . += burnt_appearance //direction is direction of travel of A /turf/open/zPassIn(direction) diff --git a/code/game/turfs/open/grass.dm b/code/game/turfs/open/grass.dm index d4bfc051e3b2d..676ba11da1f59 100644 --- a/code/game/turfs/open/grass.dm +++ b/code/game/turfs/open/grass.dm @@ -10,29 +10,52 @@ barefootstep = FOOTSTEP_GRASS clawfootstep = FOOTSTEP_GRASS heavyfootstep = FOOTSTEP_GENERIC_HEAVY - smoothing_flags = SMOOTH_BITMASK + smoothing_flags = SMOOTH_BITMASK | SMOOTH_BROKEN_TURF | SMOOTH_BURNT_TURF smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_GRASS canSmoothWith = SMOOTH_GROUP_FLOOR_GRASS + SMOOTH_GROUP_CLOSED_TURFS layer = HIGH_TURF_LAYER - damaged_dmi = 'icons/turf/damaged.dmi' + damaged_dmi = 'icons/turf/floors/grass_damaged.dmi' + /// The icon used for smoothing. var/smooth_icon = 'icons/turf/floors/grass.dmi' + /// The base icon_state for the broken state. + var/base_broken_icon_state = "grass_damaged" + /// The base icon_state for the burnt state. + var/base_burnt_icon_state = "grass_damaged" /turf/open/misc/grass/broken_states() - return list("grass_damaged") + if (!smoothing_junction || !(smoothing_flags & SMOOTH_BROKEN_TURF)) + return list("[base_broken_icon_state]-255") + + return list("[base_broken_icon_state]-[smoothing_junction]") /turf/open/misc/grass/burnt_states() - return list("grass_damaged") + if (!smoothing_junction || !(smoothing_flags & SMOOTH_BURNT_TURF)) + return list("[base_burnt_icon_state]-255") + + return list("[base_burnt_icon_state]-[smoothing_junction]") /turf/open/misc/grass/Initialize(mapload) . = ..() if(smoothing_flags) var/matrix/translation = new - translation.Translate(-9, -9) + translation.Translate(LARGE_TURF_SMOOTHING_X_OFFSET, LARGE_TURF_SMOOTHING_Y_OFFSET) transform = translation icon = smooth_icon if(is_station_level(z)) GLOB.station_turfs += src + +/turf/open/misc/grass/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) + . = ..() + if (!.) + return + + if(!smoothing_flags) + return + + underlay_appearance.transform = transform + + /turf/open/misc/grass/lavaland initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/open/planet.dm b/code/game/turfs/open/planet.dm index db391025e84b1..03ca128129037 100644 --- a/code/game/turfs/open/planet.dm +++ b/code/game/turfs/open/planet.dm @@ -66,20 +66,14 @@ /turf/open/misc/grass/jungle name = "jungle grass" + desc = "Greener on the other side." initial_gas_mix = OPENTURF_DEFAULT_ATMOS planetary_atmos = TRUE baseturfs = /turf/open/misc/dirt - desc = "Greener on the other side." icon_state = "junglegrass" base_icon_state = "junglegrass" smooth_icon = 'icons/turf/floors/junglegrass.dmi' -/turf/open/misc/grass/broken_states() - return list("jungle_damaged") - -/turf/open/misc/grass/burnt_states() - return list("jungle_damaged") - /turf/open/misc/grass/jungle/lavaland initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index 1be2bc0435f17..5dcfa85961d20 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -20,10 +20,15 @@ */ var/immerse_overlay_color = "#5AAA88" + /// Fishing element for this specific water tile + var/datum/fish_source/fishing_datum = /datum/fish_source/portal + /turf/open/water/Initialize(mapload) . = ..() AddElement(/datum/element/immerse, icon, icon_state, "immerse", immerse_overlay_color) AddElement(/datum/element/watery_tile) + if(!isnull(fishing_datum)) + AddElement(/datum/element/lazy_fishing_spot, fishing_datum) /turf/open/water/jungle @@ -36,7 +41,4 @@ base_icon_state = "water" baseturfs = /turf/open/water/beach immerse_overlay_color = "#7799AA" - -/turf/open/water/beach/Initialize(mapload) - . = ..() - AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/ocean/beach) + fishing_datum = /datum/fish_source/ocean/beach diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index 5f5b5f245bb5c..379b9475dc50b 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -617,7 +617,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w E.announce_chance = 0 E.processing = TRUE if(holder) - log_admin("[key_name(holder)] used secret [action]") + log_admin("[key_name(holder)] used secret: [action].") #undef THUNDERDOME_TEMPLATE_FILE #undef HIGHLANDER_DELAY_TEXT diff --git a/code/modules/antagonists/heretic/items/corrupted_organs.dm b/code/modules/antagonists/heretic/items/corrupted_organs.dm new file mode 100644 index 0000000000000..311e84c805599 --- /dev/null +++ b/code/modules/antagonists/heretic/items/corrupted_organs.dm @@ -0,0 +1,242 @@ +/// Renders you unable to see people who were heretics at the time that this organ is gained +/obj/item/organ/internal/eyes/corrupt + name = "corrupt orbs" + desc = "These eyes have seen something they shouldn't have." + /// The override images we are applying + var/list/hallucinations + +/obj/item/organ/internal/eyes/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "%PRONOUN_Their eyes have wide dilated pupils, and no iris. Something is moving in the darkness.", BODY_ZONE_PRECISE_EYES) + +/obj/item/organ/internal/eyes/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + if (!organ_owner.client) + return + + var/list/human_mobs = GLOB.human_list.Copy() + human_mobs -= organ_owner + for (var/mob/living/carbon/human/check_human as anything in human_mobs) + if (!IS_HERETIC(check_human) && !prob(5)) // Throw in some false positives + continue + var/image/invisible_man = image('icons/blanks/32x32.dmi', check_human, "nothing") + invisible_man.override = TRUE + LAZYADD(hallucinations, invisible_man) + + if (LAZYLEN(hallucinations)) + organ_owner.client.images |= hallucinations + +/obj/item/organ/internal/eyes/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + if (!LAZYLEN(hallucinations)) + return + organ_owner.client?.images -= hallucinations + QDEL_NULL(hallucinations) + + +/// Sometimes speak in incomprehensible tongues +/obj/item/organ/internal/tongue/corrupt + name = "corrupt tongue" + desc = "This one tells only lies." + +/obj/item/organ/internal/tongue/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "The inside of %PRONOUN_Their mouth is full of stars.", BODY_ZONE_PRECISE_MOUTH) + +/obj/item/organ/internal/tongue/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + RegisterSignal(organ_owner, COMSIG_MOB_SAY, PROC_REF(on_spoken)) + +/obj/item/organ/internal/tongue/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_MOB_SAY) + +/// When the mob speaks, sometimes put it in a different language +/obj/item/organ/internal/tongue/corrupt/proc/on_spoken(mob/living/organ_owner, list/speech_args) + SIGNAL_HANDLER + if (organ_owner.has_reagent(/datum/reagent/water/holywater) || prob(60)) + return + speech_args[SPEECH_LANGUAGE] = /datum/language/shadowtongue + + +/// Randomly secretes alcohol or hallucinogens when you're drinking something +/obj/item/organ/internal/liver/corrupt + name = "corrupt liver" + desc = "After what you've seen you could really go for a drink." + /// How much extra ingredients to add? + var/amount_added = 5 + /// What extra ingredients can we add? + var/list/extra_ingredients = list( + /datum/reagent/consumable/ethanol/pina_olivada, + /datum/reagent/consumable/ethanol/rum, + /datum/reagent/consumable/ethanol/thirteenloko, + /datum/reagent/consumable/ethanol/vodka, + /datum/reagent/consumable/superlaughter, + /datum/reagent/drug/bath_salts, + /datum/reagent/drug/blastoff, + /datum/reagent/drug/happiness, + /datum/reagent/drug/mushroomhallucinogen, + ) + +/obj/item/organ/internal/liver/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/liver/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special) + . = ..() + RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) + +/obj/item/organ/internal/liver/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) + +/// If we drank something, add a little extra +/obj/item/organ/internal/liver/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods) + SIGNAL_HANDLER + if (!(methods & INGEST)) + return + var/datum/reagents/extra_reagents = new() + extra_reagents.add_reagent(pick(extra_ingredients), amount_added) + extra_reagents.trans_to(source, amount_added, transferred_by = src, methods = INJECT) + if (prob(20)) + to_chat(source, span_warning("As you take a sip, you feel something bubbling in your stomach...")) + + +/// Rapidly become hungry if you are not digesting blood +/obj/item/organ/internal/stomach/corrupt + name = "corrupt stomach" + desc = "This parasite demands an unwholesome diet in order to be satisfied." + /// Do we have an unholy thirst? + var/thirst_satiated = FALSE + /// Timer for when we get thirsty again + var/thirst_timer + /// How long until we prompt the player to drink blood again? + COOLDOWN_DECLARE(message_cooldown) + +/obj/item/organ/internal/stomach/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "%PRONOUN_They %PRONOUN_have an unhealthy pallor.") + +/obj/item/organ/internal/stomach/corrupt/on_mob_insert(mob/living/carbon/organ_owner, special) + . = ..() + RegisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS, PROC_REF(on_drank)) + +/obj/item/organ/internal/stomach/corrupt/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, COMSIG_ATOM_EXPOSE_REAGENTS) + +/// Check if we drank a little blood +/obj/item/organ/internal/stomach/corrupt/proc/on_drank(atom/source, list/reagents, datum/reagents/source_reagents, methods) + SIGNAL_HANDLER + if (!(methods & INGEST)) + return + + var/contains_blood = locate(/datum/reagent/blood) in reagents + if (!contains_blood) + return + + if (!thirst_satiated) + to_chat(source, span_cult_italic("The thirst is satisfied... for now.")) + thirst_satiated = TRUE + deltimer(thirst_timer) + thirst_timer = addtimer(VARSET_CALLBACK(src, thirst_satiated, FALSE), 3 MINUTES, TIMER_STOPPABLE | TIMER_DELETE_ME) + +/obj/item/organ/internal/stomach/corrupt/handle_hunger(mob/living/carbon/human/human, seconds_per_tick, times_fired) + if (thirst_satiated || human.has_reagent(/datum/reagent/water/holywater)) + return ..() + + human.adjust_nutrition(-1 * seconds_per_tick) + + if (!COOLDOWN_FINISHED(src, message_cooldown)) + return ..() + COOLDOWN_START(src, message_cooldown, 30 SECONDS) + + var/static/list/blood_messages = list( + "Blood...", + "Everyone suddenly looks so tasty.", + "The blood...", + "There's an emptiness in you that only blood can fill.", + "You could really go for some blood right now.", + "You feel the blood rushing through your veins.", + "You think about biting someone's throat.", + "Your stomach growls and you feel a metallic taste in your mouth.", + ) + to_chat(human, span_cult_italic(pick(blood_messages))) + + return ..() + + +/// Occasionally bombards you with spooky hands and lets everyone hear your pulse. +/obj/item/organ/internal/heart/corrupt + name = "corrupt heart" + desc = "What corruption is this spreading along with the blood?" + /// How long until the next heart? + COOLDOWN_DECLARE(hand_cooldown) + +/obj/item/organ/internal/heart/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/heart/corrupt/on_life(seconds_per_tick, times_fired) + . = ..() + if (!COOLDOWN_FINISHED(src, hand_cooldown) || IS_IN_MANSUS(owner) || !owner.needs_heart() || !is_beating() || owner.has_reagent(/datum/reagent/water/holywater)) + return + fire_curse_hand(owner) + COOLDOWN_START(src, hand_cooldown, rand(6 SECONDS, 45 SECONDS)) // Wide variance to put you off guard + + +/// Sometimes cough out some kind of dangerous gas +/obj/item/organ/internal/lungs/corrupt + name = "corrupt lungs" + desc = "Some things SHOULD be drowned in tar." + /// How likely are we not to cough every time we take a breath? + var/cough_chance = 15 + /// How much gas to emit? + var/gas_amount = 30 + /// What can we cough up? + var/list/gas_types = list( + /datum/gas/bz = 30, + /datum/gas/miasma = 50, + /datum/gas/plasma = 20, + ) + +/obj/item/organ/internal/lungs/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + +/obj/item/organ/internal/lungs/corrupt/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather) + . = ..() + if (!. || IS_IN_MANSUS(owner) || breather.has_reagent(/datum/reagent/water/holywater) || !prob(cough_chance)) + return + breather.emote("cough"); + var/chosen_gas = pick_weight(gas_types) + var/datum/gas_mixture/mix_to_spawn = new() + mix_to_spawn.add_gas(pick(chosen_gas)) + mix_to_spawn.gases[chosen_gas][MOLES] = gas_amount + mix_to_spawn.temperature = breather.bodytemperature + log_atmos("[owner] coughed some gas into the air due to their corrupted lungs.", mix_to_spawn) + var/turf/open/our_turf = get_turf(breather) + our_turf.assume_air(mix_to_spawn) + + +/// It's full of worms +/obj/item/organ/internal/appendix/corrupt + name = "corrupt appendix" + desc = "What kind of dark, cosmic force is even going to bother to corrupt an appendix?" + /// How likely are we to spawn worms? + var/worm_chance = 2 + +/obj/item/organ/internal/appendix/corrupt/Initialize(mapload) + . = ..() + AddElement(/datum/element/corrupted_organ) + AddElement(/datum/element/noticable_organ, "%PRONOUN_Their abdomen is distended... and wiggling.", BODY_ZONE_PRECISE_GROIN) + +/obj/item/organ/internal/appendix/corrupt/on_life(seconds_per_tick, times_fired) + . = ..() + if (owner.stat != CONSCIOUS || owner.has_reagent(/datum/reagent/water/holywater) || IS_IN_MANSUS(owner) || !SPT_PROB(worm_chance, seconds_per_tick)) + return + owner.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_HARM, vomit_type = /obj/effect/decal/cleanable/vomit/nebula/worms, distance = 0) + owner.Knockdown(0.5 SECONDS) diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm index 3749e37fe6af1..30757e88a4b29 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm @@ -107,3 +107,17 @@ return bloodiest_wound.adjust_blood_flow(-0.5 * seconds_between_ticks) + +/// Torment the target with a frightening hand +/proc/fire_curse_hand(mob/living/carbon/victim) + var/grab_dir = turn(victim.dir, pick(-90, 90, 180, 180)) // Not in front, favour behind + var/turf/spawn_turf = get_ranged_target_turf(victim, grab_dir, 8) + if (isnull(spawn_turf)) + return + new /obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, victim.dir) + playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1) + var/obj/projectile/curse_hand/hel/hand = new (spawn_turf) + hand.preparePixelProjectile(victim, spawn_turf) + if (QDELETED(hand)) // safety check if above fails - above has a stack trace if it does fail + return + hand.fire() diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm new file mode 100644 index 0000000000000..87086de6e0024 --- /dev/null +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_curse.dm @@ -0,0 +1,93 @@ +/// A curse given to people to disencourage them from retaliating against someone who sacrificed them +/datum/status_effect/heretic_curse + id = "heretic_curse" + alert_type = null + status_type = STATUS_EFFECT_MULTIPLE // In case several different people sacrifice you, unlucky + /// Who cursed us? + var/mob/living/the_curser + /// Don't experience bad things too often + COOLDOWN_DECLARE(consequence_cooldown) + +/datum/status_effect/heretic_curse/on_creation(mob/living/new_owner, mob/living/the_curser) + src.the_curser = the_curser + return ..() + +/datum/status_effect/heretic_curse/Destroy() + the_curser = null + return ..() + +/datum/status_effect/heretic_curse/on_apply() + if (isnull(the_curser) || !iscarbon(owner)) + return FALSE + + the_curser.AddElement(/datum/element/relay_attackers) + RegisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_curser_attacked)) + RegisterSignal(the_curser, COMSIG_QDELETING, PROC_REF(on_curser_destroyed)) + + owner.AddElement(/datum/element/relay_attackers) + RegisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_owner_attacked)) + + return TRUE + +/datum/status_effect/heretic_curse/on_remove() + UnregisterSignal(owner, COMSIG_ATOM_WAS_ATTACKED) + UnregisterSignal(the_curser, COMSIG_ATOM_WAS_ATTACKED) + the_curser = null + + +/// If the heretic that cursed us is destroyed this thing is useless now +/datum/status_effect/heretic_curse/proc/on_curser_destroyed() + SIGNAL_HANDLER + qdel(src) + +/// If we attack the guy who cursed us, that's no good +/datum/status_effect/heretic_curse/proc/on_curser_attacked(datum/source, mob/attacker) + SIGNAL_HANDLER + if (attacker != owner || !HAS_TRAIT(source, TRAIT_ALLOW_HERETIC_CASTING)) + return + log_combat(owner, the_curser, "attacked", addition = "and lost some organs because they had previously been sacrificed by them.") + experience_the_consequences() + +/// If we are attacked by the guy who cursed us, that's also no good +/datum/status_effect/heretic_curse/proc/on_owner_attacked(datum/source, mob/attacker) + SIGNAL_HANDLER + if (attacker != the_curser || !HAS_TRAIT(attacker, TRAIT_ALLOW_HERETIC_CASTING)) + return + log_combat(the_curser, owner, "attacked", addition = "and as they had previously sacrificed them, removed some of their organs.") + experience_the_consequences() + +/// Experience something you may not enjoy which may also significantly shorten your lifespan +/datum/status_effect/heretic_curse/proc/experience_the_consequences() + if (!COOLDOWN_FINISHED(src, consequence_cooldown) || owner.stat != CONSCIOUS) + return + + var/mob/living/carbon/carbon_owner = owner + var/obj/item/bodypart/chest/organ_storage = owner.get_bodypart(BODY_ZONE_CHEST) + if (isnull(organ_storage)) + carbon_owner.gib() // IDK how you don't have a chest but you're not getting away that easily + return + + var/list/removable_organs = list() + for(var/obj/item/organ/internal/bodypart_organ in organ_storage.contents) + if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE) + continue + removable_organs += bodypart_organ + + if (!length(removable_organs)) + return // This one is a little more possible but they're probably already in pretty bad shape by this point + + var/obj/item/organ/internal/removing_organ = pick(removable_organs) + + if (carbon_owner.vomit(vomit_flags = VOMIT_CATEGORY_BLOOD)) + carbon_owner.visible_message(span_boldwarning("[carbon_owner] vomits out [carbon_owner.p_their()] [removing_organ]")) + else + carbon_owner.visible_message(span_boldwarning("[carbon_owner]'s [removing_organ] rips itself out of `[carbon_owner.p_their()] chest!")) + + removing_organ.Remove(carbon_owner) + + var/turf/land_turf = get_step(carbon_owner, carbon_owner.dir) + if (land_turf.is_blocked_turf(exclude_mobs = TRUE)) + land_turf = carbon_owner.drop_location() + + removing_organ.forceMove(land_turf) + COOLDOWN_START(src, consequence_cooldown, 10 SECONDS) diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm index 797d754ea0b0b..1d2be465b3026 100644 --- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm +++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm @@ -26,6 +26,16 @@ var/list/datum/mind/target_blacklist /// An assoc list of [ref] to [timers] - a list of all the timers of people in the shadow realm currently var/list/return_timers + /// Evil organs we can put in people + var/static/list/grantable_organs = list( + /obj/item/organ/internal/appendix/corrupt, + /obj/item/organ/internal/eyes/corrupt, + /obj/item/organ/internal/heart/corrupt, + /obj/item/organ/internal/liver/corrupt, + /obj/item/organ/internal/lungs/corrupt, + /obj/item/organ/internal/stomach/corrupt, + /obj/item/organ/internal/tongue/corrupt, + ) /datum/heretic_knowledge/hunt_and_sacrifice/Destroy(force) heretic_mind = null @@ -197,6 +207,8 @@ heretic_datum.total_sacrifices++ heretic_datum.knowledge_points += 2 + sacrifice.apply_status_effect(/datum/status_effect/heretic_curse, user) + if(!begin_sacrifice(sacrifice)) disembowel_target(sacrifice) @@ -278,6 +290,8 @@ disembowel_target(sac_target) return + curse_organs(sac_target) + // Send 'em to the destination. If the teleport fails, just disembowel them and stop the chain if(!destination || !do_teleport(sac_target, destination, asoundin = 'sound/magic/repulse.ogg', asoundout = 'sound/magic/blind.ogg', no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE)) disembowel_target(sac_target) @@ -299,6 +313,31 @@ RegisterSignal(sac_target, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_target_escape)) // Cheese condition RegisterSignal(sac_target, COMSIG_LIVING_DEATH, PROC_REF(on_target_death)) // Loss condition +/// Apply a sinister curse to some of the target's organs as an incentive to leave us alone +/datum/heretic_knowledge/hunt_and_sacrifice/proc/curse_organs(mob/living/carbon/human/sac_target) + var/usable_organs = grantable_organs.Copy() + if (isplasmaman(sac_target)) + usable_organs -= /obj/item/organ/internal/lungs/corrupt // Their lungs are already more cursed than anything I could give them + + var/total_implant = rand(2, 4) + var/gave_any = FALSE + + for (var/i in 1 to total_implant) + if (!length(usable_organs)) + break + var/organ_path = pick_n_take(usable_organs) + var/obj/item/organ/internal/to_give = new organ_path + if (!to_give.Insert(sac_target)) + qdel(to_give) + else + gave_any = TRUE + + if (!gave_any) + return + + new /obj/effect/gibspawner/human/bodypartless(get_turf(sac_target)) + sac_target.visible_message(span_boldwarning("Several organs force themselves out of [sac_target]!")) + /** * This proc is called from [proc/after_target_sleeps] when the [sac_target] should be waking up.) * @@ -375,8 +414,7 @@ if(IS_HERETIC(sac_target)) var/datum/antagonist/heretic/victim_heretic = sac_target.mind?.has_antag_datum(/datum/antagonist/heretic) victim_heretic.knowledge_points -= 3 - else - sac_target.gain_trauma(/datum/brain_trauma/mild/phobia/heresy, TRAUMA_RESILIENCE_MAGIC) + // Wherever we end up, we sure as hell won't be able to explain sac_target.adjust_timed_status_effect(40 SECONDS, /datum/status_effect/speech/slurring/heretic) sac_target.adjust_stutter(40 SECONDS) diff --git a/code/modules/antagonists/spy/spy.dm b/code/modules/antagonists/spy/spy.dm index 89809958b29b6..8bcc113f08939 100644 --- a/code/modules/antagonists/spy/spy.dm +++ b/code/modules/antagonists/spy/spy.dm @@ -130,30 +130,34 @@ your_mission.owner = owner your_mission.explanation_text = pick_list_replacements(SPY_OBJECTIVE_FILE, "objective_body") objectives += your_mission - + if((length(objectives) < 3) && prob(25)) switch(rand(1, 4)) if(1) var/datum/objective/protect/save_the_person = new() save_the_person.owner = owner + save_the_person.find_target() save_the_person.no_failure = TRUE objectives += save_the_person if(2) var/datum/objective/protect/nonhuman/save_the_entity = new() save_the_entity.owner = owner + save_the_entity.find_target() save_the_entity.no_failure = TRUE objectives += save_the_entity if(3) var/datum/objective/jailbreak/save_the_jailbird = new() save_the_jailbird.owner = owner + save_the_jailbird.find_target() save_the_jailbird.no_failure = TRUE objectives += save_the_jailbird if(4) var/datum/objective/jailbreak/detain/cage_the_jailbird = new() cage_the_jailbird.owner = owner + cage_the_jailbird.find_target() cage_the_jailbird.no_failure = TRUE objectives += cage_the_jailbird - + if(prob(10)) var/datum/objective/martyr/leave_no_trace = new() leave_no_trace.owner = owner diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm index 88e091310ca0b..5de66271fe29c 100644 --- a/code/modules/antagonists/spy/spy_uplink.dm +++ b/code/modules/antagonists/spy/spy_uplink.dm @@ -56,7 +56,7 @@ /datum/component/spy_uplink/proc/on_attack_self(obj/item/source, mob/user) SIGNAL_HANDLER - if(is_our_spy(user)) + if(IS_SPY(user)) INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, ui_interact), user) return NONE @@ -65,7 +65,7 @@ if(!ismovable(target)) return NONE - if(!is_our_spy(user)) + if(!IS_SPY(user)) return NONE if(!try_steal(target, user)) return NONE diff --git a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm index 59ea9b20afa7a..9182f23a649db 100644 --- a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm +++ b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm @@ -44,6 +44,7 @@ /datum/job/paramedic, /datum/job/psychologist, /datum/job/chemist, + /datum/job/coroner, // Service /datum/job/clown, /datum/job/botanist, @@ -75,6 +76,7 @@ telecrystal_reward = list(1, 2) target_jobs = list( // Cargo + /datum/job/bitrunner, /datum/job/shaft_miner, // Service /datum/job/chaplain, @@ -99,6 +101,7 @@ /datum/job/chief_medical_officer, /datum/job/research_director, /datum/job/quartermaster, + /datum/job/chief_engineer, ) /datum/traitor_objective/destroy_heirloom/captain diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm index 85aad594516d9..ea7fef9b4b607 100644 --- a/code/modules/antagonists/traitor/objectives/kidnapping.dm +++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm @@ -48,6 +48,7 @@ /datum/job/chemist, /datum/job/doctor, /datum/job/psychologist, + /datum/job/coroner, // Science /datum/job/geneticist, /datum/job/roboticist, @@ -79,6 +80,7 @@ target_jobs = list( // Cargo + /datum/job/bitrunner, /datum/job/shaft_miner, // Medical /datum/job/paramedic, diff --git a/code/modules/bitrunning/antagonists/ghost_role.dm b/code/modules/bitrunning/antagonists/ghost_role.dm new file mode 100644 index 0000000000000..3bf88e16dfb21 --- /dev/null +++ b/code/modules/bitrunning/antagonists/ghost_role.dm @@ -0,0 +1,22 @@ +/datum/antagonist/domain_ghost_actor + name = "Virtual Domain Actor" + antagpanel_category = ANTAG_GROUP_GLITCH + job_rank = ROLE_GLITCH + show_to_ghosts = TRUE + suicide_cry = "FATAL ERROR" + ui_name = "AntagInfoGlitch" + +/datum/antagonist/domain_ghost_actor/on_gain() + . = ..() + owner.current.AddComponent(/datum/component/npc_friendly) //Just in case + forge_objectives() + +/datum/antagonist/domain_ghost_actor/forge_objectives() + var/datum/objective/bitrunner_ghost_fluff/objective = new() + objective.owner = owner + objectives += objective + +/datum/objective/bitrunner_ghost_fluff + +/datum/objective/bitrunner_ghost_fluff/New() + explanation_text = "Defend your domain from the intruders!" diff --git a/code/modules/bitrunning/areas.dm b/code/modules/bitrunning/areas.dm index 8ac07bcc8f2de..31a2fef9f3ca1 100644 --- a/code/modules/bitrunning/areas.dm +++ b/code/modules/bitrunning/areas.dm @@ -26,7 +26,7 @@ /area/virtual_domain/safehouse name = "Virtual Domain Safehouse" - area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED + area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED | VIRTUAL_SAFE_AREA icon_state = "bit_safe" requires_power = FALSE sound_environment = SOUND_ENVIRONMENT_ROOM @@ -49,3 +49,13 @@ icon_state = "bit_space" area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED | HIDDEN_AREA +///Areas that virtual entities should not be in + +/area/virtual_domain/protected_space + name = "Virtual Domain Safe Zone" + area_flags = UNIQUE_AREA | NOTELEPORT | ABDUCTOR_PROOF | EVENT_PROTECTED | VIRTUAL_SAFE_AREA + icon_state = "bit_safe" + +/area/virtual_domain/protected_space/fullbright + static_lighting = FALSE + base_lighting_alpha = 255 diff --git a/code/modules/bitrunning/components/virtual_entity.dm b/code/modules/bitrunning/components/virtual_entity.dm new file mode 100644 index 0000000000000..12e5305ba9adc --- /dev/null +++ b/code/modules/bitrunning/components/virtual_entity.dm @@ -0,0 +1,36 @@ +/// Handles all special considerations for "virtual entities" such as bitrunning ghost roles or digital anomaly antagonists. +/datum/component/virtual_entity + ///The cooldown for balloon alerts, so the player isn't spammed while trying to enter a restricted area. + COOLDOWN_DECLARE(OOB_cooldown) + +/datum/component/virtual_entity/Initialize(obj/machinery/quantum_server) + . = ..() + + if(quantum_server.obj_flags & EMAGGED) + jailbreak_mobs() //This just sends a message and self-deletes, a bit messy but it works. + return + + RegisterSignal(parent, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_parent_pre_move)) + RegisterSignal(quantum_server, COMSIG_ATOM_EMAG_ACT, PROC_REF(jailbreak_mobs)) + +///Prevents entry to a certain area if it has flags preventing virtual entities from entering. +/datum/component/virtual_entity/proc/on_parent_pre_move(atom/movable/source, atom/new_location) + SIGNAL_HANDLER + + var/area/location_area = get_area(new_location) + if(!location_area) + stack_trace("Virtual entity entered a location with no area!") + return + + if(location_area.area_flags & VIRTUAL_SAFE_AREA) + source.balloon_alert(source, "out of bounds!") + COOLDOWN_START(src, OOB_cooldown, 2 SECONDS) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE + +///Self-destructs the component, allowing free-roam by all entities with this restriction. +/datum/component/virtual_entity/proc/jailbreak_mobs() + SIGNAL_HANDLER + + to_chat(parent, span_big("You shiver for a moment, then suddenly feel a sense of clarity you haven't felt before. \ + You can go anywhere, do anything! You could leave this simulation right now if you wanted!")) + qdel(src) diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index 06b49d790a5d8..113ed6b212625 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -88,11 +88,14 @@ /obj/machinery/quantum_server/emag_act(mob/user, obj/item/card/emag/emag_card) . = ..() + if(obj_flags & EMAGGED) + return + obj_flags |= EMAGGED glitch_chance = 0.09 add_overlay(mutable_appearance('icons/obj/machines/bitrunning.dmi', "emag_overlay")) - balloon_alert(user, "bzzzt...") + balloon_alert(user, "system jailbroken...") playsound(src, 'sound/effects/sparks1.ogg', 35, vary = TRUE) /obj/machinery/quantum_server/update_appearance(updates) diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm index 6c42322d0cf01..58c6cbdd33cc3 100644 --- a/code/modules/bitrunning/server/threats.dm +++ b/code/modules/bitrunning/server/threats.dm @@ -2,6 +2,7 @@ /obj/machinery/quantum_server/proc/add_threats(mob/living/threat) spawned_threat_refs.Add(WEAKREF(threat)) SEND_SIGNAL(src, COMSIG_BITRUNNER_THREAT_CREATED) + threat.AddComponent(/datum/component/virtual_entity, src) /// Choses which antagonist role is spawned based on threat /obj/machinery/quantum_server/proc/get_antagonist_role() diff --git a/code/modules/bitrunning/spawners.dm b/code/modules/bitrunning/spawners.dm new file mode 100644 index 0000000000000..4f8aab322ffac --- /dev/null +++ b/code/modules/bitrunning/spawners.dm @@ -0,0 +1,51 @@ +/obj/effect/mob_spawn/ghost_role/human/virtual_domain + outfit = /datum/outfit/pirate + prompt_name = "a virtual domain debug entity" + flavour_text = "You probably shouldn't be seeing this, contact a coder!" + you_are_text = "You are NOT supposed to be here. How did you let this happen?" + +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/Initialize(mapload) + . = ..() + notify_ghosts("The [name] has been created. The virtual world calls for aid!", src, "Virtual Insanity!") + +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/special(mob/living/spawned_mob, mob/mob_possessor) + . = ..() + + spawned_mob.mind.add_antag_datum(/datum/antagonist/domain_ghost_actor) + +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate + name = "Virtual Pirate Remains" + desc = "Some inanimate bones. They feel like they could spring to life at any moment!" + density = FALSE + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + prompt_name = "a virtual skeleton pirate" + you_are_text = "You are a virtual pirate. Yarrr!" + flavour_text = "You have awoken, without instruction. There's a LANDLUBBER after yer booty. Stop them!" + +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate/special(mob/living/spawned_mob, mob/mob_possessor) + . = ..() + spawned_mob.fully_replace_character_name(spawned_mob.real_name, "[pick(strings(PIRATE_NAMES_FILE, "generic_beginnings"))][pick(strings(PIRATE_NAMES_FILE, "generic_endings"))]") + +/obj/effect/mob_spawn/ghost_role/human/virtual_domain/syndie + name = "Virtual Syndicate Sleeper" + icon = 'icons/obj/machines/sleeper.dmi' + icon_state = "sleeper_s" + prompt_name = "a virtual syndicate operative" + you_are_text = "You are a virtual syndicate operative." + flavour_text = "You have awoken, without instruction. Alarms blare! We are being boarded!" + outfit = /datum/outfit/virtual_syndicate + spawner_job_path = /datum/job/space_syndicate + +/datum/outfit/virtual_syndicate + name = "Virtual Syndie" + id = /obj/item/card/id/advanced/chameleon + id_trim = /datum/id_trim/chameleon/operative + uniform = /obj/item/clothing/under/syndicate + back = /obj/item/storage/backpack + gloves = /obj/item/clothing/gloves/tackler/combat/insulated + shoes = /obj/item/clothing/shoes/combat + implants = list(/obj/item/implant/weapons_auth) + +/datum/outfit/virtual_syndicate/post_equip(mob/living/carbon/human/user, visualsOnly) + user.faction |= ROLE_SYNDICATE diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm index 636b4f4791b31..d4ef4b6a148e0 100644 --- a/code/modules/cargo/bounties/assistant.dm +++ b/code/modules/cargo/bounties/assistant.dm @@ -198,11 +198,11 @@ wanted_types = list(/obj/item/pneumatic_cannon/ghetto = TRUE) /datum/bounty/item/assistant/improvised_shells - name = "Improvised Shotgun Shells" - description = "Budget cuts are hitting our security department pretty hard. Send some improvised shotgun shells when you can." + name = "Junk Shells" + description = "Our assistant militia has chewed through all our iron supplies. To stop them making bullets out of station property, we need junk shells, pronto." reward = CARGO_CRATE_VALUE * 4 required_count = 5 - wanted_types = list(/obj/item/ammo_casing/shotgun/improvised = TRUE) + wanted_types = list(/obj/item/ammo_casing/junk = TRUE) /datum/bounty/item/assistant/flamethrower name = "Flamethrower" diff --git a/code/modules/cargo/markets/market_item.dm b/code/modules/cargo/markets/market_item.dm index 51651ffdc47f6..21ff3d01deb3b 100644 --- a/code/modules/cargo/markets/market_item.dm +++ b/code/modules/cargo/markets/market_item.dm @@ -16,6 +16,9 @@ /// Path to or the item itself what this entry is for, this should be set even if you override spawn_item to spawn your item. var/atom/movable/item + /// Used to exclude abstract/special paths from the unit test if the value matches the type itself. + var/abstract_path + /// Minimum price for the item if generated randomly. var/price_min = 0 /// Maximum price for the item if generated randomly. @@ -25,7 +28,7 @@ /// Maximum amount that there should be of this item in the market if generated randomly. var/stock_max = 0 /// Probability for this item to be available. Used by SSblackmarket on init. - var/availability_prob = 0 + var/availability_prob ///The identifier for the market item, generated on runtime and used to access them in the market categories. var/identifier diff --git a/code/modules/cargo/markets/market_items/clothing.dm b/code/modules/cargo/markets/market_items/clothing.dm index bf705e8b57251..82bda848eb8e9 100644 --- a/code/modules/cargo/markets/market_items/clothing.dm +++ b/code/modules/cargo/markets/market_items/clothing.dm @@ -1,5 +1,6 @@ /datum/market_item/clothing category = "Clothing" + abstract_path = /datum/market_item/clothing /datum/market_item/clothing/ninja_mask name = "Space Ninja Mask" diff --git a/code/modules/cargo/markets/market_items/consumables.dm b/code/modules/cargo/markets/market_items/consumables.dm index 5550d31c5f865..f002ff994249d 100644 --- a/code/modules/cargo/markets/market_items/consumables.dm +++ b/code/modules/cargo/markets/market_items/consumables.dm @@ -1,5 +1,6 @@ /datum/market_item/consumable category = "Consumables" + abstract_path = /datum/market_item/consumable /datum/market_item/consumable/clown_tears name = "Bottle of Clown's Tears" diff --git a/code/modules/cargo/markets/market_items/hostages.dm b/code/modules/cargo/markets/market_items/hostages.dm index 6551ee6156b46..ed5b1f10a7fcf 100644 --- a/code/modules/cargo/markets/market_items/hostages.dm +++ b/code/modules/cargo/markets/market_items/hostages.dm @@ -1,6 +1,7 @@ ///A special category for mobs captured by pirates, tots and contractors, should someone ever want to get them back in advance. /datum/market_item/hostage category = "Hostages" + abstract_path = /datum/market_item/hostage stock = 1 availability_prob = 100 shipping_override = list(SHIPPING_METHOD_LTSRBT = 0, SHIPPING_METHOD_SUPPLYPOD = 350) diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm index de0fcaa9256a4..435396c15f251 100644 --- a/code/modules/cargo/markets/market_items/misc.dm +++ b/code/modules/cargo/markets/market_items/misc.dm @@ -1,5 +1,6 @@ /datum/market_item/misc category = "Miscellaneous" + abstract_path = /datum/market_item/misc /datum/market_item/misc/Clear_PDA name = "Clear PDA" @@ -53,6 +54,7 @@ /datum/market_item/misc/shove_blocker name = "MOD Bulwark Module" desc = "You have no idea how much effort it took us to extract this module from that damn safeguard MODsuit last shift." + item = /obj/item/mod/module/shove_blocker price_min = CARGO_CRATE_VALUE * 4 price_max = CARGO_CRATE_VALUE * 5.75 stock_max = 1 @@ -108,6 +110,7 @@ /datum/market_item/misc/jawed_hook name = "Jawed Fishing Hook" desc = "The thing ya use if y'are strugglin' with fishes. Just rememeber to whoop yer rod before it's too late, 'cause this thing's gonna hurt them like an Arkansas toothpick." + item = /obj/item/fishing_hook/jaws price_min = CARGO_CRATE_VALUE * 0.75 price_max = CARGO_CRATE_VALUE * 2 stock_max = 3 diff --git a/code/modules/cargo/markets/market_items/stolen_goods.dm b/code/modules/cargo/markets/market_items/stolen_goods.dm index c9c17f1d2b6c8..02a72f05d26d1 100644 --- a/code/modules/cargo/markets/market_items/stolen_goods.dm +++ b/code/modules/cargo/markets/market_items/stolen_goods.dm @@ -1,6 +1,7 @@ ///A special category for goods stolen by spies for their bounties. /datum/market_item/stolen_good category = "Fenced Goods" + abstract_path = /datum/market_item/stolen_good stock = 1 availability_prob = 100 diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm index 5d036fae0ef5b..9576810b3a3c9 100644 --- a/code/modules/cargo/markets/market_items/tools.dm +++ b/code/modules/cargo/markets/market_items/tools.dm @@ -1,5 +1,6 @@ /datum/market_item/tool category = "Tools" + abstract_path = /datum/market_item/tool /datum/market_item/tool/blackmarket_telepad name = "Black Market LTSRBT" diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm index 11f242d57c874..3323e16916234 100644 --- a/code/modules/cargo/markets/market_items/weapons.dm +++ b/code/modules/cargo/markets/market_items/weapons.dm @@ -1,5 +1,6 @@ /datum/market_item/weapon category = "Weapons" + abstract_path = /datum/market_item/weapon /datum/market_item/weapon/bear_trap name = "Bear Trap" diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 8e51fa917f4da..ab1c5ffdcb7ab 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -121,6 +121,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( no_tgui_adminhelp(input(src, "Enter your ahelp", "Ahelp") as null|message) return + if(href_list["commandbar_typing"]) + handle_commandbar_typing(href_list) + switch(href_list["_src_"]) if("holder") hsrc = holder @@ -254,6 +257,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( tgui_say = new(src, "tgui_say") + initialize_commandbar_spy() + set_right_click_menu_mode(TRUE) GLOB.ahelp_tickets.ClientLogin(src) diff --git a/code/modules/client/verbs/typing.dm b/code/modules/client/verbs/typing.dm new file mode 100644 index 0000000000000..b7a7362261486 --- /dev/null +++ b/code/modules/client/verbs/typing.dm @@ -0,0 +1,70 @@ +#define IC_VERBS list("say", "me", "whisper") + +/client/var/commandbar_thinking = FALSE +/client/var/commandbar_typing = FALSE + +/client/proc/initialize_commandbar_spy() + src << output('html/typing_indicator.html', "commandbar_spy") + +/client/proc/handle_commandbar_typing(href_list) + if (!typing_indicators) //check pref + return + if (length(href_list["verb"]) < 1 || !(LOWER_TEXT(href_list["verb"]) in IC_VERBS) || text2num(href_list["argument_length"]) < 1) + if (commandbar_typing) + commandbar_typing = FALSE + stop_typing() + + if (commandbar_thinking) + commandbar_thinking = FALSE + stop_thinking() + return + + if (!commandbar_thinking) + commandbar_thinking = TRUE + start_thinking() + + if (!commandbar_typing) + commandbar_typing = TRUE + start_typing() + + +/** Sets the mob as "thinking" - with indicator and the TRAIT_THINKING_IN_CHARACTER trait */ +/client/proc/start_thinking() + if(!typing_indicators) + return FALSE + /// Special exemptions + if(isabductor(mob)) + return FALSE + ADD_TRAIT(mob, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) + mob.create_thinking_indicator() + +/** Removes typing/thinking indicators and flags the mob as not thinking */ +/client/proc/stop_thinking() + mob?.remove_all_indicators() + +/** + * Handles the user typing. After a brief period of inactivity, + * signals the client mob to revert to the "thinking" icon. + */ +/client/proc/start_typing() + var/mob/client_mob = mob + client_mob.remove_thinking_indicator() + if(!typing_indicators || !HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER)) + return FALSE + client_mob.create_typing_indicator() + addtimer(CALLBACK(src, PROC_REF(stop_typing)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE) + +/** + * Callback to remove the typing indicator after a brief period of inactivity. + * If the user was typing IC, the thinking indicator is shown. + */ +/client/proc/stop_typing() + if(isnull(mob)) + return FALSE + var/mob/client_mob = mob + client_mob.remove_typing_indicator() + if(!typing_indicators || !HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER)) + return FALSE + client_mob.create_thinking_indicator() + +#undef IC_VERBS diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index e0b06a5ab6e2f..1ea2acb78a61f 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -246,8 +246,10 @@ interaction_flags_click = FORBID_TELEKINESIS_REACH|ALLOW_RESTING ///prefix our phrases must begin with var/prefix = "go go gadget" - ///an assoc list of phrase = item (like gun = revolver) - var/list/items_by_phrase = list() + ///an assoc list of regex = item (like regex datum = revolver item) + var/list/items_by_regex = list() + ///A an assoc list of regex = phrase (like regex datum = gun text) + var/list/phrases_by_regex = list() ///how many gadgets can we hold var/max_items = 4 ///items above this weight cannot be put in the hat @@ -258,36 +260,58 @@ become_hearing_sensitive(ROUNDSTART_TRAIT) QDEL_NULL(atom_storage) +/obj/item/clothing/head/fedora/inspector_hat/proc/set_prefix(desired_prefix) + + prefix = desired_prefix + + // Regenerated the phrases here. + for(var/old_regex in phrases_by_regex) + var/old_phrase = phrases_by_regex[old_regex] + var/obj/item/old_item = items_by_regex[old_regex] + items_by_regex -= old_regex + phrases_by_regex -= old_regex + set_phrase(old_phrase,old_item) + + return TRUE + +/obj/item/clothing/head/fedora/inspector_hat/proc/set_phrase(desired_phrase,obj/item/associated_item) + + var/regex/phrase_regex = regex("[prefix]\[\\s\\W\]+[desired_phrase]","i") + + phrases_by_regex[phrase_regex] = desired_phrase + items_by_regex[phrase_regex] = associated_item + + return TRUE + /obj/item/clothing/head/fedora/inspector_hat/examine(mob/user) . = ..() . += span_notice("You can put items inside, and get them out by saying a phrase, or using it in-hand!") . += span_notice("The prefix is [prefix], and you can change it with alt-click!\n") - for(var/phrase in items_by_phrase) - var/obj/item/item = items_by_phrase[phrase] - . += span_notice("[icon2html(item, user)] You can remove [item] by saying \"[prefix] [phrase]\"!") + for(var/found_regex in phrases_by_regex) + var/found_phrase = phrases_by_regex[found_regex] + var/obj/item/found_item = items_by_regex[found_regex] + . += span_notice("[icon2html(found_item, user)] You can remove [found_item] by saying \"[prefix] [found_phrase]\"!") /obj/item/clothing/head/fedora/inspector_hat/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, list/message_mods = list(), message_range) . = ..() var/mob/living/carbon/wearer = loc if(!istype(wearer) || speaker != wearer) //if we are worn - return FALSE + return raw_message = htmlrendertext(raw_message) - var/prefix_index = findtext(raw_message, prefix) - if(prefix_index != 1) - return FALSE - - var/the_phrase = trim_left(replacetext(raw_message, prefix, "")) - var/obj/item/result = items_by_phrase[the_phrase] - if(!result) - return FALSE - if(wearer.put_in_active_hand(result)) - wearer.visible_message(span_warning("[src] drops [result] into the hands of [wearer]!")) - else - balloon_alert(wearer, "cant put in hands!") - - return TRUE + for(var/regex/found_regex as anything in phrases_by_regex) + if(!found_regex.Find(raw_message)) + continue + var/obj/item/found_item = items_by_regex[found_regex] + if(wearer.put_in_hands(found_item)) + wearer.visible_message(span_warning("[src] drops [found_item] into the hands of [wearer]!")) + . = TRUE + else + balloon_alert(wearer, "can't put in hands!") + break + + return . /obj/item/clothing/head/fedora/inspector_hat/attackby(obj/item/item, mob/user, params) . = ..() @@ -299,51 +323,61 @@ balloon_alert(user, "too big!") return - var/input = tgui_input_text(user, "What is the activation phrase?", "Activation phrase", "gadget", max_length = 26) - if(!input || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return - if(input in items_by_phrase) - balloon_alert(user, "already used!") + var/desired_phrase = tgui_input_text(user, "What is the activation phrase?", "Activation phrase", "gadget", max_length = 26) + if(!desired_phrase || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) return if(item.loc != user || !user.transferItemToLoc(item, src)) return - to_chat(user, span_notice("You install [item] into the [thtotext(contents.len)] slot in [src].")) + to_chat(user, span_notice("You install [item] into the [thtotext(contents.len)] slot of [src].")) playsound(src, 'sound/machines/click.ogg', 30, TRUE) - items_by_phrase[input] = item + set_phrase(desired_phrase,item) + + return TRUE /obj/item/clothing/head/fedora/inspector_hat/attack_self(mob/user) . = ..() - var/phrase = tgui_input_list(user, "What item do you want to remove by phrase?", "Item Removal", items_by_phrase) - if(!phrase || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return - user.put_in_inactive_hand(items_by_phrase[phrase]) + if(!length(items_by_regex)) + return CLICK_ACTION_BLOCKING + var/list/found_items = list() + for(var/found_regex in items_by_regex) + found_items += items_by_regex[found_regex] + var/obj/found_item = tgui_input_list(user, "What item do you want to remove?", "Item Removal", found_items) + if(!found_item || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) + return CLICK_ACTION_BLOCKING + user.put_in_inactive_hand(found_item) /obj/item/clothing/head/fedora/inspector_hat/click_alt(mob/user) var/new_prefix = tgui_input_text(user, "What should be the new prefix?", "Activation prefix", prefix, max_length = 24) if(!new_prefix || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) return CLICK_ACTION_BLOCKING - prefix = new_prefix + set_prefix(new_prefix) return CLICK_ACTION_SUCCESS /obj/item/clothing/head/fedora/inspector_hat/Exited(atom/movable/gone, direction) . = ..() - for(var/phrase in items_by_phrase) - var/obj/item/result = items_by_phrase[phrase] - if(gone == result) - items_by_phrase -= phrase - return + for(var/found_regex in items_by_regex) + var/obj/item/found_item = items_by_regex[found_regex] + if(gone != found_item) + continue + items_by_regex -= found_regex + phrases_by_regex -= found_regex + break /obj/item/clothing/head/fedora/inspector_hat/atom_destruction(damage_flag) - for(var/phrase in items_by_phrase) - var/obj/item/result = items_by_phrase[phrase] - result.forceMove(drop_location()) - items_by_phrase = null + + var/atom/atom_location = drop_location() + for(var/found_regex in items_by_regex) + var/obj/item/result = items_by_regex[found_regex] + result.forceMove(atom_location) + items_by_regex -= found_regex + phrases_by_regex -= found_regex + return ..() /obj/item/clothing/head/fedora/inspector_hat/Destroy() - QDEL_LIST_ASSOC(items_by_phrase) + QDEL_LIST_ASSOC(items_by_regex) //Anything that failed to drop gets deleted. return ..() //Mime diff --git a/code/modules/clothing/suits/jacket.dm b/code/modules/clothing/suits/jacket.dm index 6db889032c064..9004f773e35ba 100644 --- a/code/modules/clothing/suits/jacket.dm +++ b/code/modules/clothing/suits/jacket.dm @@ -54,6 +54,16 @@ desc = "Aviators not included." icon_state = "bomberjacket" inhand_icon_state = "brownjsuit" + allowed = list( + /obj/item/flashlight, + /obj/item/tank/internals/emergency_oxygen, + /obj/item/tank/internals/plasmaman, + /obj/item/toy, + /obj/item/storage/fancy/cigarettes, + /obj/item/lighter, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, + /obj/item/radio, + ) /obj/item/clothing/suit/jacket/leather name = "leather jacket" @@ -62,7 +72,19 @@ inhand_icon_state = "hostrench" resistance_flags = NONE max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/gun/ballistic/revolver/c38/detective, /obj/item/radio) + allowed = list( + /obj/item/flashlight, + /obj/item/tank/internals/emergency_oxygen, + /obj/item/tank/internals/plasmaman, + /obj/item/toy, + /obj/item/storage/fancy/cigarettes, + /obj/item/lighter, + /obj/item/gun/ballistic/automatic/pistol, + /obj/item/gun/ballistic/revolver, + /obj/item/gun/ballistic/revolver/c38/detective, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, + /obj/item/radio, + ) /obj/item/clothing/suit/jacket/leather/biker name = "biker jacket" @@ -96,7 +118,19 @@ desc = "A canvas jacket styled after classical American military garb. Feels sturdy, yet comfortable." icon_state = "militaryjacket" inhand_icon_state = null - allowed = list(/obj/item/flashlight, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/toy, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/revolver, /obj/item/radio) + allowed = list( + /obj/item/flashlight, + /obj/item/tank/internals/emergency_oxygen, + /obj/item/tank/internals/plasmaman, + /obj/item/toy, + /obj/item/storage/fancy/cigarettes, + /obj/item/lighter, + /obj/item/gun/ballistic/automatic/pistol, + /obj/item/gun/ballistic/revolver, + /obj/item/gun/ballistic/revolver/c38/detective, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, + /obj/item/radio, + ) /obj/item/clothing/suit/jacket/letterman name = "letterman jacket" @@ -118,6 +152,19 @@ icon_state = "letterman_s" inhand_icon_state = null species_exception = list(/datum/species/golem) + allowed = list( + /obj/item/flashlight, + /obj/item/tank/internals/emergency_oxygen, + /obj/item/tank/internals/plasmaman, + /obj/item/toy, + /obj/item/storage/fancy/cigarettes, + /obj/item/lighter, + /obj/item/gun/ballistic/automatic/pistol, + /obj/item/gun/ballistic/revolver, + /obj/item/gun/ballistic/revolver/c38/detective, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, + /obj/item/radio, + ) /obj/item/clothing/suit/jacket/letterman_nanotrasen name = "blue letterman jacket" diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index a8b2a52ff0478..fe6fce092a40c 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -179,7 +179,7 @@ /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/t_scanner, - /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, ) resistance_flags = NONE species_exception = list(/datum/species/golem) diff --git a/code/modules/clothing/suits/wetfloor.dm b/code/modules/clothing/suits/wetfloor.dm index 390a529710aef..17bef117ca644 100644 --- a/code/modules/clothing/suits/wetfloor.dm +++ b/code/modules/clothing/suits/wetfloor.dm @@ -19,7 +19,7 @@ allowed = list( /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, - /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime, + /obj/item/gun/ballistic/rifle/boltaction/pipegun, ) /datum/armor/suit_caution diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index e91da4305a659..3f64aec43431e 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -51,10 +51,15 @@ if(random_sensor) //make the sensor mode favor higher levels, except coords. sensor_mode = pick(SENSOR_VITALS, SENSOR_VITALS, SENSOR_VITALS, SENSOR_LIVING, SENSOR_LIVING, SENSOR_COORDS, SENSOR_COORDS, SENSOR_OFF) - if(!unique_reskin) // Already registered via unique reskin - register_context() + register_context() AddElement(/datum/element/update_icon_updates_onmob, flags = ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING, body = TRUE) +/obj/item/clothing/under/setup_reskinning() + if(!check_setup_reskinning()) + return + + // We already register context in Initialize. + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) /obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) . = ..() diff --git a/code/modules/clothing/under/accessories/_accessories.dm b/code/modules/clothing/under/accessories/_accessories.dm index 67c2768ad237d..fdaf666638da3 100644 --- a/code/modules/clothing/under/accessories/_accessories.dm +++ b/code/modules/clothing/under/accessories/_accessories.dm @@ -30,6 +30,13 @@ . = ..() register_context() +/obj/item/clothing/accessory/setup_reskinning() + if(!check_setup_reskinning()) + return + + // We already register context regardless in Initialize. + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) + /** * Can we be attached to the passed clothing article? */ diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index 94ba72f164def..bbafb4a132b1b 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -193,9 +193,13 @@ GLOBAL_LIST_INIT(pride_pin_reskins, list( icon_state = "pride" obj_flags = UNIQUE_RENAME | INFINITE_RESKIN -/obj/item/clothing/accessory/pride/Initialize(mapload) - . = ..() +/obj/item/clothing/accessory/pride/setup_reskinning() unique_reskin = GLOB.pride_pin_reskins + if(!check_setup_reskinning()) + return + + // We already register context regardless in Initialize. + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) /obj/item/clothing/accessory/deaf_pin name = "deaf personnel pin" diff --git a/code/modules/events/ghost_role/abductor.dm b/code/modules/events/ghost_role/abductor.dm index dfa20885f0c29..7ad20cff1eb4e 100644 --- a/code/modules/events/ghost_role/abductor.dm +++ b/code/modules/events/ghost_role/abductor.dm @@ -16,7 +16,7 @@ /datum/round_event/ghost_role/abductor/spawn_role() var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ABDUCTOR, role = ROLE_ABDUCTOR, alert_pic = /obj/item/melee/baton/abductor, role_name_text = role_name, amount_to_pick = 2) - if(candidates.len < 2) + if(length(candidates) < 2) return NOT_ENOUGH_PLAYERS SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_ABDUCTOR_SHIPS) diff --git a/code/modules/events/ghost_role/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm index 88e79fd7d60c3..1c0c938ce89fc 100644 --- a/code/modules/events/ghost_role/alien_infestation.dm +++ b/code/modules/events/ghost_role/alien_infestation.dm @@ -64,7 +64,7 @@ var/list/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, alert_pic = /mob/living/carbon/alien/larva, role_name_text = role_name) - if(!candidates.len) + if(!length(candidates)) return NOT_ENOUGH_PLAYERS while(spawncount > 0 && vents.len && candidates.len) diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm index 3aeebd298f43e..e60498a90d0ba 100644 --- a/code/modules/events/ghost_role/sentience.dm +++ b/code/modules/events/ghost_role/sentience.dm @@ -49,8 +49,9 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( priority_announce(sentience_report,"[command_name()] Medium-Priority Update") /datum/round_event/ghost_role/sentience/spawn_role() - var/list/mob/dead/observer/candidates - candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, alert_pic = /obj/item/slimepotion/slime/sentience, role_name_text = role_name) + var/list/mob/dead/observer/candidates = SSpolling.poll_ghost_candidates(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, alert_pic = /obj/item/slimepotion/slime/sentience, role_name_text = role_name) + if(!length(candidates)) + return NOT_ENOUGH_PLAYERS // find our chosen mob to breathe life into // Mobs have to be simple animals, mindless, on station, and NOT holograms. diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index 20c4af94abdc3..eeed75d2299eb 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -70,23 +70,25 @@ /datum/round_event_control/santa name = "Visit by Santa" holidayID = CHRISTMAS - typepath = /datum/round_event/santa + typepath = /datum/round_event/ghost_role/santa weight = 20 max_occurrences = 1 earliest_start = 30 MINUTES category = EVENT_CATEGORY_HOLIDAY description = "Spawns santa, who shall roam the station, handing out gifts." -/datum/round_event/santa +/datum/round_event/ghost_role/santa + role_name = "Santa" var/mob/living/carbon/human/santa //who is our santa? -/datum/round_event/santa/announce(fake) +/datum/round_event/ghost_role/santa/announce(fake) priority_announce("Santa is coming to town!", "Unknown Transmission") -/datum/round_event/santa/start() +/datum/round_event/ghost_role/santa/start() var/mob/chosen_one = SSpolling.poll_ghost_candidates("Santa is coming to town! Do you want to be [span_notice("Santa")]?", poll_time = 15 SECONDS, alert_pic = /obj/item/clothing/head/costume/santa, role_name_text = "santa", amount_to_pick = 1) - if(chosen_one) - santa = new /mob/living/carbon/human(pick(GLOB.blobstart)) - santa.key = chosen_one.key - var/datum/antagonist/santa/A = new - santa.mind.add_antag_datum(A) + if(isnull(chosen_one)) + return NOT_ENOUGH_PLAYERS + santa = new /mob/living/carbon/human(pick(GLOB.blobstart)) + santa.key = chosen_one.key + var/datum/antagonist/santa/A = new + santa.mind.add_antag_datum(A) diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index cd7ab72cd8339..683b36304a367 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -32,30 +32,30 @@ VOICE_MODULATORS, WIZARD_MIMICRY, ) - var/list/loadout[SLOTS_AMT] + var/list/loadout = list() var/ruins_spaceworthiness = FALSE var/ruins_wizard_loadout = FALSE switch(item_set) if(BIG_FAT_DOOBIE) - loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/cigarette/rollie/trippy + loadout += /obj/item/clothing/mask/cigarette/rollie/trippy ruins_spaceworthiness = TRUE if(BOXING) - loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/luchador - loadout[ITEM_SLOT_GLOVES] = /obj/item/clothing/gloves/boxing + loadout += /obj/item/clothing/mask/luchador + loadout += /obj/item/clothing/gloves/boxing ruins_spaceworthiness = TRUE if(CATGIRLS_2015) - loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/costume/kitty - ruins_spaceworthiness = TRUE - ruins_wizard_loadout = TRUE + loadout += /obj/item/clothing/head/costume/kitty + ruins_spaceworthiness += TRUE + ruins_wizard_loadout += TRUE if(CURSED_SWORDS) - loadout[ITEM_SLOT_HANDS] = /obj/item/katana/cursed + loadout += /obj/item/katana/cursed if(VOICE_MODULATORS) - loadout[ITEM_SLOT_MASK] = /obj/item/clothing/mask/chameleon + loadout += /obj/item/clothing/mask/chameleon if(WIZARD_MIMICRY) - loadout[ITEM_SLOT_OCLOTHING] = /obj/item/clothing/suit/wizrobe - loadout[ITEM_SLOT_FEET] = /obj/item/clothing/shoes/sandal/magic - loadout[ITEM_SLOT_HEAD] = /obj/item/clothing/head/wizard + loadout += /obj/item/clothing/suit/wizrobe + loadout += /obj/item/clothing/shoes/sandal/magic + loadout += /obj/item/clothing/head/wizard ruins_spaceworthiness = TRUE var/list/mob/living/carbon/human/victims = list() @@ -67,18 +67,18 @@ continue if(item_set == CATGIRLS_2015) //Wizard code means never having to say you're sorry target.gender = FEMALE - for(var/iterable in 1 to loadout.len) - if(!loadout[iterable]) - continue - - var/obj/item/item_type = loadout[iterable] - var/obj/item/thing = new item_type //dumb but required because of byond throwing a fit anytime new gets too close to a list + for(var/item_to_equip in loadout) + var/obj/item/new_item = new item_to_equip + var/slot_to_equip_to = ITEM_SLOT_HANDS + if(isclothing(new_item)) + var/obj/item/clothing/clothing_item = new_item + slot_to_equip_to = clothing_item.slot_flags - target.dropItemToGround(target.get_item_by_slot(iterable), TRUE) - target.equip_to_slot_or_del(thing, iterable, indirect_action = TRUE) - ADD_TRAIT(thing, TRAIT_NODROP, CURSED_ITEM_TRAIT(thing)) - thing.item_flags |= DROPDEL - thing.name = "cursed " + thing.name + target.dropItemToGround(target.get_item_by_slot(slot_to_equip_to), TRUE) + target.equip_to_slot_or_del(new_item, slot_to_equip_to, indirect_action = TRUE) + ADD_TRAIT(new_item, TRAIT_NODROP, CURSED_ITEM_TRAIT(new_item)) + new_item.item_flags |= DROPDEL + new_item.name = "cursed " + new_item.name victims += target diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm index 14c1d88390ccb..e3b44e8465be1 100644 --- a/code/modules/fishing/fishing_minigame.dm +++ b/code/modules/fishing/fishing_minigame.dm @@ -644,8 +644,12 @@ hud_completion = new(null, null, challenge) vis_contents += list(hud_bait, hud_fish, hud_completion) challenge.user.client.screen += src + master_ref = WEAKREF(challenge) /atom/movable/screen/fishing_hud/Destroy() + var/datum/fishing_challenge/challenge = master_ref?.resolve() + if(!isnull(challenge)) + challenge.user.client.screen -= src QDEL_NULL(hud_fish) QDEL_NULL(hud_bait) QDEL_NULL(hud_completion) diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm index 1142ba033ff75..bcf34c372285d 100644 --- a/code/modules/jobs/job_types/research_director.dm +++ b/code/modules/jobs/job_types/research_director.dm @@ -76,7 +76,7 @@ messenger = /obj/item/storage/backpack/messenger/science chameleon_extras = /obj/item/stamp/head/rd - skillchips = list(/obj/item/skillchip/job/research_director) + skillchips = list(/obj/item/skillchip/research_director, /obj/item/skillchip/job/roboticist) /datum/outfit/job/rd/mod name = "Research Director (MODsuit)" diff --git a/code/modules/library/skill_learning/job_skillchips/research_director.dm b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm similarity index 85% rename from code/modules/library/skill_learning/job_skillchips/research_director.dm rename to code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm index 899defb1fd5af..b889073909d61 100644 --- a/code/modules/library/skill_learning/job_skillchips/research_director.dm +++ b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm @@ -1,4 +1,4 @@ -/obj/item/skillchip/job/research_director +/obj/item/skillchip/research_director name = "R.D.S.P.L.X. skillchip" desc = "Knowledge of how to solve the ancient conumdrum; what happens when an unstoppable force meets an immovable object." auto_traits = list(TRAIT_ROD_SUPLEX) @@ -7,3 +7,6 @@ skill_icon = "dumbbell" activate_message = "You realise if you apply the correct force, at the correct angle, it is possible to make the immovable permanently movable." deactivate_message = "You forget how to permanently anchor a paradoxical object." + chip_category = SKILLCHIP_CATEGORY_GENERAL + skillchip_flags = NONE + slot_use = 1 diff --git a/code/modules/library/skill_learning/job_skillchips/_job.dm b/code/modules/library/skill_learning/job_skillchips/_job.dm index 87f2d47ac9cd5..23381d06066b2 100644 --- a/code/modules/library/skill_learning/job_skillchips/_job.dm +++ b/code/modules/library/skill_learning/job_skillchips/_job.dm @@ -1,11 +1,6 @@ -/// Job related skillchip category -#define SKILLCHIP_CATEGORY_JOB "job" - /obj/item/skillchip/job skillchip_flags = SKILLCHIP_RESTRICTED_CATEGORIES chip_category = SKILLCHIP_CATEGORY_JOB incompatibility_list = list(SKILLCHIP_CATEGORY_JOB) abstract_parent_type = /obj/item/skillchip/job slot_use = 2 - -#undef SKILLCHIP_CATEGORY_JOB diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm index 9657a33052729..ae40b84c64be9 100644 --- a/code/modules/library/skill_learning/skillchip.dm +++ b/code/modules/library/skill_learning/skillchip.dm @@ -1,10 +1,3 @@ -// Skillchip categories -//Various skillchip categories. Use these when setting which categories a skillchip restricts being paired with -//while using the SKILLCHIP_RESTRICTED_CATEGORIES flag -/// General related skillchip category -#define SKILLCHIP_CATEGORY_GENERAL "general" - - /obj/item/skillchip name = "skillchip" desc = "This biochip integrates with user's brain to enable mastery of specific skill. Consult certified Nanotrasen neurosurgeon before use." @@ -531,5 +524,3 @@ ADD_TRAIT(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT) source.adjustStaminaLoss(20) addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT), FLIP_EMOTE_DURATION + 0.1 SECONDS) - -#undef SKILLCHIP_CATEGORY_GENERAL diff --git a/code/modules/lootpanel/search_object.dm b/code/modules/lootpanel/search_object.dm index 520228e465eb3..4de60b04a678a 100644 --- a/code/modules/lootpanel/search_object.dm +++ b/code/modules/lootpanel/search_object.dm @@ -50,6 +50,7 @@ var/build = owner.byond_build var/version = owner.byond_version if(build < 515 || (build == 515 && version < 1635)) + icon = "n/a" return icon = "[item.icon]" @@ -64,10 +65,7 @@ /// Generates the icon for the search object. This is the expensive part. /datum/search_object/proc/generate_icon(client/owner) - if(ismob(item) || length(item.overlays) > 2) - icon = costly_icon2html(item, owner, sourceonly = TRUE) - else // our pre 515.1635 fallback for normal items - icon = icon2html(item, owner, sourceonly = TRUE) + icon = costly_icon2html(item, owner, sourceonly = TRUE) /// Parent item has been altered, search object no longer valid diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index f46c7ecebb938..8b82d4b7d7796 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -106,7 +106,10 @@ else if(!(obj_flags & EMAGGED)) security_radio.set_frequency(FREQ_SECURITY) - security_radio.talk_into(src, "A prisoner has returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY) + var/datum/record/crew/target = find_record(user_mob.real_name) + target?.wanted_status = WANTED_PAROLE + + security_radio.talk_into(src, "/p [user_mob.name] returned to the station. Minerals and Prisoner ID card ready for retrieval.", FREQ_SECURITY) user_mob.log_message("has completed their labor points goal and is now sending the gulag shuttle back to the station.", LOG_GAME) to_chat(user_mob, span_notice("Shuttle received message and will be sent shortly.")) return TRUE diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index f5afac95086ec..b118de06f057a 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -456,31 +456,32 @@ return obscured -/obj/item/proc/equip_to_best_slot(mob/M) - if(M.equip_to_appropriate_slot(src)) - M.update_held_items() +/// Tries to equip an item, store it in open storage, or in next best storage +/obj/item/proc/equip_to_best_slot(mob/user) + if(user.equip_to_appropriate_slot(src)) + user.update_held_items() return TRUE else if(equip_delay_self) return - if(M.active_storage?.attempt_insert(src, M)) + if(user.active_storage?.attempt_insert(src, user, messages = FALSE)) return TRUE var/list/obj/item/possible = list( - M.get_inactive_held_item(), - M.get_item_by_slot(ITEM_SLOT_BELT), - M.get_item_by_slot(ITEM_SLOT_DEX_STORAGE), - M.get_item_by_slot(ITEM_SLOT_BACK), + user.get_inactive_held_item(), + user.get_item_by_slot(ITEM_SLOT_BELT), + user.get_item_by_slot(ITEM_SLOT_DEX_STORAGE), + user.get_item_by_slot(ITEM_SLOT_BACK), ) - for(var/i in possible) - if(!i) + for(var/thing in possible) + if(isnull(thing)) continue - var/obj/item/I = i - if(I.atom_storage?.attempt_insert(src, M)) + var/obj/item/gear = thing + if(gear.atom_storage?.attempt_insert(src, user, messages = FALSE)) return TRUE - to_chat(M, span_warning("You are unable to equip that!")) + to_chat(user, span_warning("You are unable to equip that!")) return FALSE diff --git a/code/modules/mob/living/carbon/human/species_types/felinid.dm b/code/modules/mob/living/carbon/human/species_types/felinid.dm index 528d9a3a0073b..1e8cf9def9463 100644 --- a/code/modules/mob/living/carbon/human/species_types/felinid.dm +++ b/code/modules/mob/living/carbon/human/species_types/felinid.dm @@ -126,9 +126,6 @@ var/datum/species/human/felinid/cat_species = soon_to_be_felinid.dna.species cat_species.original_felinid = FALSE else - var/obj/item/organ/internal/ears/cat/kitty_ears = new - var/obj/item/organ/external/tail/cat/kitty_tail = new - // This removes the spines if they exist var/obj/item/organ/external/spines/current_spines = soon_to_be_felinid.get_organ_slot(ORGAN_SLOT_EXTERNAL_SPINES) if(current_spines) @@ -139,8 +136,12 @@ // Humans get converted directly to felinids, and the key is handled in on_species_gain. // Now when we get mob.dna.features[feature_key], it returns None, which is why the tail is invisible. // stored_feature_id is only set once (the first time an organ is inserted), so this should be safe. + var/obj/item/organ/internal/ears/cat/kitty_ears = new kitty_ears.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED) - kitty_tail.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED) + if(should_external_organ_apply_to(/obj/item/organ/external/tail/cat, soon_to_be_felinid)) //only give them a tail if they actually have sprites for it / are a compatible subspecies. + var/obj/item/organ/external/tail/cat/kitty_tail = new + kitty_tail.Insert(soon_to_be_felinid, special = TRUE, movement_flags = DELETE_IF_REPLACED) + if(!silent) to_chat(soon_to_be_felinid, span_boldnotice("Something is nya~t right.")) playsound(get_turf(soon_to_be_felinid), 'sound/effects/meow1.ogg', 50, TRUE, -1) @@ -161,6 +162,8 @@ qdel(old_tail) // Locate does not work on assoc lists, so we do it by hand for(var/external_organ in target_species.external_organs) + if(!should_external_organ_apply_to(external_organ, purrbated_human)) + continue if(ispath(external_organ, /obj/item/organ/external/tail)) var/obj/item/organ/external/tail/new_tail = new external_organ() new_tail.Insert(purrbated_human, special = TRUE, movement_flags = DELETE_IF_REPLACED) diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 960e50d77205f..570cf1ec30458 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -126,14 +126,14 @@ key_third_person = "flaps" message = "flaps their wings." hands_use_check = TRUE - var/wing_time = 20 + var/wing_time = 0.35 SECONDS /datum/emote/living/flap/run_emote(mob/user, params, type_override, intentional) . = ..() if(. && ishuman(user)) - var/mob/living/carbon/human/H = user + var/mob/living/carbon/human/human_user = user var/open = FALSE - var/obj/item/organ/external/wings/functional/wings = H.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS) + var/obj/item/organ/external/wings/functional/wings = human_user.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS) // open/close functional wings if(istype(wings)) @@ -144,9 +144,8 @@ wings.open_wings() addtimer(CALLBACK(wings, open ? TYPE_PROC_REF(/obj/item/organ/external/wings/functional, open_wings) : TYPE_PROC_REF(/obj/item/organ/external/wings/functional, close_wings)), wing_time) - // play moth flutter noise if moth wing - if(istype(wings, /obj/item/organ/external/wings/moth)) - playsound(H, 'sound/voice/moth/moth_flutter.ogg', 50, TRUE) + // play a flapping noise if the wing has this implemented + wings.make_flap_sound(human_user) /datum/emote/living/flap/aflap key = "aflap" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 5f5889ae0a3b4..85d71d1019d4b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -2400,10 +2400,10 @@ GLOBAL_LIST_EMPTY(fire_appearances) /// Proc to append behavior to the condition of being floored. Called when the condition starts. /mob/living/proc/on_floored_start() + on_fall() if(body_position == STANDING_UP) //force them on the ground set_body_position(LYING_DOWN) set_lying_angle(pick(90, 270)) - on_fall() /// Proc to append behavior to the condition of being floored. Called when the condition ends. diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 895df810cc4e8..b4fbd713065aa 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -281,6 +281,29 @@ qdel(src) return new_corgi +/** + * Turns the source atom into a crab crab, the peak of evolutionary design. + */ +/mob/living/carbon/human/proc/crabize() + if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM)) + return + ADD_TRAIT(src, TRAIT_NO_TRANSFORM, PERMANENT_TRANSFORMATION_TRAIT) + Paralyze(1, ignore_canstun = TRUE) + for(var/obj/item/objeto in src) + dropItemToGround(objeto) + regenerate_icons() + icon = null + SetInvisibility(INVISIBILITY_MAXIMUM) + + var/mob/living/basic/crab/new_crab = new (loc) + new_crab.set_combat_mode(TRUE) // snip snip + if(mind) + mind.transfer_to(new_crab) + + to_chat(new_crab, span_boldnotice("You have evolved into a crab!")) + qdel(src) + return new_crab + /mob/living/carbon/proc/gorillize() if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM)) return diff --git a/code/modules/photography/_pictures.dm b/code/modules/photography/_pictures.dm index 45fa5654ad922..3d430439b908c 100644 --- a/code/modules/photography/_pictures.dm +++ b/code/modules/photography/_pictures.dm @@ -72,7 +72,6 @@ .["caption"] = caption .["pixel_size_x"] = psize_x .["pixel_size_y"] = psize_y - .["blueprints"] = has_blueprints .["logpath"] = logpath SET_SERIALIZATION_SEMVER(semvers, "1.0.0") @@ -93,8 +92,6 @@ id = input["id"] psize_x = input["pixel_size_x"] psize_y = input["pixel_size_y"] - if(input["blueprints"]) - has_blueprints = input["blueprints"] if(input["caption"]) caption = input["caption"] if(input["desc"]) diff --git a/code/modules/power/apc/apc_main.dm b/code/modules/power/apc/apc_main.dm index 111cfde46e4a3..40af69a8dd08e 100644 --- a/code/modules/power/apc/apc_main.dm +++ b/code/modules/power/apc/apc_main.dm @@ -704,6 +704,8 @@ /obj/machinery/power/apc/proc/draw_energy(amount) var/grid_used = min(terminal?.surplus(), amount) terminal?.add_load(grid_used) + if(QDELETED(cell)) + return grid_used var/cell_used = 0 if(amount > grid_used) cell_used += cell.use(amount - grid_used, force = TRUE) diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index d3cfce7c14f3f..2d552dbbe9edb 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -379,6 +379,10 @@ span_notice("You open [src]'s casing."), span_hear("You hear a noise.")) deconstruct() return + + if(tool.item_flags & ABSTRACT) + return + to_chat(user, span_userdanger("You stick \the [tool] into the light socket!")) if(has_power() && (tool.obj_flags & CONDUCTS_ELECTRICITY)) do_sparks(3, TRUE, src) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index c9cbb0d3ecc50..71daf214019c7 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -62,7 +62,7 @@ /obj/machinery/power/multitool_act_secondary(mob/living/user, obj/item/tool) return multitool_act(user, tool) -/// Called on multitool_act when we can change cable layers, override to add more conditions +/// Called on multitool_act when we can change cable layers, override to add more conditions /obj/machinery/power/proc/cable_layer_act(mob/living/user, obj/item/tool) var/choice = tgui_input_list(user, "Select Power Line For Operation", "Select Cable Layer", GLOB.cable_name_to_layer) if(isnull(choice) || QDELETED(src) || QDELETED(user) || QDELETED(tool) || !user.Adjacent(src) || !user.is_holding(tool)) @@ -176,7 +176,7 @@ var/surplus = local_apc.surplus() var/grid_used = min(surplus, amount) var/apc_used = 0 - if((amount > grid_used) && !ignore_apc) // Use from the APC's cell if there isn't enough energy from the grid. + if((amount > grid_used) && !ignore_apc && !QDELETED(local_apc.cell)) // Use from the APC's cell if there isn't enough energy from the grid. apc_used = local_apc.cell.use(amount - grid_used, force = force) if(!force && (amount < grid_used + apc_used)) // If we aren't forcing it and there isn't enough energy to supply demand, return nothing. @@ -204,7 +204,7 @@ return amount var/obj/machinery/power/apc/my_apc = my_area.apc - if(isnull(my_apc)) + if(isnull(my_apc) || QDELETED(my_apc.cell)) return FALSE return my_apc.cell.use(amount, force = force) diff --git a/code/modules/power/supermatter/supermatter_variants.dm b/code/modules/power/supermatter/supermatter_variants.dm index 9d69066a5353b..ebc21b2b5b09f 100644 --- a/code/modules/power/supermatter/supermatter_variants.dm +++ b/code/modules/power/supermatter/supermatter_variants.dm @@ -21,6 +21,21 @@ layer = ABOVE_MOB_LAYER moveable = TRUE + +/obj/machinery/power/supermatter_crystal/shard/Initialize(mapload) + . = ..() + + register_context() + + +/obj/machinery/power/supermatter_crystal/shard/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + if(held_item?.tool_behaviour == TOOL_WRENCH) + context[SCREENTIP_CONTEXT_LMB] = anchored ? "Unanchor" : "Anchor" + return CONTEXTUAL_SCREENTIP_SET + + /// Shard SM with it's processing disabled. /obj/machinery/power/supermatter_crystal/shard/hugbox name = "anchored supermatter shard" diff --git a/code/modules/projectiles/ammunition/ballistic/junk.dm b/code/modules/projectiles/ammunition/ballistic/junk.dm new file mode 100644 index 0000000000000..99a9b637923f5 --- /dev/null +++ b/code/modules/projectiles/ammunition/ballistic/junk.dm @@ -0,0 +1,43 @@ +// Junk + +/obj/item/ammo_casing/junk + name = "improvised junk round" + desc = "What is in the shell? Shoot it to find out." + icon_state = "improvshell" + caliber = CALIBER_JUNK + projectile_type = /obj/projectile/bullet/junk + custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2, /datum/material/glass=SMALL_MATERIAL_AMOUNT*1) + +// Junk Shell Spawner; used to spawn in our random shells upon crafting + +/obj/effect/spawner/random/junk_shell + name = "junk shell spawner" + desc = "Bullet. Bullet Bullet." + icon_state = "junkround" + loot = list( + /obj/item/ammo_casing/junk = 50, + /obj/item/ammo_casing/junk/incendiary = 20, + /obj/item/ammo_casing/junk/shock = 20, + /obj/item/ammo_casing/junk/hunter = 20, + /obj/item/ammo_casing/junk/phasic = 5, + /obj/item/ammo_casing/junk/ripper = 5, + /obj/item/ammo_casing/junk/reaper = 1, + ) + +/obj/item/ammo_casing/junk/incendiary + projectile_type = /obj/projectile/bullet/incendiary/fire/junk + +/obj/item/ammo_casing/junk/phasic + projectile_type = /obj/projectile/bullet/junk/phasic + +/obj/item/ammo_casing/junk/shock + projectile_type = /obj/projectile/bullet/junk/shock + +/obj/item/ammo_casing/junk/hunter + projectile_type = /obj/projectile/bullet/junk/hunter + +/obj/item/ammo_casing/junk/ripper + projectile_type = /obj/projectile/bullet/junk/ripper + +/obj/item/ammo_casing/junk/reaper + projectile_type = /obj/projectile/bullet/junk/reaper diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm index 078f4bba1c4fd..b545500420bc1 100644 --- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm +++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm @@ -103,15 +103,6 @@ variance = 25 custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*2) -/obj/item/ammo_casing/shotgun/improvised - name = "improvised shell" - desc = "A homemade shotgun casing filled with crushed glass, used to commmit vandalism and property damage." - icon_state = "improvshell" - projectile_type = /obj/projectile/bullet/pellet/shotgun_improvised - custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2, /datum/material/glass=SMALL_MATERIAL_AMOUNT*1) - pellets = 6 - variance = 30 - /obj/item/ammo_casing/shotgun/ion name = "ion shell" desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \ diff --git a/code/modules/projectiles/boxes_magazines/internal/rifle.dm b/code/modules/projectiles/boxes_magazines/internal/rifle.dm index 5fdc182ccff98..52d395725a100 100644 --- a/code/modules/projectiles/boxes_magazines/internal/rifle.dm +++ b/code/modules/projectiles/boxes_magazines/internal/rifle.dm @@ -11,13 +11,23 @@ /obj/item/ammo_box/magazine/internal/boltaction/pipegun name = "pipegun internal magazine" - caliber = CALIBER_SHOTGUN - ammo_type = /obj/item/ammo_casing/shotgun/improvised + caliber = CALIBER_JUNK + ammo_type = /obj/item/ammo_casing/junk max_ammo = 1 +/obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol + name = "pipe pistol internal magazine" + max_ammo = 3 + /obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime name = "regal pipegun internal magazine" - max_ammo = 3 + max_ammo = 4 + ammo_type = /obj/item/ammo_casing/junk/reaper + +/obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol/prime + name = "regal pipe pistol internal magazine" + max_ammo = 6 + ammo_type = /obj/item/ammo_casing/junk/reaper /obj/item/ammo_box/magazine/internal/enchanted max_ammo = 1 diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm index ac988c2541bc1..aeeeaad304732 100644 --- a/code/modules/projectiles/guns/ballistic/rifle.dm +++ b/code/modules/projectiles/guns/ballistic/rifle.dm @@ -259,46 +259,78 @@ . = ..() AddComponent(/datum/component/scope, range_modifier = 2) //enough range to at least be useful for stealth +/// PIPE GUNS /// + /obj/item/gun/ballistic/rifle/boltaction/pipegun name = "pipegun" - desc = "An excellent weapon for flushing out tunnel rats and enemy assistants, but its rifling leaves much to be desired." - icon = 'icons/obj/weapons/guns/ballistic.dmi' - icon_state = "musket" - inhand_icon_state = "musket" - worn_icon_state = "musket" - lefthand_file = 'icons/mob/inhands/weapons/64x_guns_left.dmi' - righthand_file = 'icons/mob/inhands/weapons/64x_guns_right.dmi' - inhand_x_dimension = 64 - inhand_y_dimension = 64 + desc = "A symbol that the true masters of this place are not those who merely inhabit it, but the one willing to twist it towards a killing intent." + icon_state = "pipegun" + inhand_icon_state = "pipegun" + worn_icon_state = "pipegun" fire_sound = 'sound/weapons/gun/sniper/shot.ogg' accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun - initial_caliber = CALIBER_SHOTGUN - alternative_caliber = CALIBER_STRILKA310 - initial_fire_sound = 'sound/weapons/gun/sniper/shot.ogg' - alternative_fire_sound = 'sound/weapons/gun/shotgun/shot.ogg' - can_modify_ammo = TRUE + projectile_damage_multiplier = 1.35 + obj_flags = UNIQUE_RENAME can_bayonet = TRUE knife_x_offset = 25 knife_y_offset = 11 can_be_sawn_off = FALSE - projectile_damage_multiplier = 0.75 + trigger_guard = TRIGGER_GUARD_ALLOW_ALL - SET_BASE_PIXEL(0, 0) + SET_BASE_PIXEL(-8, 0) /obj/item/gun/ballistic/rifle/boltaction/pipegun/handle_chamber() . = ..() do_sparks(1, TRUE, src) +/obj/item/gun/ballistic/rifle/boltaction/pipegun/examine_more(mob/user) + . = ..() + . += span_notice("Looking down at the [name], you recall a tale told to you in some distant memory...") + + . += span_info("It's said that the first slaying committed on a Nanotrasen space station was by an assistant.") + . += span_info("That this act, done by toolbox, maybe spear, was what consigned their kind to a life of destitution, rejection and violence.") + . += span_info("They carry the weight of this act visibly; the grey jumpsuit. Breathing deeply filtered air. And with bloodsoaked yellow hands clenched into fists. Eyes, sharp and waiting. Hunters in the dark.") + . += span_info("Eventually, these killing spirits sought to stake a claim on the metal tombs they were trapped within. Rejecting their status. Determined to be something more.") + . += span_info("This weapon is one such tool. And it is a grim one indeed. Wrought from scrap, pulled from the station's walls and floors and the very nails holding it together.") + . += span_info("It is a symbol that the true masters of this place are not those who merely inhabit it. But the one willing to twist it towards a killing intent.") + +/obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol + name = "pipe pistol" + desc = "It is foolish to think that anyone wearing the grey is incapable of hurting you, simply because they are not baring their teeth." + icon_state = "pipepistol" + inhand_icon_state = "pipepistol" + worn_icon_state = "gun" + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol + projectile_damage_multiplier = 0.50 + spread = 15 //kinda inaccurate + slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_NORMAL + can_bayonet = FALSE + weapon_weight = WEAPON_MEDIUM + + SET_BASE_PIXEL(0, 0) + /obj/item/gun/ballistic/rifle/boltaction/pipegun/prime name = "regal pipegun" - desc = "Older, territorial assistants typically possess more valuable loot." - icon_state = "musket_prime" - inhand_icon_state = "musket_prime" - worn_icon_state = "musket_prime" + desc = "To call this 'regal' is a cruel irony. For the only noteworthy quality of nobility is in how it is wielded to kill. \ + All monarchs deserve to be crowned. But none will remember the dead tyrant for the red stain they left on the carpet." + icon_state = "regal_pipegun" + inhand_icon_state = "regal_pipegun" + worn_icon_state = "regal_pipegun" accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime + projectile_damage_multiplier = 2 + +/obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/prime + name = "regal pipe pistol" + desc = "What value is there in honesty towards the dishonest? So that they might twist the arm and slit the wrist? \ + The open palm is no sign of weakness; it is to draw the eyes away from the other hand, lying in wait." + icon_state = "regal_pipepistol" + inhand_icon_state = "regal_pipepistol" + accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol/prime projectile_damage_multiplier = 1 + spread = 0 -/// MAGICAL BOLT ACTIONS + ARCANE BARRAGE? /// +/// MAGICAL BOLT ACTIONS /// /obj/item/gun/ballistic/rifle/enchanted name = "enchanted bolt action rifle" diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index dfcb59f1e9c1c..b94cc63370eb5 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -36,7 +36,7 @@ /obj/item/gun/energy/fire_sounds() // What frequency the energy gun's sound will make - var/frequency_to_use + var/pitch_to_use var/obj/item/ammo_casing/energy/shot = ammo_type[select] // What percentage of the full battery a shot will expend @@ -44,15 +44,18 @@ // Ignore this on oversized/infinite cells or ammo without cost if(shot_cost_percent > 0) // The total amount of shots the fully charged energy gun can fire before running out - var/max_shots = round(100/shot_cost_percent) + var/max_shots = round(100/shot_cost_percent) - 1 // How many shots left before the energy gun's current battery runs out of energy - var/shots_left = round((round(clamp(cell.charge / cell.maxcharge, 0, 1) * 100))/shot_cost_percent) - frequency_to_use = sin((90/max_shots) * shots_left) + var/shots_left = round((round(clamp(cell.charge / cell.maxcharge, 0, 1) * 100))/shot_cost_percent) - 1 + pitch_to_use = LERP(1, 0.3, (1 - (shots_left/max_shots)) ** 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) /obj/item/gun/energy/emp_act(severity) . = ..() diff --git a/code/modules/projectiles/guns/special/medbeam.dm b/code/modules/projectiles/guns/special/medbeam.dm index 267470f17013c..0ad5caf2fec82 100644 --- a/code/modules/projectiles/guns/special/medbeam.dm +++ b/code/modules/projectiles/guns/special/medbeam.dm @@ -16,12 +16,7 @@ weapon_weight = WEAPON_MEDIUM -/obj/item/gun/medbeam/Initialize(mapload) - . = ..() - START_PROCESSING(SSobj, src) - /obj/item/gun/medbeam/Destroy(mob/user) - STOP_PROCESSING(SSobj, src) LoseTarget() return ..() @@ -41,6 +36,7 @@ QDEL_NULL(current_beam) active = FALSE on_beam_release(current_target) + STOP_PROCESSING(SSobj, src) current_target = null /** @@ -69,6 +65,7 @@ active = TRUE current_beam = user.Beam(current_target, icon_state="medbeam", time = 10 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/medical) RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died))//this is a WAY better rangecheck than what was done before (process check) + START_PROCESSING(SSobj, src) SSblackbox.record_feedback("tally", "gun_fired", 1, type) diff --git a/code/modules/projectiles/projectile/bullets/junk.dm b/code/modules/projectiles/projectile/bullets/junk.dm new file mode 100644 index 0000000000000..344a732911347 --- /dev/null +++ b/code/modules/projectiles/projectile/bullets/junk.dm @@ -0,0 +1,71 @@ +// Junk (Pipe Pistols and Pipeguns) + +/obj/projectile/bullet/junk + name = "junk bullet" + icon_state = "trashball" + damage = 30 + embedding = list(embed_chance=15, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + var/bane_mob_biotypes = MOB_ROBOTIC + var/bane_multiplier = 1.5 + var/bane_added_damage = 0 + +/obj/projectile/bullet/junk/Initialize(mapload) + . = ..() + AddElement(/datum/element/bane, mob_biotypes = bane_mob_biotypes, target_type = /mob/living, damage_multiplier = bane_multiplier, added_damage = bane_added_damage, requires_combat_mode = FALSE) + +/obj/projectile/bullet/incendiary/fire/junk + name = "burning oil" + damage = 30 + fire_stacks = 5 + suppressed = SUPPRESSED_NONE + +/obj/projectile/bullet/junk/phasic + name = "junk phasic bullet" + icon_state = "gaussphase" + projectile_phasing = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF | PASSMACHINE | PASSSTRUCTURE | PASSDOORS + +/obj/projectile/bullet/junk/shock + name = "bundle of live electrical parts" + icon_state = "tesla_projectile" + damage = 15 + embedding = null + shrapnel_type = null + bane_multiplier = 3 + +/obj/projectile/bullet/junk/shock/on_hit(atom/target, blocked = 0, pierce_hit) + . = ..() + if(isliving(target)) + var/mob/living/victim = target + victim.electrocute_act(damage, src, siemens_coeff = 1, flags = SHOCK_NOSTUN) + +/obj/projectile/bullet/junk/hunter + name = "junk hunter bullet" + icon_state = "gauss" + bane_mob_biotypes = MOB_ROBOTIC | MOB_BEAST | MOB_SPECIAL + bane_multiplier = 0 + bane_added_damage = 50 + +/obj/projectile/bullet/junk/ripper + name = "junk ripper bullet" + icon_state = "redtrac" + damage = 10 + embedding = list(embed_chance=100, fall_chance=3, jostle_chance=4, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + wound_bonus = 10 + bare_wound_bonus = 30 + +/obj/projectile/bullet/junk/reaper + name = "junk reaper bullet" + tracer_type = /obj/effect/projectile/tracer/sniper + impact_type = /obj/effect/projectile/impact/sniper + muzzle_type = /obj/effect/projectile/muzzle/sniper + hitscan = TRUE + impact_effect_type = null + hitscan_light_intensity = 3 + hitscan_light_range = 0.75 + hitscan_light_color_override = LIGHT_COLOR_DIM_YELLOW + muzzle_flash_intensity = 5 + muzzle_flash_range = 1 + muzzle_flash_color_override = LIGHT_COLOR_DIM_YELLOW + impact_light_intensity = 5 + impact_light_range = 1 + impact_light_color_override = LIGHT_COLOR_DIM_YELLOW diff --git a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm index bb9e5869ca804..5811f49da8f21 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/glass_styles/alcohol.dm @@ -107,8 +107,8 @@ desc = "It's as strong as it smells." icon_state = "absinthe" -/datum/glass_style/drinking_glass/hooch - required_drink_type = /datum/reagent/consumable/ethanol/hooch +/datum/glass_style/drinking_glass/ale + required_drink_type = /datum/reagent/consumable/ethanol/ale name = "glass of ale" desc = "A freezing pint of delicious Ale." icon_state = "aleglass" 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 3c677e43d1c07..3ae946d4fab46 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 @@ -100,18 +100,7 @@ Basically, we fill the time between now and 2s from now with hands based off the /datum/reagent/inverse/helgrasp/proc/spawn_hands(mob/living/carbon/affected_mob) if(!affected_mob && iscarbon(holder.my_atom))//Catch timer affected_mob = holder.my_atom - //Adapted from the end of the curse - but lasts a short time - var/grab_dir = turn(affected_mob.dir, pick(-90, 90, 180, 180)) //grab them from a random direction other than the one faced, favoring grabbing from behind - var/turf/spawn_turf = get_ranged_target_turf(affected_mob, grab_dir, 8)//Larger range so you have more time to dodge - if(!spawn_turf) - return - new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, affected_mob.dir) - playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1) - var/obj/projectile/curse_hand/hel/hand = new (spawn_turf) - hand.preparePixelProjectile(affected_mob, spawn_turf) - if(QDELETED(hand)) //safety check if above fails - above has a stack trace if it does fail - return - hand.fire() + fire_curse_hand(affected_mob) //At the end, we clear up any loose hanging timers just in case and spawn any remaining lag_remaining hands all at once. /datum/reagent/inverse/helgrasp/on_mob_delete(mob/living/affected_mob) diff --git a/code/modules/recycling/conveyor.dm b/code/modules/recycling/conveyor.dm index 6ef15929ecc5e..2c8ca83ca1227 100644 --- a/code/modules/recycling/conveyor.dm +++ b/code/modules/recycling/conveyor.dm @@ -446,17 +446,33 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) else position = CONVEYOR_OFF -/// Called when a user clicks on this switch with an open hand. -/obj/machinery/conveyor_switch/attack_hand(mob/living/user, list/modifiers) +/obj/machinery/conveyor_switch/proc/on_user_activation(mob/user, direction) add_fingerprint(user) - if(LAZYACCESS(modifiers, RIGHT_CLICK)) - update_position(CONVEYOR_BACKWARDS) - else - update_position(CONVEYOR_FORWARD) + update_position(direction) update_appearance() update_linked_conveyors() update_linked_switches() - return TRUE + +/// Called when a user clicks on this switch with an open hand. +/obj/machinery/conveyor_switch/attack_hand(mob/user, list/modifiers) + . = ..() + on_user_activation(user, CONVEYOR_FORWARD) + +/obj/machinery/conveyor_switch/attack_hand_secondary(mob/user, list/modifiers) + on_user_activation(user, CONVEYOR_BACKWARDS) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + +/obj/machinery/conveyor_switch/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/conveyor_switch/attack_ai_secondary(mob/user, list/modifiers) + return attack_hand_secondary(user, modifiers) + +/obj/machinery/conveyor_switch/attack_robot(mob/user) + return attack_hand(user) + +/obj/machinery/conveyor_switch/attack_robot_secondary(mob/user, list/modifiers) + return attack_hand_secondary(user, modifiers) /obj/machinery/conveyor_switch/attackby(obj/item/attacking_item, mob/user, params) if(is_wire_tool(attacking_item)) diff --git a/code/modules/spells/spell_types/right_and_wrong.dm b/code/modules/spells/spell_types/right_and_wrong.dm index 27662943af03a..306770c074f43 100644 --- a/code/modules/spells/spell_types/right_and_wrong.dm +++ b/code/modules/spells/spell_types/right_and_wrong.dm @@ -230,9 +230,12 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( SSevents.reschedule() if(user) - to_chat(user, span_warning("You have intensified summon events, causing them to occur more often!")) - message_admins("[ADMIN_LOOKUPFLW(user)] intensified summon events!") - user.log_message("intensified events!", LOG_GAME) + message_admins("[ADMIN_LOOKUPFLW(user)] [ismob(user) ? "":"admin triggered "]intensified summon events!") + if(ismob(user)) + to_chat(user, span_warning("You have intensified summon events, causing them to occur more often!")) + user.log_message("intensified events!", LOG_GAME) + else //admin triggered + log_admin("[key_name(user)] intensified summon events.") else log_game("Summon Events was intensified!") @@ -245,9 +248,12 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( SSevents.toggleWizardmode() SSevents.reschedule() if(user) - to_chat(user, span_warning("You have cast summon events!")) - message_admins("[ADMIN_LOOKUPFLW(user)] summoned events!") - user.log_message("summoned events!", LOG_GAME) + message_admins("[ADMIN_LOOKUPFLW(user)] [ismob(user) ? "summoned":"admin triggered summon"] events!") + if(ismob(user)) + to_chat(user, span_warning("You have cast summon events!")) + user.log_message("summoned events!", LOG_GAME) + else //admin triggered + log_admin("[key_name(user)] summoned events.") else message_admins("Summon Events was triggered!") log_game("Summon Events was triggered!") diff --git a/code/modules/surgery/organs/external/wings/functional_wings.dm b/code/modules/surgery/organs/external/wings/functional_wings.dm index 5f2851b467635..a9eab672c1bdb 100644 --- a/code/modules/surgery/organs/external/wings/functional_wings.dm +++ b/code/modules/surgery/organs/external/wings/functional_wings.dm @@ -112,19 +112,20 @@ human.remove_traits(list(TRAIT_NO_FLOATING_ANIM, TRAIT_MOVE_FLYING), SPECIES_FLIGHT_TRAIT) passtable_off(human, SPECIES_FLIGHT_TRAIT) close_wings() - human.update_body_parts() ///SPREAD OUR WINGS AND FLLLLLYYYYYY /obj/item/organ/external/wings/functional/proc/open_wings() var/datum/bodypart_overlay/mutant/wings/functional/overlay = bodypart_overlay overlay.open_wings() wings_open = TRUE + owner.update_body_parts() ///close our wings /obj/item/organ/external/wings/functional/proc/close_wings() var/datum/bodypart_overlay/mutant/wings/functional/overlay = bodypart_overlay wings_open = FALSE overlay.close_wings() + owner.update_body_parts() if(isturf(owner?.loc)) var/turf/location = loc @@ -186,6 +187,9 @@ desc = "Powered by pure edgy-teenager-notebook-scribblings. Just kidding. But seriously, how do these keep you flying?!" sprite_accessory_override = /datum/sprite_accessory/wings/skeleton +/obj/item/organ/external/wings/functional/moth/make_flap_sound(mob/living/carbon/wing_owner) + playsound(wing_owner, 'sound/voice/moth/moth_flutter.ogg', 50, TRUE) + ///mothra wings, which relate to moths. /obj/item/organ/external/wings/functional/moth/mothra name = "mothra wings" diff --git a/code/modules/surgery/organs/external/wings/moth_wings.dm b/code/modules/surgery/organs/external/wings/moth_wings.dm index 11aebf4e8f1b5..87b944622aa09 100644 --- a/code/modules/surgery/organs/external/wings/moth_wings.dm +++ b/code/modules/surgery/organs/external/wings/moth_wings.dm @@ -25,6 +25,9 @@ UnregisterSignal(organ_owner, list(COMSIG_HUMAN_BURNING, COMSIG_LIVING_POST_FULLY_HEAL, COMSIG_MOVABLE_PRE_MOVE)) REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src)) +/obj/item/organ/external/wings/moth/make_flap_sound(mob/living/carbon/wing_owner) + playsound(wing_owner, 'sound/voice/moth/moth_flutter.ogg', 50, TRUE) + /obj/item/organ/external/wings/moth/can_soften_fall() return !burnt diff --git a/code/modules/surgery/organs/external/wings/wings.dm b/code/modules/surgery/organs/external/wings/wings.dm index 189c03e0277dd..775ffebf54cdf 100644 --- a/code/modules/surgery/organs/external/wings/wings.dm +++ b/code/modules/surgery/organs/external/wings/wings.dm @@ -13,6 +13,10 @@ /obj/item/organ/external/wings/proc/can_soften_fall() return TRUE +///Implement as needed to play a sound effect on *flap emote +/obj/item/organ/external/wings/proc/make_flap_sound(mob/living/carbon/wing_owner) + return + ///Bodypart overlay of default wings. Does not have any wing functionality /datum/bodypart_overlay/mutant/wings layers = ALL_EXTERNAL_OVERLAYS diff --git a/code/modules/surgery/organs/organ_movement.dm b/code/modules/surgery/organs/organ_movement.dm index c39541fb8efa4..2889cbe07081b 100644 --- a/code/modules/surgery/organs/organ_movement.dm +++ b/code/modules/surgery/organs/organ_movement.dm @@ -193,7 +193,6 @@ // The true movement is here moveToNullspace() - bodypart_owner.contents -= src bodypart_owner = null on_bodypart_remove(limb) diff --git a/code/modules/surgery/stomachpump.dm b/code/modules/surgery/stomachpump.dm index 0c9a0ce564f68..25f067aef6505 100644 --- a/code/modules/surgery/stomachpump.dm +++ b/code/modules/surgery/stomachpump.dm @@ -46,7 +46,7 @@ span_notice("[user] forces [target_human] to vomit, cleansing their stomach of some chemicals!"), span_notice("[user] forces [target_human] to vomit!"), ) - target_human.vomit(20, FALSE, TRUE, 1, TRUE, FALSE, purge_ratio = 0.67) //higher purge ratio than regular vomiting + target_human.vomit((MOB_VOMIT_MESSAGE | MOB_VOMIT_STUN), lost_nutrition = 20, purge_ratio = 0.67) //higher purge ratio than regular vomiting return ..() /datum/surgery_step/stomach_pump/failure(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) @@ -55,7 +55,7 @@ display_results( user, target, - span_warning("You screw up, brusing [target_human]'s chest!"), + span_warning("You screw up, bruising [target_human]'s chest!"), span_warning("[user] screws up, brusing [target_human]'s chest!"), span_warning("[user] screws up!"), ) diff --git a/code/modules/tgui_input/say_modal/typing.dm b/code/modules/tgui_input/say_modal/typing.dm index a6367c088d694..7fc6a93f29425 100644 --- a/code/modules/tgui_input/say_modal/typing.dm +++ b/code/modules/tgui_input/say_modal/typing.dm @@ -38,42 +38,31 @@ /** Sets the mob as "thinking" - with indicator and the TRAIT_THINKING_IN_CHARACTER trait */ /datum/tgui_say/proc/start_thinking() - if(!window_open || !client.typing_indicators) + if(!window_open) return FALSE - /// Special exemptions - if(isabductor(client.mob)) - return FALSE - ADD_TRAIT(client.mob, TRAIT_THINKING_IN_CHARACTER, CURRENTLY_TYPING_TRAIT) - client.mob.create_thinking_indicator() + return client.start_thinking() /** Removes typing/thinking indicators and flags the mob as not thinking */ /datum/tgui_say/proc/stop_thinking() - client.mob?.remove_all_indicators() + return client.stop_thinking() /** * Handles the user typing. After a brief period of inactivity, * signals the client mob to revert to the "thinking" icon. */ /datum/tgui_say/proc/start_typing() - var/mob/client_mob = client.mob - client_mob.remove_thinking_indicator() - if(!window_open || !client.typing_indicators || !HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER)) + if(!window_open) return FALSE - client_mob.create_typing_indicator() - addtimer(CALLBACK(src, PROC_REF(stop_typing)), 5 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_STOPPABLE) + return client.start_typing() /** - * Callback to remove the typing indicator after a brief period of inactivity. + * Remove the typing indicator after a brief period of inactivity or during say events. * If the user was typing IC, the thinking indicator is shown. */ /datum/tgui_say/proc/stop_typing() - if(isnull(client?.mob)) - return FALSE - var/mob/client_mob = client.mob - client_mob.remove_typing_indicator() - if(!window_open || !client.typing_indicators || !HAS_TRAIT(client_mob, TRAIT_THINKING_IN_CHARACTER)) + if(!window_open) return FALSE - client_mob.create_thinking_indicator() + client.stop_typing() /// Overrides for overlay creation /mob/living/create_thinking_indicator() diff --git a/code/modules/tutorials/_tutorial.dm b/code/modules/tutorials/_tutorial.dm index 7819a9e2b85ac..aecdcb7f23b88 100644 --- a/code/modules/tutorials/_tutorial.dm +++ b/code/modules/tutorials/_tutorial.dm @@ -13,8 +13,7 @@ /datum/tutorial/New(mob/user) src.user = user - RegisterSignal(user, COMSIG_QDELETING, PROC_REF(destroy_self)) - RegisterSignal(user.client, COMSIG_QDELETING, PROC_REF(destroy_self)) + RegisterSignals(user, list(COMSIG_QDELETING, COMSIG_MOB_LOGOUT), PROC_REF(destroy_self)) /datum/tutorial/Destroy(force) user.client?.screen -= instruction_screen @@ -242,7 +241,13 @@ /// Dismisses the tutorial, not marking it as completed in the database. /// Call `/datum/tutorial/proc/dismiss()` instead. /datum/tutorial_manager/proc/dismiss(mob/user) - performing_ckeys -= user.ckey + // this can be null in some disconnect/mob logout cases so we use some fallbacks + var/user_ckey = user.ckey + if(!user_ckey && user.canon_client) + user_ckey = user.canon_client.ckey + if(!user_ckey && user.mind?.key) + user_ckey = ckey(user.mind.key) + performing_ckeys -= user_ckey /// Given a ckey, will mark them as being completed without affecting the database. /// Call `/datum/tutorial/proc/complete()` instead. diff --git a/code/modules/tutorials/tutorial_instruction.dm b/code/modules/tutorials/tutorial_instruction.dm index 05c95e2f7540d..0ad9ce6f2e0fe 100644 --- a/code/modules/tutorials/tutorial_instruction.dm +++ b/code/modules/tutorials/tutorial_instruction.dm @@ -7,7 +7,6 @@ layer = TUTORIAL_INSTRUCTIONS_LAYER mouse_opacity = MOUSE_OPACITY_TRANSPARENT - var/client/client var/atom/movable/screen/tutorial_instruction_text/instruction_text /atom/movable/screen/tutorial_instruction/Initialize(mapload, datum/hud/hud_owner, message, client/client) @@ -15,14 +14,12 @@ transform = transform.Scale(36, 2.5) - src.client = client animate(src, alpha = 245, time = 0.8 SECONDS, easing = SINE_EASING) instruction_text = new(src, null, message, client) vis_contents += instruction_text /atom/movable/screen/tutorial_instruction/Destroy() - client = null QDEL_NULL(instruction_text) return ..() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 357c48f7a7b6f..e16824fd81604 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -101,6 +101,7 @@ #include "bespoke_id.dm" #include "binary_insert.dm" #include "bitrunning.dm" +#include "blackmarket.dm" #include "blindness.dm" #include "bloody_footprints.dm" #include "breath.dm" diff --git a/code/modules/unit_tests/blackmarket.dm b/code/modules/unit_tests/blackmarket.dm new file mode 100644 index 0000000000000..984e2ea815503 --- /dev/null +++ b/code/modules/unit_tests/blackmarket.dm @@ -0,0 +1,23 @@ +/// Ensures black market items have acceptable variable values. +/datum/unit_test/blackmarket + +/datum/unit_test/blackmarket/Run() + for(var/datum/market_item/prototype as anything in subtypesof(/datum/market_item)) + if(prototype::abstract_path == prototype) //skip abstract paths + continue + if(!prototype::category) + TEST_FAIL("[prototype] doesn't have a set category (or the abstract path var isn't correctly set)") + continue + if(!prototype::item) + TEST_FAIL("[prototype] doesn't have a set item (or the abstract path var isn't correctly set)") + continue + if(isnull(prototype::price) && prototype::price_max <= prototype::price_min) + TEST_FAIL("[prototype] doesn't have a correctly set random price (price_max should be higher than price_min)") + if(isnull(prototype::stock) && prototype::stock_max < prototype::stock_min) + TEST_FAIL("[prototype] doesn't have a correctly set random stock (stock_max shouldn't be lower than stock_min)") + if(!isnum(prototype::availability_prob)) + TEST_FAIL("[prototype] doesn't have a set availability_prob (must be a number)") + if(!prototype::name) + TEST_FAIL("[prototype] doesn't have a set name") + if(!prototype::desc) + TEST_FAIL("[prototype] doesn't have a set desc") diff --git a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm index 8343dc85a54b7..f7a866bdffe90 100644 --- a/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm +++ b/code/modules/vehicles/mecha/equipment/tools/mining_tools.dm @@ -32,6 +32,29 @@ ADD_TRAIT(src, TRAIT_INSTANTLY_PROCESSES_BOULDERS, INNATE_TRAIT) ADD_TRAIT(src, TRAIT_BOULDER_BREAKER, INNATE_TRAIT) +/obj/item/mecha_parts/mecha_equipment/drill/attach(obj/vehicle/sealed/mecha/new_mecha, attach_right) + . = ..() + RegisterSignal(chassis, COMSIG_MOVABLE_BUMP, PROC_REF(bump_mine)) + +/obj/item/mecha_parts/mecha_equipment/drill/detach(atom/moveto) + UnregisterSignal(chassis, COMSIG_MOVABLE_BUMP) + return ..() + +/obj/item/mecha_parts/mecha_equipment/drill/Destroy() + if(chassis) + UnregisterSignal(chassis, COMSIG_MOVABLE_BUMP) + return ..() + +///Called whenever the mech bumps into something; action() handles checking if it is a mineable turf +/obj/item/mecha_parts/mecha_equipment/drill/proc/bump_mine(obj/vehicle/sealed/mecha/bumper, atom/bumped_into) + SIGNAL_HANDLER + var/list/drivers = chassis.return_drivers() + if(!LAZYLEN(drivers)) //I don't know if this is possible but just in case + return + + //Just use the first one /shrug + INVOKE_ASYNC(src, PROC_REF(action), drivers[1], bumped_into, null, TRUE) + /obj/item/mecha_parts/mecha_equipment/drill/do_after_checks(atom/target) // Gotta be close to the target if(!loc.Adjacent(target)) @@ -41,55 +64,71 @@ return FALSE return ..() -/obj/item/mecha_parts/mecha_equipment/drill/action(mob/source, atom/target, list/modifiers) - // We can only drill non-space turfs, living mobs and objects. - if(isspaceturf(target) || !(isliving(target) || isobj(target) || isturf(target))) - return +/obj/item/mecha_parts/mecha_equipment/drill/action(mob/source, atom/target, list/modifiers, bumped) + //If bumped, only bother drilling mineral turfs + if(bumped) + if(!ismineralturf(target)) + return + + //Prevent drilling into gibtonite more than once; code mostly from MODsuit drill + if(istype(target, /turf/closed/mineral/gibtonite)) + var/turf/closed/mineral/gibtonite/giberal_turf = target + if(giberal_turf.stage != GIBTONITE_UNSTRUCK) + playsound(chassis, 'sound/machines/scanbuzz.ogg', 25, TRUE, SILENCED_SOUND_EXTRARANGE) + to_chat(source, span_warning("[icon2html(src, source)] Active gibtonite ore deposit detected! Safety protocols preventing continued drilling.")) + return - // For whatever reason we can't drill things that acid won't even stick too, and probably - // shouldn't waste our time drilling indestructible things. - if(isobj(target)) - var/obj/target_obj = target - if(target_obj.resistance_flags & (UNACIDABLE | INDESTRUCTIBLE)) + else + // We can only drill non-space turfs, living mobs and objects. + if(isspaceturf(target) || !(isliving(target) || isobj(target) || isturf(target))) return + // For whatever reason we can't drill things that acid won't even stick too, and probably + // shouldn't waste our time drilling indestructible things. + if(isobj(target)) + var/obj/target_obj = target + if(target_obj.resistance_flags & (UNACIDABLE | INDESTRUCTIBLE)) + return + // You can't drill harder by clicking more. - if(!DOING_INTERACTION_WITH_TARGET(source, target) && do_after_cooldown(target, source, DOAFTER_SOURCE_MECHADRILL)) - target.visible_message(span_warning("[chassis] starts to drill [target]."), \ - span_userdanger("[chassis] starts to drill [target]..."), \ - span_hear("You hear drilling.")) + if(DOING_INTERACTION_WITH_TARGET(source, target) && do_after_cooldown(target, source, DOAFTER_SOURCE_MECHADRILL)) + return - log_message("Started drilling [target]", LOG_MECHA) + target.visible_message(span_warning("[chassis] starts to drill [target]."), \ + span_userdanger("[chassis] starts to drill [target]..."), \ + span_hear("You hear drilling.")) - // Drilling a turf is a one-and-done procedure. - if(isturf(target)) - // Check if we can even use the equipment to begin with. - if(!action_checks(target)) - return + log_message("Started drilling [target]", LOG_MECHA) + + // Drilling a turf is a one-and-done procedure. + if(isturf(target)) + // Check if we can even use the equipment to begin with. + if(!action_checks(target)) + return - var/turf/T = target - T.drill_act(src, source) - - return ..() - - // Drilling objects and mobs is a repeating procedure. - while(do_after_mecha(target, source, drill_delay)) - if(isliving(target)) - drill_mob(target, source) - playsound(src,'sound/weapons/drill.ogg',40,TRUE) - else if(isobj(target)) - var/obj/O = target - if(istype(O, /obj/item/boulder)) - var/obj/item/boulder/nu_boulder = O - nu_boulder.manual_process(src, source) - else - O.take_damage(15, BRUTE, 0, FALSE, get_dir(chassis, target)) - playsound(src,'sound/weapons/drill.ogg', 40, TRUE) - - // If we caused a qdel drilling the target, we can stop drilling them. - // Prevents starting a do_after on a qdeleted target. - if(QDELETED(target)) - break + var/turf/T = target + T.drill_act(src, source) + + return ..() + + // Drilling objects and mobs is a repeating procedure. + while(do_after_mecha(target, source, drill_delay)) + if(isliving(target)) + drill_mob(target, source) + playsound(src,'sound/weapons/drill.ogg',40,TRUE) + else if(isobj(target)) + var/obj/O = target + if(istype(O, /obj/item/boulder)) + var/obj/item/boulder/nu_boulder = O + nu_boulder.manual_process(src, source) + else + O.take_damage(15, BRUTE, 0, FALSE, get_dir(chassis, target)) + playsound(src,'sound/weapons/drill.ogg', 40, TRUE) + + // If we caused a qdel drilling the target, we can stop drilling them. + // Prevents starting a do_after on a qdeleted target. + if(QDELETED(target)) + break return ..() diff --git a/config/config.txt b/config/config.txt index 4cad73b932e32..3d8a0b976a582 100644 --- a/config/config.txt +++ b/config/config.txt @@ -379,14 +379,14 @@ AUTOADMIN_RANK Game Master ## These trigger for any version below (non-inclusive) the given version, so 510 triggers on 509 or lower. ## These messages will be followed by one stating the clients current version and the required version for clarity. ## If CLIENT_WARN_POPUP is uncommented a popup window with the message will be displayed instead -#CLIENT_WARN_VERSION 511 -#CLIENT_WARN_BUILD 1421 +#CLIENT_WARN_VERSION 515 +#CLIENT_WARN_BUILD 1635 #CLIENT_WARN_POPUP -#CLIENT_WARN_MESSAGE Byond released 511 as the stable release. You can set the framerate your client runs at, which makes the game feel very different and cool. Shortly after its release we will end up using 511 client features and you will be forced to update. -CLIENT_ERROR_VERSION 511 +#CLIENT_WARN_MESSAGE Byond released 515 as the stable release. This comes bundled with a host of niceties, including image generation for UIs and :: operators. +CLIENT_ERROR_VERSION 515 CLIENT_ERROR_MESSAGE Your version of byond is not supported. Please upgrade. -## The minimum build needed for joining the server, if using 512, a good minimum build would be 1421 as that disables the Middle Mouse Button exploit. -CLIENT_ERROR_BUILD 1421 +## The minimum build needed for joining the server. +CLIENT_ERROR_BUILD 1590 ## TOPIC RATE LIMITING ## This allows you to limit how many topic calls (clicking on an interface window) the client can do in any given game second and/or game minute. diff --git a/dependencies.sh b/dependencies.sh index 22c531441e9d6..d350e833c372c 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -5,15 +5,16 @@ # byond version export BYOND_MAJOR=515 -export BYOND_MINOR=1633 +export BYOND_MINOR=1637 #rust_g git tag export RUST_G_VERSION=3.1.0 export RUST_G_VERSION_SS220=3.0.0-ss220 #node version -export NODE_VERSION=20 -export NODE_VERSION_LTS=20.12.0 +export NODE_VERSION_LTS=20.13.0 +# compatiblility mode MUST work with windows 7 +export NODE_VERSION_COMPAT=20.2.0 # SpacemanDMM git tag export SPACEMAN_DMM_VERSION=suite-1.8 diff --git a/html/changelogs/AutoChangeLog-pr-83038.yml b/html/changelogs/AutoChangeLog-pr-83038.yml deleted file mode 100644 index fa026276635e7..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-83038.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "CandleJaxx" -delete-after: True -changes: - - rscadd: "'puppy' 'kitten' and 'spider' pai skins" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83153.yml b/html/changelogs/AutoChangeLog-pr-83153.yml new file mode 100644 index 0000000000000..6407279c802ac --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-83153.yml @@ -0,0 +1,4 @@ +author: "Higgin" +delete-after: True +changes: + - bugfix: "Spies no longer get the 'nothing' / 'free objective' due to trying to make Protect objectives without targets." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-83169.yml b/html/changelogs/AutoChangeLog-pr-83169.yml new file mode 100644 index 0000000000000..affae3ed0c056 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-83169.yml @@ -0,0 +1,4 @@ +author: "Xander3359" +delete-after: True +changes: + - bugfix: "Ale now has a drinking_glass style" \ No newline at end of file diff --git a/html/changelogs/archive/2024-05.yml b/html/changelogs/archive/2024-05.yml index 249d2e84cee3d..cd1dc769e1af4 100644 --- a/html/changelogs/archive/2024-05.yml +++ b/html/changelogs/archive/2024-05.yml @@ -144,3 +144,120 @@ - bugfix: fixes traditional equipment crate name paganiy: - qol: Admin modsuit now has a radiation protect module +2024-05-07: + CandleJaxx: + - rscadd: '''puppy'' ''kitten'' and ''spider'' pai skins' +2024-05-08: + '@MrEmre12, @Majkl-J': + - rscadd: Fishing pool introduced to metastation garden + - refactor: All water tiles now handle fishing through a unified fishing_datum variable + BurgerBB: + - qol: The inspector's Fedora now uses regex. When saying commands, it is much more + generous on picking up trigger word. + Derpguy3: + - bugfix: A meter attached to distribution pipes in Birdshot's atmospherics has + been moved to the matching pipe layer. + Fikou: + - rscdel: You can no longer use another station's blueprint photo to look at wires + or do the traitor objective with. + Iamgoofball: + - balance: Spies can now use other spies' uplinks. + Melbert: + - balance: To see wires in photos of blueprints, you first must squint at the photo. + ReturnToZender (hitting delete on a power cable): + - rscdel: Single loose power cable in icebox maintenance + Rhials: + - balance: Virtual domain ghost roles can no longer enter the safehouse/"equipment" + areas of a domain. + - bugfix: Pirate virtual domain ghost roles will no longer make a pirate team antag + datum. + ShizCalev: + - bugfix: Inducers no longer break completely after trying to charge a PDA with + them. + delingar: + - rscadd: RD's skillchip adds cyborg wires knowledge + jlsnow301: + - bugfix: Lootpanel now requires 515.1635 to generate most icons. TG support for + 514 ended May 1. Update your client to fix the icons. + mc-oofert: + - qol: temporarily soulless (deathmatch, etc) bodies dont appear as soulless on + medhud +2024-05-09: + 00-Steven: + - bugfix: Pride pins can be reskinned again with alt-click. + AMyriad: + - bugfix: Removed stray pixels appearing on certain parts of railing + Bisar: + - balance: Knockdown effects will know consistently disarm, instead of failing to + disarm if you're already lying down. + Ghommie: + - bugfix: Added the missing bulwark MOD module and the jawed fishing hook to the + black market. + GoldenAlpharex: + - bugfix: Removed another rogue cable from underneath a disconnected SMES. + Jane: + - qol: The Coroner's Ritual Knife can now sit upon Med Belts! + Melbert: + - bugfix: DNA infusing tiers works again + - bugfix: Cult can build again + - qol: There is now a slight animation to entering a portal or teleporter. + - qol: Spies may spawn in less numbers, but rarely may also spawn in more numbers. + XElectricX: + - qol: Mech drills can auto-mine by walking into rock. + jlsnow301: + - bugfix: Silicons can set the teleporter destinations again. + - bugfix: The quick equip 'E' hotkey shouldn't warn if one of your bags is full + anymore +2024-05-10: + 00-Steven: + - qol: Added alt-click usage context to toggle_icon component + - bugfix: The stomach pump surgery actually works again. + - spellcheck: '"brusing" to "bruising" in the message you get when failing the stomach + pump surgery.' + Echriser: + - bugfix: conveyor switches work for cyborgs again + GoldenAlpharex for the code, Thlumyn for the sprites: + - bugfix: Fixed smoothing turfs displaying some incorrect damaged overlays because + of a non-smoothing damage overlay of different dimensions than the smoothing + sprite used as the base. Less black void, let's go! + - image: Added smooth broken/burnt overlays for grass, meaning that it's going to + look a lot less broken when the grass tile is at the edge of a smoothing group. + Jacquerel: + - rscdel: Being sacrified by a Heretic no longer gives you an incurable phobia. + - rscadd: Being sacrificed by a Heretic will drop 2-4 of your organs on the ground + and replace them with "corrupt organs" with negative effects which can be suppressed + with Holy Water. + - rscadd: Players who have been sacrificed by Heretics will experience additional + and rapidly lethal consequences for attempting to fight someone who previously + sacrificed them, as long as that person is wearing a focus. + - sound: Lasers adjust their pitch as they run out of charge, rather than frequency + Melbert: + - qol: Morgue trays (and the contents inside) are now animated on open and close + MrStonedOne & Lilah Novi: + - rscadd: Say commands typed in the command bar now trigger typing indicators + Pickle-Coding: + - qol: The labor camp shuttle properly sets people to parole after they complete + their work. + - qol: The labor camp shuttle specifies which person returned to the station. + ShizCalev: + - bugfix: Cursed Items wizard event actually works again. + TiviPlus: + - bugfix: fixed clients being seen as stuck doing tutorials in some cases when changing + mobs + jlsnow301: + - bugfix: Supermatter shards now have screentips with a wrench in hand + malton33: + - bugfix: space heaters now display the correct mode when the maintenance panel + is closed + paganiy: + - bugfix: The shuttle will no longer delete you while you are in jaunt + vinylspiders: + - bugfix: moths with functional/flight potion wings get an animation when they *flap + once again, and it makes a sound +2024-05-11: + carlarctg: + - rscadd: Chance to become crab on nonlethal DNA meltdown + norsvenska: + - bugfix: The Coroner and Bitrunner can now be selected as a target for kidnapping + and heirloom destruction objectives. + - bugfix: The Chief Engineer is now a valid target for heirloom destruction objectives. diff --git a/html/typing_indicator.html b/html/typing_indicator.html new file mode 100644 index 0000000000000..2988edff55fa0 --- /dev/null +++ b/html/typing_indicator.html @@ -0,0 +1,46 @@ + + + + + + + + + diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi index 08df14c0ffc99..ddd6fd6f608bd 100644 Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi index 30469c85dae02..4fc02eaa2c0fc 100644 Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ diff --git a/icons/mob/clothing/belt_mirror.dmi b/icons/mob/clothing/belt_mirror.dmi index 37658b154dbcb..0d44e0ce6ea0c 100644 Binary files a/icons/mob/clothing/belt_mirror.dmi and b/icons/mob/clothing/belt_mirror.dmi differ diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi index 4c1acc9fa46f0..c1bcd197278b5 100644 Binary files a/icons/mob/inhands/weapons/guns_lefthand.dmi and b/icons/mob/inhands/weapons/guns_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi index f3a5f34e24723..98788d7371135 100644 Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ diff --git a/icons/obj/railings.dmi b/icons/obj/railings.dmi index 7dcb4e7c6f7d7..49b560d2c8707 100644 Binary files a/icons/obj/railings.dmi and b/icons/obj/railings.dmi differ diff --git a/icons/obj/weapons/guns/projectiles.dmi b/icons/obj/weapons/guns/projectiles.dmi index b2579d58bf2df..b4056f661b8d3 100644 Binary files a/icons/obj/weapons/guns/projectiles.dmi and b/icons/obj/weapons/guns/projectiles.dmi differ diff --git a/icons/obj/weapons/guns/wide_guns.dmi b/icons/obj/weapons/guns/wide_guns.dmi index c05453c3c25cf..568a3bc051f40 100644 Binary files a/icons/obj/weapons/guns/wide_guns.dmi and b/icons/obj/weapons/guns/wide_guns.dmi differ diff --git a/icons/turf/floors/grass_damaged.dmi b/icons/turf/floors/grass_damaged.dmi new file mode 100644 index 0000000000000..73540b3ca6a4c Binary files /dev/null and b/icons/turf/floors/grass_damaged.dmi differ diff --git a/icons/turf/floors/grass_damaged.png b/icons/turf/floors/grass_damaged.png new file mode 100644 index 0000000000000..4123f91cabe7d Binary files /dev/null and b/icons/turf/floors/grass_damaged.png differ diff --git a/icons/turf/floors/grass_damaged.png.toml b/icons/turf/floors/grass_damaged.png.toml new file mode 100644 index 0000000000000..18341264515ee --- /dev/null +++ b/icons/turf/floors/grass_damaged.png.toml @@ -0,0 +1,14 @@ +output_name = "grass_damaged" +template = "bitmask/diagonal_32x32.toml" + +[icon_size] +x = 50 +y = 50 + +[output_icon_size] +x = 50 +y = 50 + +[cut_pos] +x = 25 +y = 25 \ No newline at end of file diff --git a/interface/skin.dmf b/interface/skin.dmf index b3e581d344f52..3bfab58ea52a9 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -94,6 +94,15 @@ window "mainwindow" anchor2 = -1,-1 is-visible = false saved-params = "" + elem "commandbar_spy" + type = BROWSER + is-default = false + pos = 0,0 + size = 200x200 + anchor1 = -1,-1 + anchor2 = -1,-1 + is-visible = false + saved-params = "" window "mapwindow" elem "mapwindow" diff --git a/tgstation.dme b/tgstation.dme index cdab78a6ce609..abae16d7e065a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1393,6 +1393,7 @@ #include "code\datums\elements\connect_loc.dm" #include "code\datums\elements\consumable_mob.dm" #include "code\datums\elements\content_barfer.dm" +#include "code\datums\elements\corrupted_organ.dm" #include "code\datums\elements\crackable.dm" #include "code\datums\elements\crusher_loot.dm" #include "code\datums\elements\cuffsnapping.dm" @@ -3065,6 +3066,7 @@ #include "code\modules\antagonists\heretic\moon_lunatic.dm" #include "code\modules\antagonists\heretic\rust_effect.dm" #include "code\modules\antagonists\heretic\transmutation_rune.dm" +#include "code\modules\antagonists\heretic\items\corrupted_organs.dm" #include "code\modules\antagonists\heretic\items\eldritch_flask.dm" #include "code\modules\antagonists\heretic\items\eldritch_painting.dm" #include "code\modules\antagonists\heretic\items\forbidden_book.dm" @@ -3095,6 +3097,7 @@ #include "code\modules\antagonists\heretic\knowledge\starting_lore.dm" #include "code\modules\antagonists\heretic\knowledge\void_lore.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_buff.dm" +#include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_curse.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_knowledge.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_map.dm" #include "code\modules\antagonists\heretic\knowledge\sacrifice_knowledge\sacrifice_moodlets.dm" @@ -3460,16 +3463,19 @@ #include "code\modules\bitrunning\event.dm" #include "code\modules\bitrunning\job.dm" #include "code\modules\bitrunning\outfits.dm" +#include "code\modules\bitrunning\spawners.dm" #include "code\modules\bitrunning\turfs.dm" #include "code\modules\bitrunning\antagonists\_parent.dm" #include "code\modules\bitrunning\antagonists\cyber_police.dm" #include "code\modules\bitrunning\antagonists\cyber_tac.dm" +#include "code\modules\bitrunning\antagonists\ghost_role.dm" #include "code\modules\bitrunning\antagonists\netguardian.dm" #include "code\modules\bitrunning\components\avatar_connection.dm" #include "code\modules\bitrunning\components\bitrunning_points.dm" #include "code\modules\bitrunning\components\glitch.dm" #include "code\modules\bitrunning\components\netpod_healing.dm" #include "code\modules\bitrunning\components\npc_friendly.dm" +#include "code\modules\bitrunning\components\virtual_entity.dm" #include "code\modules\bitrunning\objects\byteforge.dm" #include "code\modules\bitrunning\objects\clothing.dm" #include "code\modules\bitrunning\objects\debug.dm" @@ -3726,6 +3732,7 @@ #include "code\modules\client\verbs\ooc.dm" #include "code\modules\client\verbs\ping.dm" #include "code\modules\client\verbs\suicide.dm" +#include "code\modules\client\verbs\typing.dm" #include "code\modules\client\verbs\who.dm" #include "code\modules\clothing\clothing.dm" #include "code\modules\clothing\belts\polymorph_belt.dm" @@ -4367,13 +4374,13 @@ #include "code\modules\library\random_books.dm" #include "code\modules\library\skill_learning\skill_station.dm" #include "code\modules\library\skill_learning\skillchip.dm" +#include "code\modules\library\skill_learning\generic_skillchips\rod_suplex.dm" #include "code\modules\library\skill_learning\job_skillchips\_job.dm" #include "code\modules\library\skill_learning\job_skillchips\chef.dm" #include "code\modules\library\skill_learning\job_skillchips\clown.dm" #include "code\modules\library\skill_learning\job_skillchips\janitor.dm" #include "code\modules\library\skill_learning\job_skillchips\miner.dm" #include "code\modules\library\skill_learning\job_skillchips\psychologist.dm" -#include "code\modules\library\skill_learning\job_skillchips\research_director.dm" #include "code\modules\library\skill_learning\job_skillchips\roboticist.dm" #include "code\modules\library\skill_learning\job_skillchips\station_engineer.dm" #include "code\modules\lighting\lighting_area.dm" @@ -5356,6 +5363,7 @@ #include "code\modules\projectiles\ammunition\_firing.dm" #include "code\modules\projectiles\ammunition\ballistic\foam.dm" #include "code\modules\projectiles\ammunition\ballistic\harpoon.dm" +#include "code\modules\projectiles\ammunition\ballistic\junk.dm" #include "code\modules\projectiles\ammunition\ballistic\lmg.dm" #include "code\modules\projectiles\ammunition\ballistic\pistol.dm" #include "code\modules\projectiles\ammunition\ballistic\revolver.dm" @@ -5439,6 +5447,7 @@ #include "code\modules\projectiles\projectile\bullets\dnainjector.dm" #include "code\modules\projectiles\projectile\bullets\foam_dart.dm" #include "code\modules\projectiles\projectile\bullets\grenade.dm" +#include "code\modules\projectiles\projectile\bullets\junk.dm" #include "code\modules\projectiles\projectile\bullets\lmg.dm" #include "code\modules\projectiles\projectile\bullets\pistol.dm" #include "code\modules\projectiles\projectile\bullets\revolver.dm" diff --git a/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx index 11e52b2ac5544..9a078f3f755ae 100644 --- a/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx +++ b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx @@ -16,6 +16,10 @@ export function IconDisplay(props: Props) { return fallback; } + if (icon === 'n/a') { + return ; + } + if (icon_state) { return ( { data.targetTemp + '°C'} - {(!data.open && 'Auto') || ( + {(!data.open && capitalize(data.mode)) || ( <>