diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 817626d228..643b4d6e64 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -414,10 +414,17 @@ Regarding sprites & sounds, you must credit the artist and possibly the codebase Do not add any of the following in a Pull Request or risk getting the PR closed: * National Socialist Party of Germany content, National Socialist Party of Germany related content, or National Socialist Party of Germany references * Code where one line of code is split across mutiple lines (except for multiple, separate strings and comments; in those cases, existing longer lines must not be split up) -* Code adding, removing, or updating the availability of alien races/species/human mutants without prior approval. Pull requests attempting to add or remove features from said races/species/mutants require prior approval as well. Just because something isn't on this list doesn't mean that it's acceptable. Use common sense above all else. +## Content requiring prior approval +Certain types of changes may require prior approval from maintainers. This currently includes: + +* Code adding, removing, or updating the availability of alien races/species/human mutants. This includes pull requests attempting to add or remove features from said races/species/mutants. (Requires approval from at least one maintainer) +* Code adding, removing, or modifying the functionality of adult-oriented features (such as, but not limited to: vore, genitals, MKUltra, and more). This also includes pull requests attempting to add or remove these features outright. (Requires approval from at least half of the formal maintainer team) + +The above content requires approval from the specified amount of maintainers prior to PR creation. Seeking approval must be done via a @Maintainer ping in a relevant development/code or staff channel on the Discord, otherwise it will be considered insufficient. If a PR contains any of the above content, but the creator does not have sufficient approval prior to the PR's creation, then the PR may be closed by any maintainer, at any time, for any reason. + ## A word on Git Yes, we know that the files have a tonne of mixed Windows and Linux line endings. Attempts to fix this have been met with less than stellar success, and as such we have decided to give up caring until there comes a time when it matters. diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm index 969acdd871..23abc4b731 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_mining_site.dmm @@ -16,7 +16,7 @@ /turf/open/floor/wood, /area/ruin/unpowered) "d" = ( -/turf/open/openspace/icemoon, +/turf/open/transparent/openspace/icemoon, /area/icemoon/surface/outdoors) "e" = ( /obj/item/clothing/suit/hooded/explorer, diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index a77fcf1bd5..264fdb1058 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -6471,6 +6471,12 @@ }, /turf/open/floor/plasteel/white, /area/science/xenobiology) +"alX" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/turf/open/floor/plasteel/white, +/area/medical/chemistry) "alY" = ( /obj/structure/cable{ icon_state = "1-2" @@ -8073,6 +8079,13 @@ }, /turf/open/floor/plasteel/showroomfloor, /area/security/warden) +"apI" = ( +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/structure/closet/secure_closet/medical1, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "apJ" = ( /turf/closed/wall, /area/construction/mining/aux_base) @@ -9385,6 +9398,13 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/construction/mining/aux_base) +"asG" = ( +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/machinery/disposal/bin, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) "asH" = ( /obj/structure/closet/toolcloset, /obj/effect/turf_decal/tile/yellow{ @@ -9428,6 +9448,35 @@ }, /turf/open/floor/plating, /area/maintenance/port/fore) +"asL" = ( +/obj/machinery/light{ + dir = 8 + }, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = -24 + }, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/crowbar, +/obj/item/clothing/neck/stethoscope, +/obj/item/storage/box/gloves{ + pixel_x = 3; + pixel_y = 4 + }, +/obj/item/storage/box/masks, +/obj/structure/table, +/turf/open/floor/plasteel/white, +/area/medical/sleeper) +"asM" = ( +/obj/structure/table, +/obj/item/crowbar, +/obj/item/clothing/neck/stethoscope, +/obj/item/reagent_containers/spray/cleaner, +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/turf/open/floor/plasteel/dark, +/area/medical/sleeper) "asN" = ( /obj/machinery/airalarm{ dir = 4; @@ -9457,12 +9506,66 @@ /obj/structure/closet/firecloset, /turf/open/floor/plating, /area/maintenance/fore) +"asT" = ( +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/medical/medbay/central) +"asU" = ( +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/yellow{ + dir = 4 + }, +/obj/machinery/door/airlock/medical/glass{ + id_tag = "MedbayFoyer"; + name = "Medbay"; + req_access_txt = "5" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) +"asV" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/plasteel, +/area/medical/medbay/central) "asW" = ( /obj/machinery/light/small{ dir = 8 }, /turf/open/floor/plasteel/grimy, /area/security/detectives_office) +"asX" = ( +/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ + dir = 4 + }, +/obj/machinery/door/airlock/medical/glass{ + id_tag = "MedbayFoyer"; + name = "Medbay"; + req_access_txt = "5" + }, +/turf/open/floor/plasteel/white, +/area/medical/medbay/central) +"asY" = ( +/obj/structure/sign/warning/nosmoking{ + pixel_y = 30 + }, +/turf/open/floor/plasteel, +/area/hallway/primary/central) "ata" = ( /turf/open/floor/wood, /area/lawoffice) @@ -24471,12 +24574,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/closed/wall/r_wall, /area/engine/gravity_generator) -"bgP" = ( -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/turf/open/floor/plasteel/white, -/area/medical/chemistry) "bgQ" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 @@ -28214,12 +28311,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bpy" = ( -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bpz" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 @@ -28708,13 +28799,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel, /area/hallway/primary/central) -"bqM" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/turf/open/floor/plating, -/area/medical/medbay/central) "bqN" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden{ dir = 5 @@ -28747,22 +28831,6 @@ }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bqR" = ( -/obj/structure/table, -/obj/item/crowbar, -/obj/item/clothing/neck/stethoscope, -/obj/item/reagent_containers/spray/cleaner, -/obj/structure/sign/warning/nosmoking{ - pixel_y = 30 - }, -/obj/machinery/atmospherics/pipe/simple/supply/hidden{ - dir = 4 - }, -/obj/effect/turf_decal/tile/yellow{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bqS" = ( /obj/structure/cable{ icon_state = "4-8" @@ -29265,13 +29333,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/crew_quarters/heads/hop) -"brV" = ( -/obj/effect/spawner/structure/window/reinforced, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/turf/open/floor/plating, -/area/medical/medbay/central) "brW" = ( /obj/structure/cable{ icon_state = "4-8" @@ -29279,18 +29340,6 @@ /obj/machinery/vending/cart, /turf/open/floor/plasteel, /area/crew_quarters/heads/hop) -"brX" = ( -/obj/structure/table, -/obj/item/storage/box/masks, -/obj/item/storage/box/gloves{ - pixel_x = 3; - pixel_y = 4 - }, -/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ - dir = 4 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "brY" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -31215,23 +31264,10 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel, /area/medical/medbay/central) -"bwy" = ( -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bwz" = ( /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel/white, /area/medical/medbay/central) -"bwA" = ( -/obj/machinery/disposal/bin, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/open/floor/plasteel/white, -/area/medical/medbay/central) "bwB" = ( /obj/structure/chair, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ @@ -32971,17 +33007,6 @@ }, /turf/open/floor/plating, /area/maintenance/port/aft) -"bAp" = ( -/obj/structure/closet/secure_closet/medical1, -/obj/machinery/light{ - dir = 8 - }, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = -24 - }, -/turf/open/floor/plasteel/white, -/area/medical/sleeper) "bAq" = ( /obj/machinery/sleeper{ dir = 8 @@ -55998,6 +56023,10 @@ }, /turf/open/floor/plasteel, /area/crew_quarters/theatre) +"kEm" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plating, +/area/maintenance/port/aft) "kEY" = ( /obj/effect/landmark/stationroom/box/engine, /turf/open/space/basic, @@ -58125,6 +58154,10 @@ /obj/effect/decal/cleanable/blood/old, /turf/open/floor/plating, /area/maintenance/fore) +"rtU" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard/fore) "ruo" = ( /obj/structure/cable{ icon_state = "4-8" @@ -85309,7 +85342,7 @@ bVI bTA xgk bHE -bHE +kEm bHE bHE bHE @@ -92215,7 +92248,7 @@ btJ buV bws bqH -aJq +asY aJq byW bCv @@ -94266,8 +94299,8 @@ bfF bfF bfF bfF -bqM -brV +asT +asV bof bwv bvj @@ -94523,8 +94556,8 @@ bmF bob bnB bfF -bqR -brX +asU +asX bof bwx bvj @@ -95031,7 +95064,7 @@ aYV bfF bhc bip -bgP +alX bjL bkL bmT @@ -95297,7 +95330,7 @@ bpF bqS brY bwz -bwy +asG bvj bza bxb @@ -95553,10 +95586,10 @@ bpt bfF bqV bEe +bhh bBL -bwA bvj -bAl +asM bAl bvh bzS @@ -96071,7 +96104,7 @@ bvh ajv bxN bze -bAp +asL bvh bCG bBd @@ -98119,7 +98152,7 @@ bjX blp bmO bhi -bpy +apI bwz brg btZ @@ -104254,7 +104287,7 @@ anf anf alP alP -anf +rtU alP alP alP diff --git a/_maps/map_files/CogStation/CogStation.dmm b/_maps/map_files/CogStation/CogStation.dmm index 182f1a4f27..8bf6d0e32b 100644 --- a/_maps/map_files/CogStation/CogStation.dmm +++ b/_maps/map_files/CogStation/CogStation.dmm @@ -242,13 +242,13 @@ pixel_x = 22 }, /obj/structure/rack, -/obj/item/gun/energy{ +/obj/item/gun/energy/e_gun{ pixel_y = -6 }, -/obj/item/gun/energy{ +/obj/item/gun/energy/e_gun{ pixel_y = -3 }, -/obj/item/gun/energy, +/obj/item/gun/energy/e_gun, /turf/open/floor/plasteel/dark, /area/ai_monitored/security/armory) "aaI" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 4a18b67e02..1b7b9c7414 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -126384,6 +126384,13 @@ }, /turf/open/space, /area/engine/atmos) +"kqo" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/port/fore) "ktK" = ( /obj/effect/turf_decal/tile/neutral{ dir = 4 @@ -127042,6 +127049,10 @@ dir = 1 }, /area/science/circuit) +"pok" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/grimy, +/area/crew_quarters/theatre/abandoned) "poI" = ( /obj/structure/bed, /obj/item/tank/internals/anesthetic, @@ -164027,7 +164038,7 @@ aof aky aky aky -arD +kqo asZ aui avF @@ -178786,7 +178797,7 @@ dCX dEj dFq dGJ -dGJ +pok dJy dGJ dGJ diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index fbe5b954ff..e848fd87d1 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -78498,6 +78498,7 @@ icon_state = "4-8" }, /obj/machinery/atmospherics/pipe/manifold/supply/hidden, +/mob/living/simple_animal/opossum/poppy, /turf/open/floor/plating{ icon_state = "platingdmg3" }, @@ -84902,6 +84903,11 @@ /obj/machinery/light, /turf/open/floor/plating/airless, /area/hallway/secondary/entry) +"jGN" = ( +/obj/effect/decal/cleanable/dirt, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard/aft) "jHJ" = ( /obj/machinery/atmospherics/pipe/simple/general/visible{ dir = 9 @@ -125028,7 +125034,7 @@ bAP bCf bhQ bhQ -bAM +jGN bGH bOc bHZ diff --git a/_maps/map_files/LambdaStation/lambda.dmm b/_maps/map_files/LambdaStation/lambda.dmm index 9000c90388..47d8dd9cb4 100644 --- a/_maps/map_files/LambdaStation/lambda.dmm +++ b/_maps/map_files/LambdaStation/lambda.dmm @@ -80285,6 +80285,11 @@ }, /turf/open/space/basic, /area/engine/engineering) +"eCO" = ( +/obj/effect/decal/cleanable/dirt, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/dark, +/area/maintenance/starboard/aft) "eDP" = ( /obj/effect/turf_decal/trimline/blue/filled/corner{ dir = 8 @@ -82038,6 +82043,10 @@ }, /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/kitchen) +"vDO" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/fore) "vDW" = ( /obj/structure/cable{ icon_state = "1-2" @@ -107578,7 +107587,7 @@ aaA abY ayy acw -acw +vDO acw acw aeG @@ -135197,7 +135206,7 @@ bPp ccJ clR bQh -clN +eCO bPp bQh bYx diff --git a/_maps/map_files/Mafia/mafia_ayylmao.dmm b/_maps/map_files/Mafia/mafia_ayylmao.dmm new file mode 100644 index 0000000000..5d6f2aec5f --- /dev/null +++ b/_maps/map_files/Mafia/mafia_ayylmao.dmm @@ -0,0 +1,768 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/closed/indestructible{ + desc = "A wall with alien alloy plating."; + icon = 'icons/turf/walls/abductor_wall.dmi'; + icon_state = "abductor"; + name = "alien wall"; + smooth = 5 + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/fakespace, +/area/mafia) +"j" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien22" + }, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/abductor, +/turf/open/floor/plating/abductor, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/obj/structure/bed/abductor, +/turf/open/floor/plating/abductor, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/obj/structure/bed/abductor, +/turf/open/floor/plating/abductor2, +/area/mafia) +"o" = ( +/obj/structure/closet/abductor, +/turf/open/floor/plating/abductor2, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plating/abductor, +/area/mafia) +"r" = ( +/turf/open/floor/plating/abductor2, +/area/mafia) +"t" = ( +/turf/open/floor/light/colour_cycle/dancefloor_a, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/light/colour_cycle/dancefloor_b, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"x" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/floor/plating/abductor2, +/area/mafia) +"z" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/fakespace, +/area/mafia) +"B" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien2" + }, +/area/mafia) +"D" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien15" + }, +/area/mafia) +"E" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien7" + }, +/area/mafia) +"F" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien12" + }, +/area/mafia) +"G" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien17" + }, +/area/mafia) +"H" = ( +/turf/open/space/basic, +/area/mafia) +"I" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien24" + }, +/area/mafia) +"J" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien18" + }, +/area/mafia) +"K" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien11" + }, +/area/mafia) +"L" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien14" + }, +/area/mafia) +"M" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien4" + }, +/area/mafia) +"N" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien21" + }, +/area/mafia) +"P" = ( +/turf/closed/indestructible/abductor, +/area/mafia) +"Q" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien5" + }, +/area/mafia) +"S" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien3" + }, +/area/mafia) +"U" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien23" + }, +/area/mafia) +"V" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien13" + }, +/area/mafia) +"W" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien20" + }, +/area/mafia) +"Y" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien8" + }, +/area/mafia) +"Z" = ( +/turf/closed/indestructible/abductor{ + icon_state = "alien10" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +b +a +"} +(4,1,1) = {" +a +b +i +i +g +z +i +i +i +i +i +i +i +i +i +H +i +z +k +i +i +b +a +"} +(5,1,1) = {" +a +b +i +i +z +i +i +i +W +L +F +F +F +Z +P +i +i +i +h +i +i +b +a +"} +(6,1,1) = {" +a +b +i +i +i +W +L +Z +G +l +q +b +r +o +E +L +Z +P +i +i +i +b +a +"} +(7,1,1) = {" +a +b +i +i +i +N +o +r +p +m +q +x +r +n +x +q +l +B +i +i +i +b +a +"} +(8,1,1) = {" +a +b +i +i +i +U +n +r +b +b +q +b +r +b +b +q +m +M +i +i +i +b +a +"} +(9,1,1) = {" +a +b +i +i +W +G +x +r +r +x +q +x +r +x +q +q +x +E +P +i +i +b +a +"} +(10,1,1) = {" +a +b +i +i +N +l +x +b +x +x +x +x +x +x +x +b +x +o +B +i +i +b +a +"} +(11,1,1) = {" +a +b +i +i +j +m +q +q +q +x +t +t +t +x +r +r +r +n +S +i +i +b +a +"} +(12,1,1) = {" +a +b +i +i +j +b +x +b +x +x +t +u +t +x +x +b +x +b +S +i +i +b +a +"} +(13,1,1) = {" +a +b +i +i +j +n +r +r +r +x +t +t +t +x +q +q +q +m +S +i +i +b +a +"} +(14,1,1) = {" +a +b +i +i +U +o +x +b +x +x +x +x +x +x +x +b +x +l +M +i +i +b +a +"} +(15,1,1) = {" +a +b +i +i +I +J +x +q +q +x +r +x +q +x +r +r +x +Y +Q +i +i +b +a +"} +(16,1,1) = {" +a +b +i +i +i +N +m +q +b +b +r +b +q +b +b +r +n +B +i +i +i +b +a +"} +(17,1,1) = {" +a +b +i +i +i +U +l +q +x +n +r +x +q +m +x +r +o +M +i +i +i +b +a +"} +(18,1,1) = {" +a +b +i +i +i +I +D +K +J +o +r +b +q +l +Y +D +K +Q +i +i +i +b +a +"} +(19,1,1) = {" +a +b +i +i +z +i +i +i +I +D +V +V +V +K +Q +i +i +i +z +i +i +b +a +"} +(20,1,1) = {" +a +b +i +i +k +z +i +i +i +i +i +i +i +i +i +i +i +z +v +i +i +b +a +"} +(21,1,1) = {" +a +b +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +i +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_ball.dmm b/_maps/map_files/Mafia/mafia_ball.dmm new file mode 100644 index 0000000000..6ca955726b --- /dev/null +++ b/_maps/map_files/Mafia/mafia_ball.dmm @@ -0,0 +1,675 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/dark, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel, +/area/mafia) +"o" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/turf/open/floor/fakespace, +/area/mafia) +"t" = ( +/turf/open/floor/plasteel/solarpanel, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/plasteel/solarpanel, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(3,1,1) = {" +a +a +a +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +a +a +a +"} +(4,1,1) = {" +a +a +a +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +a +a +a +"} +(5,1,1) = {" +a +a +a +c +h +d +d +e +a +a +j +a +j +a +a +d +d +i +h +c +a +a +a +"} +(6,1,1) = {" +a +a +a +c +i +a +a +j +a +l +q +a +r +o +a +j +a +a +d +c +a +a +a +"} +(7,1,1) = {" +a +a +c +c +d +a +o +r +p +m +q +p +r +n +p +q +l +a +d +c +c +a +a +"} +(8,1,1) = {" +a +a +c +d +d +a +n +r +a +a +q +a +r +a +a +q +m +a +d +d +c +a +a +"} +(9,1,1) = {" +a +a +c +d +a +a +p +r +r +p +q +p +r +p +q +q +p +a +a +d +c +a +a +"} +(10,1,1) = {" +a +a +c +d +a +l +p +a +p +p +p +p +p +p +p +a +p +o +a +d +c +a +a +"} +(11,1,1) = {" +a +a +c +d +j +m +q +q +q +p +s +t +s +p +r +r +r +n +j +e +c +a +a +"} +(12,1,1) = {" +a +a +c +e +a +a +p +a +p +p +t +u +t +p +p +a +p +a +a +d +c +a +a +"} +(13,1,1) = {" +a +a +c +d +j +n +r +r +r +p +s +t +s +p +q +q +q +m +j +d +c +a +a +"} +(14,1,1) = {" +a +a +c +d +a +o +p +a +p +p +p +p +p +p +p +a +p +l +a +i +c +a +a +"} +(15,1,1) = {" +a +a +c +d +a +a +p +q +q +p +r +p +q +p +r +r +p +a +a +d +c +a +a +"} +(16,1,1) = {" +a +a +c +f +d +a +m +q +a +a +r +a +q +a +a +r +n +a +d +d +c +a +a +"} +(17,1,1) = {" +a +a +c +c +d +a +l +q +p +n +r +p +q +m +p +r +o +a +d +c +c +a +a +"} +(18,1,1) = {" +a +a +a +c +d +a +a +j +a +o +r +a +q +l +a +j +a +a +d +c +a +a +a +"} +(19,1,1) = {" +a +a +a +c +h +i +d +d +a +a +j +a +j +a +a +d +e +d +h +c +a +a +a +"} +(20,1,1) = {" +a +a +a +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +a +a +a +"} +(21,1,1) = {" +a +a +a +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +a +a +a +"} +(22,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_gothic.dmm b/_maps/map_files/Mafia/mafia_gothic.dmm new file mode 100644 index 0000000000..a3c19fee85 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_gothic.dmm @@ -0,0 +1,724 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/closed/wall/mineral/iron, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/turf/closed/indestructible/fakedoor, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/carpet/red, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/carpet/red, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) +"o" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/space/basic, +/area/mafia) +"q" = ( +/turf/open/floor/carpet/red, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"t" = ( +/turf/open/floor/carpet/royalblack, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/carpet/royalblack, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"D" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"E" = ( +/turf/open/floor/plasteel/chapel{ + dir = 4 + }, +/area/mafia) +"H" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/turf/open/floor/carpet/red, +/area/mafia) +"J" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) +"M" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"N" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/chapel, +/area/mafia) +"O" = ( +/turf/open/floor/plasteel/chapel, +/area/mafia) +"P" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel, +/area/mafia) +"Q" = ( +/obj/structure/closet/crate/coffin, +/turf/open/floor/plasteel/chapel{ + dir = 8 + }, +/area/mafia) +"X" = ( +/turf/open/floor/plasteel/chapel{ + dir = 1 + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +b +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +b +b +a +"} +(4,1,1) = {" +a +b +b +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +b +b +a +"} +(5,1,1) = {" +a +b +b +c +h +d +d +e +b +b +j +b +j +b +b +d +d +i +h +c +b +b +a +"} +(6,1,1) = {" +a +b +b +c +i +b +b +j +b +l +q +b +X +Q +b +j +b +b +d +c +b +b +a +"} +(7,1,1) = {" +a +b +c +c +d +b +J +r +p +m +q +p +E +N +p +q +l +b +d +c +c +b +a +"} +(8,1,1) = {" +a +b +c +d +d +b +D +O +b +b +q +b +r +b +b +q +m +b +d +d +c +b +a +"} +(9,1,1) = {" +a +b +c +d +b +b +p +X +r +p +q +p +O +p +q +q +p +b +b +d +c +b +a +"} +(10,1,1) = {" +a +b +c +d +b +l +p +b +p +p +p +p +p +p +p +b +p +Q +b +d +c +b +a +"} +(11,1,1) = {" +a +b +c +d +j +m +q +q +q +p +t +t +t +p +X +r +E +N +j +e +c +b +a +"} +(12,1,1) = {" +a +b +c +e +b +b +p +b +p +p +t +u +t +p +p +b +p +b +b +d +c +b +a +"} +(13,1,1) = {" +a +b +c +d +j +n +r +E +O +p +t +t +t +p +q +q +q +m +j +d +c +b +a +"} +(14,1,1) = {" +a +b +c +d +b +o +p +b +p +p +p +p +p +p +p +b +p +l +b +i +c +b +a +"} +(15,1,1) = {" +a +b +c +d +b +b +p +q +q +p +X +p +q +p +E +O +p +b +b +d +c +b +a +"} +(16,1,1) = {" +a +b +c +f +d +b +m +q +b +b +E +b +q +b +b +X +M +b +d +d +c +b +a +"} +(17,1,1) = {" +a +b +c +c +d +b +l +q +p +n +r +p +q +m +p +E +P +b +d +c +c +b +a +"} +(18,1,1) = {" +a +b +b +c +d +b +b +j +b +o +O +b +q +H +b +j +b +b +d +c +b +b +a +"} +(19,1,1) = {" +a +b +b +c +h +i +d +d +b +b +j +b +j +b +b +d +e +d +h +c +b +b +a +"} +(20,1,1) = {" +a +b +b +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +b +b +a +"} +(21,1,1) = {" +a +b +b +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +b +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_lavaland.dmm b/_maps/map_files/Mafia/mafia_lavaland.dmm new file mode 100644 index 0000000000..8c58fb9054 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_lavaland.dmm @@ -0,0 +1,976 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner, +/turf/open/floor/plasteel, +/area/mafia) +"o" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 4 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/grass/fakebasalt, +/area/mafia) +"r" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/turf/open/floor/plasteel, +/area/mafia) +"t" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"u" = ( +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red, +/turf/open/floor/plasteel/dark, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"w" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner, +/turf/open/floor/plasteel, +/area/mafia) +"x" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/line, +/turf/open/floor/plasteel, +/area/mafia) +"y" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"z" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"A" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/turf/open/floor/plasteel, +/area/mafia) +"B" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"C" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 4 + }, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"D" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"E" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"F" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"G" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/turf/open/floor/plasteel, +/area/mafia) +"H" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"I" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"J" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"K" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 8 + }, +/obj/structure/closet{ + desc = "It's a storage unit. For mining stuff. Y'know."; + icon_state = "mining"; + name = "miner equipment locker" + }, +/obj/item/clothing/under/rank/cargo/miner/lavaland, +/turf/open/floor/plasteel, +/area/mafia) +"L" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"M" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 10 + }, +/turf/open/floor/plasteel, +/area/mafia) +"N" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 4 + }, +/turf/open/floor/plasteel, +/area/mafia) +"O" = ( +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 8 + }, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 5 + }, +/turf/open/floor/plasteel, +/area/mafia) +"P" = ( +/obj/effect/turf_decal/trimline/brown/filled/end, +/turf/open/floor/plasteel, +/area/mafia) +"Q" = ( +/obj/effect/landmark/mafia, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 9 + }, +/turf/open/floor/plasteel, +/area/mafia) +"R" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"S" = ( +/obj/effect/turf_decal/trimline/brown/filled/end{ + icon_state = "trimline_end_fill"; + dir = 8 + }, +/turf/open/floor/plasteel, +/area/mafia) +"T" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"U" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/effect/turf_decal/trimline/brown/filled/corner{ + icon_state = "trimline_corner_fill"; + dir = 1 + }, +/turf/open/floor/plasteel, +/area/mafia) +"V" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + icon_state = "trimline_fill"; + dir = 6 + }, +/turf/open/floor/plasteel, +/area/mafia) +"W" = ( +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"X" = ( +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"Y" = ( +/turf/closed/indestructible{ + icon = 'icons/turf/walls/reinforced_wall.dmi'; + icon_state = "r_wall"; + name = "reinforced wall" + }, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +a +"} +(3,1,1) = {" +a +Y +Y +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +Y +Y +a +"} +(4,1,1) = {" +a +Y +Y +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +Y +Y +a +"} +(5,1,1) = {" +a +Y +Y +c +h +d +d +e +Y +Y +j +Y +j +Y +Y +d +d +i +h +c +Y +Y +a +"} +(6,1,1) = {" +a +Y +Y +c +i +Y +Y +j +Y +l +q +Y +s +H +Y +j +Y +Y +d +c +Y +Y +a +"} +(7,1,1) = {" +a +Y +c +c +d +Y +r +M +p +m +q +p +w +A +p +q +l +Y +d +c +c +Y +a +"} +(8,1,1) = {" +a +Y +c +d +d +Y +L +N +Y +Y +q +Y +x +Y +Y +q +m +Y +d +d +c +Y +a +"} +(9,1,1) = {" +a +Y +c +d +Y +Y +p +O +P +p +q +p +y +p +q +q +p +Y +Y +d +c +Y +a +"} +(10,1,1) = {" +a +Y +c +d +Y +l +p +Y +p +p +p +p +p +p +p +Y +p +K +Y +d +c +Y +a +"} +(11,1,1) = {" +a +Y +c +d +j +m +q +q +q +p +q +u +q +p +B +C +C +J +j +e +c +Y +a +"} +(12,1,1) = {" +a +Y +c +e +Y +Y +p +Y +p +p +t +R +X +p +p +Y +p +Y +Y +d +c +Y +a +"} +(13,1,1) = {" +a +Y +c +d +j +n +C +C +P +p +q +W +q +p +q +q +q +m +j +d +c +Y +a +"} +(14,1,1) = {" +a +Y +c +d +Y +o +p +Y +p +p +p +p +p +p +p +Y +p +l +Y +i +c +Y +a +"} +(15,1,1) = {" +a +Y +c +d +Y +Y +p +q +q +p +S +p +q +p +B +D +p +Y +Y +d +c +Y +a +"} +(16,1,1) = {" +a +Y +c +f +d +Y +m +q +Y +Y +T +Y +q +Y +Y +E +G +Y +d +d +c +Y +a +"} +(17,1,1) = {" +a +Y +c +c +d +Y +l +q +p +Q +U +p +q +m +p +F +I +Y +d +c +c +Y +a +"} +(18,1,1) = {" +a +Y +Y +c +d +Y +Y +j +Y +z +V +Y +q +l +Y +j +Y +Y +d +c +Y +Y +a +"} +(19,1,1) = {" +a +Y +Y +c +h +i +d +d +Y +Y +j +Y +j +Y +Y +d +e +d +h +c +Y +Y +a +"} +(20,1,1) = {" +a +Y +Y +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +Y +Y +a +"} +(21,1,1) = {" +a +Y +Y +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +Y +Y +a +"} +(22,1,1) = {" +a +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +Y +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_reebe.dmm b/_maps/map_files/Mafia/mafia_reebe.dmm new file mode 100644 index 0000000000..e82174ff93 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_reebe.dmm @@ -0,0 +1,658 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/turf/open/indestructible/reebe_void, +/area/mafia) +"c" = ( +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) +"d" = ( +/obj/mafia_game_board, +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) +"e" = ( +/turf/closed/wall/clockwork, +/area/mafia) +"f" = ( +/turf/open/floor/clockwork/reebe, +/area/mafia) +"g" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"h" = ( +/obj/structure/window/reinforced/clockwork/fulltile, +/obj/structure/grille/ratvar, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"i" = ( +/obj/structure/bed, +/obj/item/bedsheet/orange, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"j" = ( +/obj/structure/chair/brass{ + dir = 4 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"k" = ( +/obj/structure/chair/brass, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"l" = ( +/obj/structure/chair/brass{ + dir = 8 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"m" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/structure/chair/brass, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"n" = ( +/obj/structure/chair/brass{ + icon_state = "brass_chair"; + dir = 1 + }, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"o" = ( +/obj/item/toy/plush/plushvar, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"p" = ( +/obj/structure/window/reinforced/clockwork/fulltile, +/obj/structure/grille/ratvar, +/turf/open/floor/clockwork/reebe, +/area/mafia) +"q" = ( +/obj/item/toy/plush/plushvar, +/turf/open/indestructible/reebe_void/spawning/lattices, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(3,1,1) = {" +a +b +b +c +c +c +b +b +b +b +b +b +b +b +b +b +b +c +c +c +b +b +a +"} +(4,1,1) = {" +a +b +b +c +d +c +b +b +b +b +b +b +b +b +b +b +b +q +d +c +b +b +a +"} +(5,1,1) = {" +a +b +b +c +c +c +c +b +e +e +p +e +p +e +e +b +b +c +c +c +b +b +a +"} +(6,1,1) = {" +a +b +b +b +b +p +p +e +e +i +f +e +f +i +e +e +p +p +c +b +b +b +a +"} +(7,1,1) = {" +a +b +b +b +b +p +i +f +h +g +f +h +f +g +h +f +i +p +b +b +b +b +a +"} +(8,1,1) = {" +a +b +b +b +b +e +g +f +e +e +f +e +f +e +e +f +g +e +b +b +b +b +a +"} +(9,1,1) = {" +a +b +b +b +e +e +h +f +j +h +j +h +j +h +j +f +h +e +e +b +b +b +a +"} +(10,1,1) = {" +a +b +b +b +e +i +h +e +h +h +h +h +h +h +h +e +h +i +e +b +b +b +a +"} +(11,1,1) = {" +a +b +b +b +p +g +f +f +k +h +f +f +o +h +n +f +f +g +p +b +b +b +a +"} +(12,1,1) = {" +a +b +b +b +e +e +h +e +h +h +f +m +f +h +h +e +h +e +e +b +b +b +a +"} +(13,1,1) = {" +a +b +b +b +p +g +f +f +k +h +f +f +f +h +n +f +f +g +p +b +b +b +a +"} +(14,1,1) = {" +a +b +b +b +e +i +h +e +h +h +h +h +h +h +h +e +h +i +e +b +b +b +a +"} +(15,1,1) = {" +a +b +b +b +e +e +h +f +l +h +l +h +l +h +l +f +h +e +e +b +b +b +a +"} +(16,1,1) = {" +a +b +b +b +b +e +g +f +e +e +f +e +f +e +e +f +g +e +b +b +b +b +a +"} +(17,1,1) = {" +a +b +b +b +b +p +i +f +h +g +f +h +f +g +h +f +i +p +b +b +b +b +a +"} +(18,1,1) = {" +a +b +b +b +b +p +p +e +e +f +i +e +i +f +e +e +p +p +c +b +b +b +a +"} +(19,1,1) = {" +a +b +b +c +c +c +c +b +e +e +p +e +p +e +e +b +b +c +c +c +b +b +a +"} +(20,1,1) = {" +a +b +b +c +d +c +b +b +b +b +b +b +b +b +b +b +b +c +d +c +b +b +a +"} +(21,1,1) = {" +a +b +b +c +c +c +b +b +b +b +b +b +b +b +b +b +b +c +c +c +b +b +a +"} +(22,1,1) = {" +a +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +b +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_snow.dmm b/_maps/map_files/Mafia/mafia_snow.dmm new file mode 100644 index 0000000000..a47440f00e --- /dev/null +++ b/_maps/map_files/Mafia/mafia_snow.dmm @@ -0,0 +1,755 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/item/bedsheet/brown, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/holofloor/wood, +/area/mafia) +"n" = ( +/obj/item/bedsheet/green, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"o" = ( +/obj/item/bedsheet/blue, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass, +/area/mafia) +"q" = ( +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/plasteel, +/area/mafia) +"s" = ( +/turf/closed/wall/ice, +/area/mafia) +"t" = ( +/turf/open/floor/holofloor/snow, +/area/mafia) +"u" = ( +/obj/structure/statue/plasma/scientist{ + anchored = 1 + }, +/turf/open/floor/holofloor/snow, +/area/mafia) +"v" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/holofloor/snow, +/area/mafia) +"w" = ( +/turf/open/lava/plasma, +/area/mafia) +"x" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 9999; + opacity = 0 + }, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plasteel/dark, +/area/mafia) +"y" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/holofloor/snow, +/area/mafia) +"z" = ( +/obj/structure/lattice/catwalk, +/turf/open/lava/plasma, +/area/mafia) +"A" = ( +/obj/machinery/door/airlock/external{ + max_integrity = 9999 + }, +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plasteel, +/area/mafia) +"B" = ( +/obj/effect/landmark/mafia, +/obj/structure/lattice/catwalk, +/turf/open/lava/plasma, +/area/mafia) +"C" = ( +/obj/mafia_game_board, +/turf/open/floor/holofloor/snow, +/area/mafia) +"D" = ( +/obj/structure/closet/crate/science, +/obj/item/clothing/suit/hooded/wintercoat, +/obj/item/clothing/shoes/winterboots, +/turf/open/floor/holofloor/snow, +/area/mafia) +"E" = ( +/obj/structure/lattice/catwalk, +/obj/structure/closet/crate/critter, +/obj/item/clothing/suit/hooded/wintercoat/miner, +/obj/item/clothing/shoes/winterboots, +/turf/open/lava/plasma, +/area/mafia) +"F" = ( +/turf/open/floor/holofloor/wood, +/area/mafia) +"G" = ( +/obj/item/bedsheet/orange, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"H" = ( +/obj/item/bedsheet/black, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"I" = ( +/obj/item/bedsheet/red, +/obj/structure/bed, +/turf/open/floor/holofloor/wood, +/area/mafia) +"J" = ( +/turf/open/floor/plating/snowed/smoothed, +/area/mafia) +"K" = ( +/turf/closed/indestructible/rock/snow, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +s +K +K +K +K +K +K +K +K +K +K +a +"} +(3,1,1) = {" +a +a +a +s +s +s +s +s +s +s +s +s +w +w +t +w +w +w +w +w +w +K +a +"} +(4,1,1) = {" +a +a +a +s +g +h +d +d +d +d +d +s +t +w +w +w +w +w +C +w +w +K +a +"} +(5,1,1) = {" +a +a +a +s +h +d +d +e +s +s +j +s +p +p +p +w +w +w +w +w +w +K +a +"} +(6,1,1) = {" +a +a +a +s +i +s +s +j +s +H +F +s +t +D +p +p +p +p +w +w +w +K +a +"} +(7,1,1) = {" +a +a +s +s +d +s +o +F +p +m +q +p +t +y +p +z +E +p +w +t +w +K +a +"} +(8,1,1) = {" +a +a +s +d +d +s +m +r +s +s +q +s +t +p +p +z +B +p +w +w +w +K +a +"} +(9,1,1) = {" +a +a +s +d +s +s +p +r +r +p +q +p +t +p +z +z +p +p +p +w +w +K +a +"} +(10,1,1) = {" +a +a +s +d +s +l +p +s +p +p +p +p +p +p +p +p +p +D +p +w +w +K +a +"} +(11,1,1) = {" +a +a +s +d +j +m +F +q +q +x +J +t +t +p +t +t +t +y +p +w +t +K +a +"} +(12,1,1) = {" +a +a +s +e +s +a +p +s +p +p +u +v +t +p +p +p +p +p +p +w +w +K +a +"} +(13,1,1) = {" +a +a +s +d +j +m +F +r +r +A +J +t +t +p +t +t +t +y +p +w +w +K +a +"} +(14,1,1) = {" +a +a +s +d +s +n +p +s +p +p +p +p +p +p +p +p +p +D +p +w +w +K +a +"} +(15,1,1) = {" +a +a +s +d +s +s +p +q +q +p +r +p +t +p +z +z +p +p +p +w +w +K +a +"} +(16,1,1) = {" +a +a +s +f +d +s +m +q +s +s +r +s +t +p +p +z +B +p +w +w +w +K +a +"} +(17,1,1) = {" +a +a +s +s +d +s +G +F +p +m +r +p +t +y +p +z +E +p +w +w +w +K +a +"} +(18,1,1) = {" +a +a +a +s +d +s +s +j +s +I +F +s +t +D +p +p +p +p +w +t +w +K +a +"} +(19,1,1) = {" +a +a +a +s +h +i +d +d +s +s +j +s +p +p +p +t +w +w +w +w +w +K +a +"} +(20,1,1) = {" +a +a +a +s +k +h +d +d +e +d +d +s +t +w +w +w +w +w +C +w +w +K +a +"} +(21,1,1) = {" +a +a +a +s +s +s +s +s +s +s +s +s +w +w +w +w +w +w +w +w +w +K +a +"} +(22,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +s +K +K +K +K +K +K +K +K +K +K +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_spiderclan.dmm b/_maps/map_files/Mafia/mafia_spiderclan.dmm new file mode 100644 index 0000000000..89771212b8 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_spiderclan.dmm @@ -0,0 +1,690 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/obj/item/reagent_containers/food/snacks/sashimi, +/turf/open/floor/carpet/black, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/turf/closed/wall/mineral/wood{ + desc = "A door that goes nowhere. How kafkaesque."; + icon = 'icons/obj/doors/mineral_doors.dmi'; + icon_state = "paperframe"; + name = "Door" + }, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"m" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/wood, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/carpet/black, +/area/mafia) +"o" = ( +/obj/structure/closet/cabinet{ + anchored = 1 + }, +/obj/item/reagent_containers/food/snacks/sashimi, +/turf/open/floor/wood, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/turf/closed/indestructible/fakeglass{ + icon = 'icons/obj/smooth_structures/paperframes.dmi'; + icon_state = "paper" + }, +/area/mafia) +"q" = ( +/turf/open/floor/wood, +/area/mafia) +"r" = ( +/turf/open/floor/carpet/black, +/area/mafia) +"t" = ( +/turf/open/floor/plating/beach/sand, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/turf/open/floor/plating/beach/sand, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"B" = ( +/obj/structure/showcase{ + density = 0; + desc = "Welp, only one way to recover your honour."; + icon = 'icons/obj/items_and_weapons.dmi'; + icon_state = "katana"; + name = "seppuku katana" + }, +/turf/open/floor/plating/beach/sand, +/area/mafia) +"S" = ( +/turf/closed/wall/mineral/wood, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +a +"} +(3,1,1) = {" +a +S +S +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +S +S +a +"} +(4,1,1) = {" +a +S +S +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +S +S +a +"} +(5,1,1) = {" +a +S +S +c +h +d +d +e +S +S +j +S +j +S +S +d +d +i +h +c +S +S +a +"} +(6,1,1) = {" +a +S +S +c +i +S +S +j +S +o +q +S +r +b +S +j +S +S +d +c +S +S +a +"} +(7,1,1) = {" +a +S +c +c +d +S +b +r +p +m +q +p +r +n +p +q +o +S +d +c +c +S +a +"} +(8,1,1) = {" +a +S +c +d +d +S +n +r +S +S +q +S +r +S +S +q +m +S +d +d +c +S +a +"} +(9,1,1) = {" +a +S +c +d +S +S +p +r +r +p +q +p +r +p +q +q +p +S +S +d +c +S +a +"} +(10,1,1) = {" +a +S +c +d +S +o +p +S +p +p +p +p +p +p +p +S +p +b +S +d +c +S +a +"} +(11,1,1) = {" +a +S +c +d +j +m +q +q +q +p +t +t +t +p +r +r +r +n +j +e +c +S +a +"} +(12,1,1) = {" +a +S +c +e +S +S +p +S +p +p +t +u +B +p +p +S +p +S +S +d +c +S +a +"} +(13,1,1) = {" +a +S +c +d +j +n +r +r +r +p +t +t +t +p +q +q +q +m +j +d +c +S +a +"} +(14,1,1) = {" +a +S +c +d +S +b +p +S +p +p +p +p +p +p +p +S +p +o +S +i +c +S +a +"} +(15,1,1) = {" +a +S +c +d +S +S +p +q +q +p +r +p +q +p +r +r +p +S +S +d +c +S +a +"} +(16,1,1) = {" +a +S +c +f +d +S +m +q +S +S +r +S +q +S +S +r +n +S +d +d +c +S +a +"} +(17,1,1) = {" +a +S +c +c +d +S +o +q +p +n +r +p +q +m +p +r +b +S +d +c +c +S +a +"} +(18,1,1) = {" +a +S +S +c +d +S +S +j +S +b +r +S +q +o +S +j +S +S +d +c +S +S +a +"} +(19,1,1) = {" +a +S +S +c +h +i +d +d +S +S +j +S +j +S +S +d +e +d +h +c +S +S +a +"} +(20,1,1) = {" +a +S +S +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +S +S +a +"} +(21,1,1) = {" +a +S +S +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +S +S +a +"} +(22,1,1) = {" +a +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +S +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/Mafia/mafia_syndie.dmm b/_maps/map_files/Mafia/mafia_syndie.dmm new file mode 100644 index 0000000000..d56c3cc0c6 --- /dev/null +++ b/_maps/map_files/Mafia/mafia_syndie.dmm @@ -0,0 +1,869 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/riveted, +/area/mafia) +"b" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"c" = ( +/turf/closed/wall/rust, +/area/mafia) +"d" = ( +/turf/open/floor/plating, +/area/mafia) +"e" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"f" = ( +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"g" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "platingdmg3" + }, +/area/mafia) +"h" = ( +/obj/structure/grille/indestructable, +/turf/open/floor/plating, +/area/mafia) +"i" = ( +/turf/open/floor/plating{ + icon_state = "platingdmg1" + }, +/area/mafia) +"j" = ( +/obj/machinery/door/airlock/maintenance_hatch{ + max_integrity = 99999; + name = "Maintenance" + }, +/obj/effect/mapping_helpers/airlock/locked, +/turf/open/floor/plating, +/area/mafia) +"k" = ( +/obj/mafia_game_board, +/turf/open/floor/plating, +/area/mafia) +"l" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"m" = ( +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"n" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"o" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"p" = ( +/obj/machinery/door/poddoor/preopen{ + desc = "When it's time to sleep, the lights will go out. Remember - no one in space can hear you scream."; + id = "mafia"; + max_integrity = 99999; + name = "Station Night Shutters" + }, +/obj/effect/spawner/structure/window/plastitanium, +/turf/open/space/basic, +/area/mafia) +"q" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 1; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"r" = ( +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"s" = ( +/turf/open/floor/mineral/plastitanium/red, +/area/mafia) +"t" = ( +/turf/open/floor/circuit/red, +/area/mafia) +"u" = ( +/obj/effect/baseturf_helper/asteroid, +/obj/effect/landmark/mafia/town_center, +/obj/structure/chair, +/turf/open/floor/circuit/red, +/area/mafia) +"v" = ( +/obj/mafia_game_board, +/turf/open/floor/plating{ + icon_state = "panelscorched" + }, +/area/mafia) +"w" = ( +/turf/closed/indestructible/syndicate, +/area/mafia) +"x" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 4; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"y" = ( +/obj/structure/chair/office{ + dir = 2; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"z" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 2; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"A" = ( +/obj/structure/chair/office{ + dir = 8; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"B" = ( +/obj/structure/chair/office{ + dir = 4; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"C" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/chair/office{ + dir = 8; + name = "tactical swivel chair" + }, +/turf/open/floor/plasteel/dark, +/area/mafia) +"E" = ( +/obj/structure/chair/office{ + dir = 1; + name = "tactical swivel chair" + }, +/turf/open/floor/mineral/plastitanium, +/area/mafia) +"F" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/plasteel/dark, +/area/mafia) +"G" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/item/clothing/under/syndicate/tacticool, +/obj/structure/closet/syndicate{ + desc = "A storage closet for syndicate conflict resolution operatives."; + name = "red closet" + }, +/obj/item/clothing/under/syndicate/tacticool/skirt, +/turf/open/floor/plasteel/dark, +/area/mafia) +"H" = ( +/obj/effect/turf_decal/tile/red, +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red{ + dir = 1 + }, +/obj/effect/landmark/mafia, +/turf/open/floor/plasteel/dark, +/area/mafia) + +(1,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} +(2,1,1) = {" +a +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +a +"} +(3,1,1) = {" +a +w +w +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +w +w +a +"} +(4,1,1) = {" +a +w +w +c +g +h +d +d +d +d +d +e +d +d +d +d +d +h +k +c +w +w +a +"} +(5,1,1) = {" +a +w +w +c +h +d +d +e +w +w +j +w +j +w +w +d +d +i +h +c +w +w +a +"} +(6,1,1) = {" +a +w +w +c +i +w +w +j +w +b +r +w +o +F +w +j +w +w +d +c +w +w +a +"} +(7,1,1) = {" +a +w +c +c +d +w +l +o +p +n +r +p +o +H +p +r +b +w +d +c +c +w +a +"} +(8,1,1) = {" +a +w +c +d +d +w +H +o +w +w +r +w +o +w +w +r +n +w +d +d +c +w +a +"} +(9,1,1) = {" +a +w +c +d +w +w +p +o +x +p +B +p +x +p +B +r +p +w +w +d +c +w +a +"} +(10,1,1) = {" +a +w +c +d +w +b +p +w +p +p +p +p +p +p +p +w +p +m +w +d +c +w +a +"} +(11,1,1) = {" +a +w +c +d +j +n +r +r +y +p +s +t +s +p +q +o +o +H +j +e +c +w +a +"} +(12,1,1) = {" +a +w +c +e +w +w +p +w +p +p +t +u +t +p +p +w +p +w +w +d +c +w +a +"} +(13,1,1) = {" +a +w +c +d +j +H +o +o +z +p +s +t +s +p +E +r +r +n +j +d +c +w +a +"} +(14,1,1) = {" +a +w +c +d +w +l +p +w +p +p +p +p +p +p +p +w +p +b +w +i +c +w +a +"} +(15,1,1) = {" +a +w +c +d +w +w +p +r +A +p +C +p +A +p +C +o +p +w +w +d +c +w +a +"} +(16,1,1) = {" +a +w +c +f +d +w +n +r +w +w +o +w +r +w +w +o +H +w +d +d +c +w +a +"} +(17,1,1) = {" +a +w +c +c +d +w +b +r +p +H +o +p +r +n +p +o +G +w +d +c +c +w +a +"} +(18,1,1) = {" +a +w +w +c +d +w +w +j +w +F +o +w +r +b +w +j +w +w +d +c +w +w +a +"} +(19,1,1) = {" +a +w +w +c +h +i +d +d +w +w +j +w +j +w +w +d +e +d +h +c +w +w +a +"} +(20,1,1) = {" +a +w +w +c +k +h +d +d +e +d +d +d +d +d +d +d +d +h +v +c +w +w +a +"} +(21,1,1) = {" +a +w +w +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +c +w +w +a +"} +(22,1,1) = {" +a +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +w +a +"} +(23,1,1) = {" +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +a +"} diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 6415c984bf..16659f3e7a 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -10324,6 +10324,7 @@ /turf/open/floor/carpet, /area/crew_quarters/dorms) "atd" = ( +/mob/living/simple_animal/opossum, /turf/open/floor/plating{ icon_state = "panelscorched" }, @@ -82149,6 +82150,16 @@ /obj/structure/grille, /turf/open/floor/plating, /area/maintenance/port/aft) +"pbc" = ( +/obj/structure/light_construct/small{ + dir = 4 + }, +/obj/machinery/computer/security/telescreen/entertainment{ + pixel_x = 30 + }, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/wood, +/area/maintenance/port/aft) "pbF" = ( /obj/machinery/portable_atmospherics/canister, /obj/machinery/atmospherics/components/unary/portables_connector/visible{ @@ -100175,7 +100186,7 @@ csT dux cxQ ckS -cmi +pbc cnk ckP cmi diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm index f3b1b2afb6..e07873089a 100644 --- a/_maps/map_files/OmegaStation/OmegaStation.dmm +++ b/_maps/map_files/OmegaStation/OmegaStation.dmm @@ -41988,6 +41988,16 @@ }, /turf/open/floor/plasteel, /area/hydroponics) +"saI" = ( +/obj/structure/cable/white{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/supply/hidden{ + dir = 4 + }, +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/starboard) "scn" = ( /obj/structure/cable{ icon_state = "4-8" @@ -45205,6 +45215,10 @@ /obj/effect/landmark/carpspawn, /turf/open/space, /area/space/station_ruins) +"uXV" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/wood, +/area/maintenance/port/aft) "ver" = ( /obj/machinery/atmospherics/pipe/simple/scrubbers/visible{ dir = 10 @@ -79446,7 +79460,7 @@ aFM aOH aOT aPV -aRm +uXV aSI aRC aKm @@ -92526,7 +92540,7 @@ aSD aRz aRz aRz -sGY +saI aRz sIb bsD diff --git a/_maps/map_files/PubbyStation/PubbyStation.dmm b/_maps/map_files/PubbyStation/PubbyStation.dmm index 5cb4f044ae..e6f14badd9 100644 --- a/_maps/map_files/PubbyStation/PubbyStation.dmm +++ b/_maps/map_files/PubbyStation/PubbyStation.dmm @@ -54046,6 +54046,20 @@ }, /turf/open/floor/plasteel, /area/construction/mining/aux_base) +"fgS" = ( +/obj/effect/turf_decal/tile/neutral{ + dir = 1 + }, +/obj/effect/turf_decal/tile/neutral, +/obj/effect/turf_decal/tile/neutral{ + dir = 4 + }, +/obj/effect/turf_decal/tile/neutral{ + dir = 8 + }, +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plasteel/dark, +/area/maintenance/department/engine) "fhM" = ( /obj/item/storage/secure/safe{ pixel_x = -22 @@ -58645,6 +58659,10 @@ "pyw" = ( /turf/open/space/basic, /area/hallway/secondary/entry) +"pzF" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/wood, +/area/maintenance/department/crew_quarters/dorms) "pBD" = ( /obj/structure/cable{ icon_state = "0-8" @@ -83454,7 +83472,7 @@ bHP bJb bJb bJb -bJb +fgS bJb bJb bPp @@ -99823,7 +99841,7 @@ akh alc alR amF -alb +pzF alb anm ajv diff --git a/_maps/map_files/Snaxi/Snaxi.dmm b/_maps/map_files/Snaxi/Snaxi.dmm index 43e7915ca4..45e6e4dee0 100644 --- a/_maps/map_files/Snaxi/Snaxi.dmm +++ b/_maps/map_files/Snaxi/Snaxi.dmm @@ -210,7 +210,8 @@ "aap" = ( /obj/machinery/quantumpad{ map_pad_id = "5"; - map_pad_link_id = "6" + map_pad_link_id = "6"; + name = "South Sector Pad" }, /obj/structure/sign/directions/evac{ pixel_y = 40 @@ -221,6 +222,13 @@ /obj/structure/sign/directions/command{ pixel_y = 24 }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_s"; + layer = 4.1; + name = "south sector"; + pixel_y = 13 + }, /turf/open/floor/plasteel, /area/hallway/primary/aft) "aaq" = ( @@ -519,7 +527,8 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on, /obj/machinery/quantumpad{ map_pad_id = "1"; - map_pad_link_id = "2" + map_pad_link_id = "2"; + name = "Northwest Sector Pad" }, /obj/structure/sign/directions/medical{ dir = 8; @@ -531,6 +540,13 @@ icon_state = "direction_sci"; pixel_y = 28 }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_nw"; + layer = 4.1; + name = "northwest sector"; + pixel_y = 13 + }, /turf/open/floor/plasteel, /area/quartermaster/miningdock) "aaO" = ( @@ -730,13 +746,12 @@ /obj/machinery/light{ dir = 4 }, -/obj/structure/reagent_dispensers/watertank, -/obj/item/radio/intercom{ - pixel_y = -26 - }, /obj/structure/sign/departments/xenobio{ pixel_x = 32 }, +/obj/structure/closet/bombcloset{ + anchored = 1 + }, /turf/open/floor/plasteel/white, /area/science/misc_lab) "abi" = ( @@ -1242,11 +1257,8 @@ /turf/open/floor/plasteel/dark, /area/security/main) "acb" = ( -/obj/structure/sign/poster/official/science{ - pixel_y = -32 - }, -/turf/open/floor/plasteel/white, -/area/science/mixing) +/turf/open/floor/plasteel/dark, +/area/hydroponics) "acc" = ( /obj/structure/sign/poster/contraband/lusty_xenomorph{ pixel_x = -32; @@ -1640,8 +1652,8 @@ "aiS" = ( /obj/structure/closet/secure_closet/CMO, /obj/machinery/keycard_auth{ - pixel_y = 4; - pixel_x = -24 + pixel_x = -24; + pixel_y = 4 }, /obj/effect/turf_decal/tile/blue{ dir = 4 @@ -3147,8 +3159,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "packageSort2"; - dir = 1 + dir = 1; + id = "packageSort2" }, /obj/machinery/disposal/deliveryChute, /obj/structure/window/reinforced{ @@ -4061,9 +4073,9 @@ /area/crew_quarters/dorms) "aNS" = ( /obj/machinery/turnstile{ + dir = 4; name = "Genpop Entrance Turnstile"; - req_access_txt = "69"; - dir = 4 + req_access_txt = "69" }, /obj/machinery/door/poddoor/preopen{ id = "permacell1"; @@ -4554,9 +4566,9 @@ /area/maintenance/solars/port/fore) "bcc" = ( /obj/machinery/door/window/brigdoor/security/cell{ + dir = 1; id = "Cell 2"; - name = "Cell 2"; - dir = 1 + name = "Cell 2" }, /obj/effect/turf_decal/vg_decals/numbers/two, /obj/machinery/door/poddoor/preopen{ @@ -5031,9 +5043,9 @@ "brE" = ( /obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden, /obj/machinery/door/window/southleft{ + dir = 1; name = "Court Cell"; - req_access_txt = "2"; - dir = 1 + req_access_txt = "2" }, /obj/structure/window/reinforced{ dir = 8 @@ -5169,7 +5181,7 @@ /turf/open/floor/plasteel/dark, /area/hallway/secondary/exit/departure_lounge) "bum" = ( -/turf/open/openspace/icemoon, +/turf/open/transparent/openspace/icemoon, /area/icemoon/surface/outdoors) "buF" = ( /obj/machinery/light, @@ -5209,7 +5221,15 @@ "buO" = ( /obj/machinery/quantumpad{ map_pad_id = "3"; - map_pad_link_id = "4" + map_pad_link_id = "4"; + name = "Northeast Sector Pad" + }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_ne"; + layer = 4.1; + name = "northeast sector"; + pixel_y = 13 }, /turf/open/floor/plasteel, /area/hallway/primary/port) @@ -5284,9 +5304,9 @@ dir = 1 }, /obj/machinery/turnstile{ + dir = 4; name = "Genpop Entrance Turnstile"; - req_access_txt = "69"; - dir = 4 + req_access_txt = "69" }, /turf/open/floor/plasteel/dark, /area/security/brig) @@ -6957,9 +6977,9 @@ name = "west facing firelock" }, /obj/machinery/door/window/southleft{ + dir = 8; name = "Test Chamber"; - req_access_txt = "55"; - dir = 8 + req_access_txt = "55" }, /obj/machinery/door/poddoor/preopen{ id = "xenobio2"; @@ -6975,9 +6995,9 @@ dir = 10 }, /obj/machinery/door/window/southleft{ + dir = 4; name = "Test Chamber"; - req_access_txt = "55"; - dir = 4 + req_access_txt = "55" }, /turf/open/floor/plasteel, /area/science/xenobiology) @@ -7072,9 +7092,9 @@ dir = 4 }, /obj/machinery/door/window/brigdoor{ + dir = 1; name = "Reception Desk"; - req_access_txt = "63"; - dir = 1 + req_access_txt = "63" }, /obj/machinery/door/window/brigdoor{ dir = 8; @@ -7186,9 +7206,6 @@ dir = 5 }, /obj/machinery/atmospherics/components/unary/portables_connector/visible, -/obj/machinery/firealarm{ - pixel_y = 24 - }, /turf/open/floor/plasteel, /area/science/mixing) "bKO" = ( @@ -7475,8 +7492,8 @@ }, /obj/structure/table/wood, /obj/machinery/recharger{ - pixel_y = 4; - pixel_x = -7 + pixel_x = -7; + pixel_y = 4 }, /obj/item/book/manual/wiki/security_space_law{ pixel_x = 6; @@ -8958,7 +8975,15 @@ "cpC" = ( /obj/machinery/quantumpad{ map_pad_id = "2"; - map_pad_link_id = "1" + map_pad_link_id = "1"; + name = "South Sector Pad" + }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_s"; + layer = 4.1; + name = "south sector"; + pixel_y = 13 }, /turf/open/floor/plasteel, /area/hallway/primary/port) @@ -9296,8 +9321,8 @@ }, /obj/machinery/camera{ c_tag = "Medbay West"; - network = list("ss13","medbay"); - dir = 4 + dir = 4; + network = list("ss13","medbay") }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) @@ -9414,9 +9439,9 @@ req_access_txt = "10" }, /obj/machinery/door/window/northright{ + dir = 4; name = "Engineering Delivery"; - req_access_txt = "10"; - dir = 4 + req_access_txt = "10" }, /obj/effect/turf_decal/delivery, /turf/open/floor/plasteel/dark, @@ -9818,7 +9843,15 @@ "cQa" = ( /obj/machinery/quantumpad{ map_pad_id = "6"; - map_pad_link_id = "5" + map_pad_link_id = "5"; + name = "Northeast Sector Pad" + }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_ne"; + layer = 4.1; + name = "northeast sector"; + pixel_y = 13 }, /turf/open/floor/plasteel, /area/quartermaster/miningdock) @@ -10387,8 +10420,8 @@ dir = 4 }, /obj/machinery/shower{ - pixel_y = 20; - pixel_x = 11 + pixel_x = 11; + pixel_y = 20 }, /obj/structure/disposalpipe/segment{ dir = 4 @@ -10760,8 +10793,8 @@ /obj/structure/table/wood, /obj/item/book/manual/wiki/security_space_law, /obj/item/flashlight/lamp/green{ - pixel_y = 1; - pixel_x = -7 + pixel_x = -7; + pixel_y = 1 }, /turf/open/floor/wood, /area/security/courtroom) @@ -11054,6 +11087,13 @@ "dAV" = ( /turf/closed/wall/r_wall, /area/ai_monitored/turret_protected/ai_upload_foyer) +"dBA" = ( +/obj/machinery/portable_atmospherics/pump, +/obj/item/radio/intercom{ + pixel_y = -26 + }, +/turf/open/floor/plasteel/white, +/area/science/mixing) "dBL" = ( /obj/structure/closet/secure_closet/medical1, /obj/structure/extinguisher_cabinet{ @@ -11207,9 +11247,9 @@ dir = 4 }, /obj/machinery/turnstile{ + dir = 8; name = "Genpop Exit Turnstile"; - req_access_txt = "70"; - dir = 8 + req_access_txt = "70" }, /turf/open/floor/plasteel/dark, /area/security/brig) @@ -12173,8 +12213,8 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, /obj/machinery/button/flasher{ id = "PCell 1"; - pixel_y = 9; - pixel_x = -37 + pixel_x = -37; + pixel_y = 9 }, /turf/open/floor/plasteel/dark, /area/security/brig) @@ -12356,9 +12396,9 @@ dir = 4 }, /obj/machinery/turnstile{ + dir = 8; name = "Genpop Exit Turnstile"; - req_access_txt = "70"; - dir = 8 + req_access_txt = "70" }, /obj/effect/turf_decal/tile/red{ dir = 1 @@ -12388,7 +12428,7 @@ /turf/open/floor/plasteel, /area/hallway/primary/port) "esY" = ( -/turf/open/openspace/icemoon, +/turf/open/transparent/openspace/icemoon, /area/engine/atmos) "ets" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ @@ -13510,9 +13550,9 @@ icon_state = "1-4" }, /obj/machinery/door/window/brigdoor/security/cell{ + dir = 8; id = "Cell 1"; - name = "Cell 1"; - dir = 8 + name = "Cell 1" }, /obj/effect/turf_decal/vg_decals/numbers/one, /obj/machinery/door/poddoor/preopen{ @@ -13766,7 +13806,6 @@ dir = 4; pixel_x = -24 }, -/obj/structure/closet/crate/bin, /turf/open/floor/plasteel/white, /area/science/misc_lab) "fjS" = ( @@ -14345,8 +14384,8 @@ name = "Door Bolt Control"; normaldoorcontrol = 1; pixel_x = 24; - specialfunctions = 4; - pixel_y = -5 + pixel_y = -5; + specialfunctions = 4 }, /obj/machinery/light/small{ dir = 8 @@ -14836,8 +14875,8 @@ /obj/structure/table/wood, /obj/item/book/manual/wiki/security_space_law, /obj/item/flashlight/lamp/green{ - pixel_y = 1; - pixel_x = -7 + pixel_x = -7; + pixel_y = 1 }, /turf/open/floor/wood, /area/security/courtroom) @@ -15167,7 +15206,15 @@ "gct" = ( /obj/machinery/quantumpad{ map_pad_id = "4"; - map_pad_link_id = "3" + map_pad_link_id = "3"; + name = "Northwest Sector Pad" + }, +/obj/structure/sign/directions/science{ + dir = 8; + icon_state = "pad_nw"; + layer = 4.1; + name = "northwest sector"; + pixel_y = 13 }, /turf/open/floor/plasteel, /area/hallway/primary/aft) @@ -15538,9 +15585,9 @@ /obj/machinery/button/door{ id = "Secure Gate"; name = "Cell Shutters"; + pixel_x = 3; pixel_y = 24; - req_access_txt = "2"; - pixel_x = 3 + req_access_txt = "2" }, /turf/open/floor/plasteel/dark, /area/security/brig) @@ -15934,8 +15981,6 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on{ dir = 4 }, -/obj/machinery/portable_atmospherics/scrubber, -/obj/item/storage/firstaid/toxin, /obj/structure/disposalpipe/segment{ dir = 4 }, @@ -16444,6 +16489,9 @@ /area/storage/atmos) "gPE" = ( /obj/machinery/portable_atmospherics/pump, +/obj/machinery/firealarm{ + pixel_y = 24 + }, /turf/open/floor/plasteel/white, /area/science/mixing) "gPF" = ( @@ -16835,9 +16883,9 @@ /obj/machinery/button/door{ id = "kitchen"; name = "Kitchen Shutters Control"; - req_access_txt = "28"; pixel_x = 24; - pixel_y = -4 + pixel_y = -4; + req_access_txt = "28" }, /turf/open/floor/plasteel/cafeteria, /area/crew_quarters/kitchen) @@ -17096,8 +17144,8 @@ dir = 8 }, /obj/machinery/computer/monitor{ - name = "bridge power monitoring console"; - dir = 1 + dir = 1; + name = "bridge power monitoring console" }, /obj/structure/cable{ icon_state = "0-2" @@ -17235,8 +17283,8 @@ /obj/item/folder/blue, /obj/item/stamp/law, /obj/item/flashlight/lamp/green{ - pixel_y = 1; - pixel_x = -7 + pixel_x = -7; + pixel_y = 1 }, /turf/open/floor/plasteel/cult, /area/lawoffice) @@ -18346,9 +18394,8 @@ /turf/open/floor/carpet, /area/security/detectives_office) "hTL" = ( -/obj/effect/spawner/structure/window/plasma/reinforced, /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden, -/turf/open/floor/plating, +/turf/open/floor/plasteel/white, /area/science/mixing) "hTO" = ( /obj/structure/cable{ @@ -18426,9 +18473,9 @@ /area/engine/engineering) "hVH" = ( /obj/machinery/atmospherics/components/unary/vent_pump/siphon/on{ + dir = 1; external_pressure_bound = 120; - name = "killroom vent"; - dir = 1 + name = "killroom vent" }, /turf/open/floor/circuit/telecomms, /area/science/xenobiology) @@ -18466,8 +18513,8 @@ }, /obj/machinery/camera{ c_tag = "Surgery Observation"; - network = list("ss13","medbay"); - dir = 8 + dir = 8; + network = list("ss13","medbay") }, /turf/open/floor/plasteel/dark, /area/medical/surgery) @@ -18616,8 +18663,8 @@ /area/security/main) "iam" = ( /obj/machinery/computer/slot_machine{ - pixel_y = 2; - pixel_x = -7 + pixel_x = -7; + pixel_y = 2 }, /turf/open/floor/wood, /area/maintenance/bar) @@ -18912,6 +18959,10 @@ }, /turf/open/floor/plasteel, /area/storage/primary) +"ihL" = ( +/mob/living/simple_animal/opossum, +/turf/open/floor/plating, +/area/maintenance/aft/secondary) "ihX" = ( /obj/structure/cable{ icon_state = "0-2" @@ -19807,8 +19858,8 @@ /obj/machinery/button/door{ id = "hos"; name = "HoS Office Shutters"; - pixel_y = -26; - pixel_x = -6 + pixel_x = -6; + pixel_y = -26 }, /turf/open/floor/carpet, /area/crew_quarters/heads/hos) @@ -21383,8 +21434,8 @@ pixel_y = -22 }, /obj/machinery/button/door/incinerator_vent_toxmix{ - pixel_y = -22; - pixel_x = 9 + pixel_x = 9; + pixel_y = -22 }, /obj/machinery/meter, /turf/open/floor/plasteel/white, @@ -22642,6 +22693,8 @@ c_tag = "Testing Lab East"; network = list("ss13","rd") }, +/obj/machinery/portable_atmospherics/scrubber, +/obj/item/storage/firstaid/toxin, /turf/open/floor/plasteel/white, /area/science/misc_lab) "kxI" = ( @@ -24053,6 +24106,10 @@ "luo" = ( /turf/open/floor/plasteel/white, /area/science/misc_lab) +"lwl" = ( +/mob/living/simple_animal/opossum/poppy, +/turf/open/floor/plating, +/area/engine/engineering) "lwH" = ( /obj/structure/cable{ icon_state = "1-4" @@ -24577,8 +24634,8 @@ "lMF" = ( /obj/machinery/camera{ c_tag = "Virology Module"; - network = list("ss13","medbay"); - dir = 1 + dir = 1; + network = list("ss13","medbay") }, /obj/structure/disposalpipe/segment{ dir = 4 @@ -26012,8 +26069,8 @@ /obj/machinery/button/door{ id = "engsm"; name = "Radiation Shutters Control"; - req_access_txt = "10"; - pixel_y = 25 + pixel_y = 25; + req_access_txt = "10" }, /obj/machinery/atmospherics/pipe/manifold/cyan/visible, /obj/effect/turf_decal/stripes/line{ @@ -26159,9 +26216,9 @@ dir = 4 }, /obj/machinery/turnstile{ + dir = 8; name = "Genpop Exit Turnstile"; - req_access_txt = "70"; - dir = 8 + req_access_txt = "70" }, /obj/structure/cable{ icon_state = "4-8" @@ -27616,8 +27673,8 @@ /area/bridge) "nFM" = ( /obj/machinery/atmospherics/components/binary/pump{ - name = "Port Mix to Engine"; - dir = 1 + dir = 1; + name = "Port Mix to Engine" }, /turf/open/floor/plasteel, /area/engine/atmos) @@ -27738,9 +27795,9 @@ "nJI" = ( /obj/structure/table/reinforced, /obj/machinery/door/window/southleft{ + dir = 4; name = "Reception Desk"; - req_access_txt = "63"; - dir = 4 + req_access_txt = "63" }, /obj/structure/cable{ icon_state = "1-2" @@ -27989,8 +28046,8 @@ /area/engine/engineering) "nSX" = ( /obj/machinery/atmospherics/components/binary/pump/on{ - name = "Gas to Cooling Loop"; - dir = 1 + dir = 1; + name = "Gas to Cooling Loop" }, /turf/open/floor/plating, /area/engine/atmospherics_engine) @@ -28291,6 +28348,12 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/plasteel, /area/maintenance/starboard) +"odf" = ( +/obj/structure/closet/bombcloset{ + anchored = 1 + }, +/turf/open/floor/plasteel/white, +/area/science/mixing) "odg" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple{ dir = 4 @@ -30136,8 +30199,8 @@ /area/quartermaster/storage) "pfb" = ( /obj/machinery/atmospherics/components/binary/pump/on{ - name = "Gas to Cooling Loop"; - dir = 1 + dir = 1; + name = "Gas to Cooling Loop" }, /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -30845,10 +30908,10 @@ /area/ai_monitored/security/armory) "pvY" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on{ + dir = 1; external_pressure_bound = 140; name = "killroom vent"; - pressure_checks = 0; - dir = 1 + pressure_checks = 0 }, /turf/open/floor/circuit/telecomms, /area/science/xenobiology) @@ -31385,9 +31448,9 @@ dir = 4 }, /obj/machinery/turnstile{ + dir = 8; name = "Genpop Exit Turnstile"; - req_access_txt = "70"; - dir = 8 + req_access_txt = "70" }, /obj/machinery/door/firedoor/border_only{ dir = 8; @@ -32565,8 +32628,8 @@ }, /obj/machinery/button/ignition{ id = "testigniter"; - pixel_y = -30; - pixel_x = -1 + pixel_x = -1; + pixel_y = -30 }, /obj/structure/disposalpipe/trunk{ dir = 1 @@ -32750,9 +32813,9 @@ /area/maintenance/bar) "qEz" = ( /obj/machinery/door/window/brigdoor/security/cell{ + dir = 1; id = "Cell 3"; - name = "Cell 3"; - dir = 1 + name = "Cell 3" }, /obj/effect/turf_decal/vg_decals/numbers/three, /obj/machinery/door/poddoor/preopen{ @@ -33684,8 +33747,8 @@ "rgQ" = ( /obj/machinery/camera{ c_tag = "Xenobiology Test Chamber"; - network = list("xeno","rd"); - dir = 4 + dir = 4; + network = list("xeno","rd") }, /turf/open/floor/engine, /area/science/xenobiology) @@ -34106,7 +34169,6 @@ /obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{ dir = 4 }, -/obj/structure/reagent_dispensers/fueltank, /turf/open/floor/plasteel/white, /area/science/misc_lab) "rqE" = ( @@ -34451,8 +34513,8 @@ }, /obj/machinery/camera{ c_tag = "AI Chamber - Core"; - network = list("aicore"); - dir = 4 + dir = 4; + network = list("aicore") }, /turf/open/floor/circuit/off, /area/ai_monitored/turret_protected/ai) @@ -34542,9 +34604,9 @@ dir = 4 }, /obj/machinery/turnstile{ + dir = 8; name = "Genpop Exit Turnstile"; - req_access_txt = "70"; - dir = 8 + req_access_txt = "70" }, /obj/machinery/door/firedoor/border_only{ dir = 4; @@ -34779,6 +34841,10 @@ }, /turf/open/floor/plasteel/white, /area/medical/medbay/central) +"rQB" = ( +/obj/structure/reagent_dispensers/fueltank, +/turf/open/floor/plasteel/white, +/area/science/mixing) "rQH" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/simple/supply/hidden, @@ -34883,7 +34949,7 @@ /turf/open/floor/plasteel, /area/engine/engineering) "rUd" = ( -/turf/open/openspace/icemoon, +/turf/open/transparent/openspace/icemoon, /area/science/mixing) "rUl" = ( /obj/structure/cable{ @@ -36330,7 +36396,7 @@ /turf/open/floor/plasteel/white, /area/medical/medbay/central) "sTq" = ( -/turf/open/openspace/icemoon, +/turf/open/transparent/openspace/icemoon, /area/engine/atmospherics_engine) "sTz" = ( /obj/effect/turf_decal/bot_white, @@ -37650,7 +37716,6 @@ /area/icemoon/surface/outdoors) "tDO" = ( /obj/machinery/atmospherics/components/unary/portables_connector/visible, -/obj/machinery/portable_atmospherics/canister/toxins, /obj/machinery/computer/security/telescreen{ name = "Test Chamber Monitor"; network = list("test"); @@ -37770,8 +37835,8 @@ }, /obj/machinery/camera{ c_tag = "Genetics Research"; - network = list("ss13","medbay"); - dir = 8 + dir = 8; + network = list("ss13","medbay") }, /turf/open/floor/plasteel/white, /area/medical/genetics) @@ -37932,9 +37997,9 @@ /obj/machinery/button/door{ id = "Secure Brig Control"; name = "Brig Control Shutters"; + pixel_x = -6; pixel_y = -3; - req_access_txt = "2"; - pixel_x = -6 + req_access_txt = "2" }, /turf/open/floor/plasteel/dark, /area/security/warden) @@ -39014,8 +39079,8 @@ "uuY" = ( /obj/machinery/camera{ c_tag = "Toxins Lab West"; - network = list("ss13","rd"); - dir = 4 + dir = 4; + network = list("ss13","rd") }, /obj/structure/table/reinforced, /obj/item/assembly/prox_sensor{ @@ -39406,9 +39471,9 @@ pixel_y = 23 }, /obj/machinery/door/window/southright{ + dir = 8; name = "Bar Door"; - req_one_access_txt = "25;28"; - dir = 8 + req_one_access_txt = "25;28" }, /obj/machinery/door/firedoor/border_only{ dir = 8; @@ -39560,8 +39625,8 @@ dir = 4 }, /obj/machinery/computer/security/telescreen/prison{ - pixel_x = 29; - dir = 8 + dir = 8; + pixel_x = 29 }, /turf/open/floor/plasteel/cult, /area/lawoffice) @@ -39968,8 +40033,8 @@ dir = 8 }, /obj/structure/chair{ - name = "Judge"; - dir = 4 + dir = 4; + name = "Judge" }, /turf/open/floor/wood, /area/security/courtroom) @@ -40683,8 +40748,8 @@ }, /obj/machinery/button/ignition{ id = "Incinerator"; - pixel_y = 35; - pixel_x = -1 + pixel_x = -1; + pixel_y = 35 }, /turf/open/floor/plasteel, /area/engine/atmospherics_engine) @@ -42591,8 +42656,8 @@ "wtt" = ( /obj/structure/table/wood, /obj/item/flashlight/lamp/green{ - pixel_y = 13; - pixel_x = -7 + pixel_x = -7; + pixel_y = 13 }, /obj/item/camera/detective, /obj/item/hand_labeler{ @@ -42600,8 +42665,8 @@ }, /obj/item/storage/briefcase, /obj/item/storage/secure/safe{ - pixel_y = 36; - pixel_x = 6 + pixel_x = 6; + pixel_y = 36 }, /turf/open/floor/carpet, /area/security/detectives_office) @@ -44647,8 +44712,8 @@ areastring = "/area/engine/engineering"; dir = 8; name = "Engineering APC"; - pixel_y = -1; - pixel_x = -27 + pixel_x = -27; + pixel_y = -1 }, /obj/machinery/firealarm{ dir = 1; @@ -45207,14 +45272,6 @@ /area/medical/medbay/central) "ykk" = ( /obj/machinery/atmospherics/pipe/simple/supply/hidden, -/obj/machinery/door/firedoor/border_only{ - dir = 1; - name = "north facing firelock" - }, -/obj/machinery/door/airlock/research{ - name = "Toxins Lab"; - req_access_txt = "7" - }, /obj/structure/disposalpipe/segment, /turf/open/floor/plasteel/white, /area/science/mixing) @@ -81146,7 +81203,7 @@ bDW bEl bDE bFK -bJC +rQB prL fiN hmX @@ -81403,7 +81460,7 @@ bDW bIn bQP wOK -bJC +fjU gzZ gSu ipY @@ -82174,7 +82231,7 @@ bDW mOv jOB lYy -bJC +fjU ihE fJO eex @@ -82431,7 +82488,7 @@ bDW gpp wbT nHi -bJC +fjU rDA fjU dvO @@ -82688,7 +82745,7 @@ bEa kxj aaw rqx -qJV +fjU bKL fJO jKT @@ -82945,10 +83002,10 @@ bEa pHl aaw abh -qJV +odf +fjU fjU fjU -acb qJV fuG qJV @@ -83205,7 +83262,7 @@ cBt tRe gPE syy -gPE +dBA qJV goM gxF @@ -83466,7 +83523,7 @@ tRe tRe tRe bWf -bVa +ihL kXG sqx owj @@ -86968,7 +87025,7 @@ now mKv sAh sAh -sAh +acb aar uEz uEz @@ -87225,7 +87282,7 @@ qss mKv sAh sAh -sAh +acb urI qkO uEz @@ -87739,7 +87796,7 @@ qss sYv sAh sAh -sAh +acb urI uoV uEz @@ -87996,7 +88053,7 @@ qss sYv sAh sAh -sAh +acb urI uoV uEz @@ -98024,7 +98081,7 @@ xUL xUL dKF ijZ -ijZ +lwl bnU oJa cNE @@ -100071,11 +100128,11 @@ xUL xUL xUL xUL -xUL -xUL -xUL rGc jls +rGc +cly +jls odg ucC gUL @@ -100327,12 +100384,12 @@ bum bum bum bum -bum -bum -bum rGc tdX tdX +tdX +cly +tdX jPj ucC gUL @@ -100584,12 +100641,12 @@ bum bum bum bum -bum -bum -bum dQW tdX tdX +tdX +cly +tdX jls ucC gUL @@ -100841,12 +100898,12 @@ bum bum bum bum -bum -bum -bum rGc tdX tdX +tdX +cly +tdX jPj ucC cfs @@ -101098,12 +101155,12 @@ bum bum bum bum -bum -bum -bum dQW tdX tdX +tdX +cly +tdX jls ucC cfs @@ -101355,11 +101412,11 @@ bum bum bum bum -bum -bum -bum -xUL +rGc +jPj dQW +jPj +rGc flw flw lFN @@ -101612,11 +101669,11 @@ bum bum bum bum -bum -bum -bum -xUL -xUL +dQW +cly +cly +cly +jPj dQW jPj bnU @@ -101869,9 +101926,9 @@ bum bum bum bum -bum -bum -bum +xUL +xUL +xUL bDA bYY bYY @@ -102126,9 +102183,9 @@ bum bum bum bum -bum -bum -bum +xUL +xUL +xUL gjW xUL xUL @@ -102383,9 +102440,9 @@ bum bum bum bum -bum -bum -bum +xUL +xUL +xUL xUL xUL bum @@ -102640,9 +102697,9 @@ bum bum bum bum -bum -bum -bum +xUL +xUL +xUL xUL xUL sTq diff --git a/_maps/map_files/debug/multiz.dmm b/_maps/map_files/debug/multiz.dmm index 4614829679..5a18682129 100644 --- a/_maps/map_files/debug/multiz.dmm +++ b/_maps/map_files/debug/multiz.dmm @@ -102,7 +102,7 @@ /turf/open/floor/plasteel, /area/engine/gravity_generator) "au" = ( -/turf/open/openspace, +/turf/open/transparent/openspace, /area/space) "av" = ( /obj/structure/stairs{ @@ -1407,7 +1407,7 @@ }, /area/hallway/secondary/service) "eC" = ( -/turf/open/openspace, +/turf/open/transparent/openspace, /area/maintenance/department/bridge) "eD" = ( /obj/effect/turf_decal/stripes/asteroid/line{ @@ -1542,7 +1542,7 @@ /turf/open/floor/plating, /area/space) "iu" = ( -/turf/open/openspace, +/turf/open/transparent/openspace, /area/hallway/secondary/service) "iK" = ( /turf/open/floor/plasteel{ @@ -1593,7 +1593,7 @@ }, /area/hallway/secondary/service) "nx" = ( -/turf/open/openspace, +/turf/open/transparent/openspace, /area/space/nearstation) "nz" = ( /obj/machinery/light{ @@ -1633,7 +1633,7 @@ /turf/open/floor/plating, /area/maintenance/department/bridge) "qo" = ( -/turf/open/openspace, +/turf/open/transparent/openspace, /area/engine/storage) "qR" = ( /obj/effect/turf_decal/stripes/white/line{ @@ -1646,7 +1646,7 @@ /obj/machinery/light{ dir = 8 }, -/turf/open/openspace, +/turf/open/transparent/openspace, /area/engine/storage) "sh" = ( /turf/open/floor/plasteel{ @@ -1746,7 +1746,7 @@ /area/construction) "Bk" = ( /obj/structure/grille, -/turf/open/openspace, +/turf/open/transparent/openspace, /area/space/nearstation) "Bm" = ( /obj/machinery/light{ @@ -1876,7 +1876,7 @@ "Ob" = ( /obj/structure/lattice, /obj/structure/grille, -/turf/open/openspace, +/turf/open/transparent/openspace, /area/space/nearstation) "Og" = ( /obj/machinery/airalarm/directional/north, diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index 5a14eb9041..020017c3fe 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -13490,6 +13490,9 @@ /obj/structure/table/wood, /obj/item/instrument/piano_synth, /obj/item/instrument/guitar, +/obj/structure/sign/painting{ + pixel_x = -32 + }, /turf/open/indestructible/hotelwood, /area/centcom/holding) "Fg" = ( @@ -13794,6 +13797,9 @@ /turf/open/indestructible/hotelwood, /area/centcom/holding) "FX" = ( +/obj/structure/sign/painting{ + pixel_x = 32 + }, /turf/open/floor/plasteel/stairs, /area/centcom/holding) "FZ" = ( @@ -14421,6 +14427,9 @@ dir = 8 }, /obj/structure/musician/piano, +/obj/structure/sign/painting{ + pixel_x = -32 + }, /turf/open/indestructible/hotelwood, /area/centcom/holding) "Hk" = ( @@ -16225,6 +16234,12 @@ dir = 1 }, /obj/structure/table/wood, +/obj/item/storage/crayons, +/obj/item/storage/crayons, +/obj/item/storage/crayons, +/obj/structure/sign/painting{ + pixel_x = -32 + }, /turf/open/indestructible/hotelwood, /area/centcom/holding) "KU" = ( @@ -16304,6 +16319,14 @@ /obj/item/storage/box/bodybags, /turf/open/floor/plasteel/white, /area/centcom/holding) +"Ll" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/easel, +/obj/item/canvas/twentythreeXtwentythree, +/turf/open/indestructible/hotelwood, +/area/centcom/holding) "Lm" = ( /obj/machinery/shower{ dir = 4 @@ -16808,6 +16831,22 @@ }, /turf/open/floor/plasteel, /area/centcom/supplypod/loading/two) +"NA" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/table/wood, +/obj/item/canvas/twentythreeXtwentythree, +/obj/item/canvas/twentythreeXtwentythree, +/obj/item/canvas/twentythreeXtwentythree, +/obj/item/canvas/twentythreeXnineteen, +/obj/item/canvas/twentythreeXnineteen, +/obj/item/canvas/twentythreeXnineteen, +/obj/item/canvas/nineteenXnineteen, +/obj/item/canvas/nineteenXnineteen, +/obj/item/canvas/nineteenXnineteen, +/turf/open/indestructible/hotelwood, +/area/centcom/holding) "NE" = ( /obj/machinery/light{ dir = 4 @@ -16924,6 +16963,12 @@ }, /turf/open/floor/plasteel, /area/centcom/supplypod/loading/three) +"On" = ( +/obj/structure/sign/painting{ + pixel_x = 32 + }, +/turf/open/indestructible/hotelwood, +/area/centcom/holding) "Op" = ( /obj/structure/sink{ dir = 8; @@ -17249,6 +17294,10 @@ }, /turf/open/floor/mineral/titanium, /area/centcom/evac) +"Qc" = ( +/obj/effect/landmark/mafia_game_area, +/turf/open/space/basic, +/area/space) "Qe" = ( /turf/open/ai_visible, /area/ai_multicam_room) @@ -17906,6 +17955,15 @@ /obj/machinery/light, /turf/open/floor/plasteel/freezer, /area/centcom/holding) +"TD" = ( +/obj/machinery/light{ + dir = 4 + }, +/obj/structure/sign/painting{ + pixel_x = 32 + }, +/turf/open/indestructible/hotelwood, +/area/centcom/holding) "TF" = ( /obj/effect/turf_decal/tile/red{ dir = 8 @@ -45453,8 +45511,8 @@ Nd AE CV Sd -KT -Sd +Ll +CV Sd Sd Sd @@ -45710,7 +45768,7 @@ Nd QJ Sd Sd -KT +NA Sd Sd Sd @@ -46222,8 +46280,8 @@ Fh Fh Wl Sd -MR -Sd +TD +On FX Sd Sd @@ -58911,7 +58969,7 @@ aa aa aa aa -aa +Qc aa aa aa diff --git a/_maps/multiz_debug.json b/_maps/multiz_debug.json index 7e39981e23..e916a77d1d 100644 --- a/_maps/multiz_debug.json +++ b/_maps/multiz_debug.json @@ -2,5 +2,5 @@ "map_name": "MultiZ Debug", "map_path": "map_files/debug", "map_file": "multiz.dmm", - "traits": [{"Up": 1}, {"Up": 1, "Down": -1}, {"Down": -1}] + "traits": [{"Up" : 1, "Linkage" : "Cross"}, {"Up" : 1, "Down" : -1, "Baseturf" : "/turf/open/transparent/openspace", "Linkage" : "Cross"}, {"Down" : -1, "Baseturf" : "/turf/open/transparent/openspace", "Linkage" : "Cross"}] } diff --git a/_maps/shuttles/emergency_discoinferno.dmm b/_maps/shuttles/emergency_discoinferno.dmm index e142208e12..92b515f17e 100644 --- a/_maps/shuttles/emergency_discoinferno.dmm +++ b/_maps/shuttles/emergency_discoinferno.dmm @@ -94,7 +94,7 @@ /turf/open/floor/light/colour_cycle, /area/shuttle/escape) "r" = ( -/turf/open/floor/mineral/plasma, +/turf/open/floor/mineral/plasma/disco, /area/shuttle/escape) "s" = ( /turf/open/floor/mineral/silver, @@ -112,7 +112,7 @@ resistance_flags = 2 }, /obj/structure/fans/tiny, -/turf/open/floor/mineral/plasma, +/turf/open/floor/mineral/plasma/disco, /area/shuttle/escape) "v" = ( /obj/machinery/jukebox/disco/indestructible, diff --git a/_maps/snaxi.json b/_maps/snaxi.json index cb1917b6f6..3fc8eba15d 100644 --- a/_maps/snaxi.json +++ b/_maps/snaxi.json @@ -33,7 +33,7 @@ "Linkage":null, "Gravity":true, "Ice Ruins Underground":true, - "Baseturf":"/turf/open/openspace/icemoon" + "Baseturf":"/turf/open/transparent/openspace/icemoon" }, { "Down":-1, diff --git a/code/__DEFINES/_flags/_flags.dm b/code/__DEFINES/_flags/_flags.dm index 0734feaa42..ffa3f1c751 100644 --- a/code/__DEFINES/_flags/_flags.dm +++ b/code/__DEFINES/_flags/_flags.dm @@ -98,7 +98,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define ZAP_MOB_DAMAGE (1<<3) #define ZAP_MOB_STUN (1<<4) -#define ZAP_DEFAULT_FLAGS ALL +#define ZAP_DEFAULT_FLAGS ZAP_MOB_STUN | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE #define ZAP_FUSION_FLAGS ZAP_OBJ_DAMAGE | ZAP_MOB_DAMAGE | ZAP_MOB_STUN #define ZAP_SUPERMATTER_FLAGS NONE diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index b883a54209..a3e5423753 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -63,6 +63,16 @@ #define CONTRACT_UPLINK_PAGE_CONTRACTS "CONTRACTS" #define CONTRACT_UPLINK_PAGE_HUB "HUB" + +///Heretics -- +#define IS_HERETIC(mob) (mob.mind?.has_antag_datum(/datum/antagonist/heretic)) + +#define PATH_SIDE "Side" + +#define PATH_ASH "Ash" +#define PATH_RUST "Rust" +#define PATH_FLESH "Flesh" + //Overthrow time to update heads obj #define OBJECTIVE_UPDATING_TIME 300 @@ -91,4 +101,4 @@ #define BLOB_REROLL_TIME 2400 // blob gets a free reroll every X time #define BLOB_SPREAD_COST 4 #define BLOB_ATTACK_REFUND 2 //blob refunds this much if it attacks and doesn't spread -#define BLOB_REFLECTOR_COST 15 \ No newline at end of file +#define BLOB_REFLECTOR_COST 15 diff --git a/code/__DEFINES/atom_hud.dm b/code/__DEFINES/atom_hud.dm index 01f14c748e..386473a3ae 100644 --- a/code/__DEFINES/atom_hud.dm +++ b/code/__DEFINES/atom_hud.dm @@ -60,6 +60,7 @@ #define ANTAG_HUD_BROTHER 23 #define ANTAG_HUD_BLOODSUCKER 24 #define ANTAG_HUD_FUGITIVE 25 +#define ANTAG_HUD_HERETIC 26 // Notification action types #define NOTIFY_JUMP "jump" diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 6900f36a33..bdee4cdcd5 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -15,22 +15,31 @@ ////////////////////////////////////////////////////////////////// // /datum signals -#define COMSIG_COMPONENT_ADDED "component_added" //sent to the new datum parent when a component is added to them: (/datum/component) -#define COMSIG_COMPONENT_REMOVING "component_removing" //sent to the datum parent before a component is removed from them because of RemoveComponent: (/datum/component) -#define COMSIG_COMPONENT_UNREGISTER_PARENT "component_unregister_parent" //sent to the component itself when unregistered from a parent -#define COMSIG_COMPONENT_REGISTER_PARENT "component_register_parent" //sent to the component itself when registered to a parent -#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" //before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation -#define COMSIG_PARENT_QDELETING "parent_qdeleting" //just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called - -/// Trait signals -#define COMPONENT_ADD_TRAIT (1<<0) -#define COMPONENT_REMOVE_TRAIT (1<<1) +/// when a component is added to a datum: (/datum/component) +#define COMSIG_COMPONENT_ADDED "component_added" +/// before a component is removed from a datum because of RemoveComponent: (/datum/component) +#define COMSIG_COMPONENT_REMOVING "component_removing" +/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation +#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" +/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called +#define COMSIG_PARENT_QDELETING "parent_qdeleting" +/// generic topic handler (usr, href_list) +#define COMSIG_TOPIC "handle_topic" /// fires on the target datum when an element is attached to it (/datum/element) #define COMSIG_ELEMENT_ATTACH "element_attach" /// fires on the target datum when an element is attached to it (/datum/element) #define COMSIG_ELEMENT_DETACH "element_detach" +/// sent to the component itself when unregistered from a parent +#define COMSIG_COMPONENT_UNREGISTER_PARENT "component_unregister_parent" +/// sent to the component itself when registered to a parent +#define COMSIG_COMPONENT_REGISTER_PARENT "component_register_parent" + +/// Trait signals +#define COMPONENT_ADD_TRAIT (1<<0) +#define COMPONENT_REMOVE_TRAIT (1<<1) + // /atom signals //from base of atom/proc/Initialize(): sent any time a new atom is created #define COMSIG_ATOM_CREATED "atom_created" @@ -56,30 +65,54 @@ #define COMSIG_ATOM_EXIT "atom_exit" //from base of atom/Exit(): (/atom/movable/exiting, /atom/newloc) #define COMPONENT_ATOM_BLOCK_EXIT 1 #define COMSIG_ATOM_EXITED "atom_exited" //from base of atom/Exited(): (atom/movable/exiting, atom/newloc) -#define COMSIG_ATOM_EX_ACT "atom_ex_act" //from base of atom/ex_act(): (severity, target) -#define COMSIG_ATOM_EMP_ACT "atom_emp_act" //from base of atom/emp_act(): (severity) -#define COMSIG_ATOM_FIRE_ACT "atom_fire_act" //from base of atom/fire_act(): (exposed_temperature, exposed_volume) -#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act" //from base of atom/bullet_act(): (/obj/item/projectile, def_zone) -#define COMSIG_ATOM_BLOB_ACT "atom_blob_act" //from base of atom/blob_act(): (/obj/structure/blob) -#define COMSIG_ATOM_ACID_ACT "atom_acid_act" //from base of atom/acid_act(): (acidpwr, acid_volume) -#define COMSIG_ATOM_EMAG_ACT "atom_emag_act" //from base of atom/emag_act(): () -#define COMSIG_ATOM_RAD_ACT "atom_rad_act" //from base of atom/rad_act(intensity) -#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act" //from base of atom/narsie_act(): () -#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): () -#define COMSIG_ATOM_RCD_ACT "atom_rcd_act" //from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode) -#define COMSIG_ATOM_SING_PULL "atom_sing_pull" //from base of atom/singularity_pull(): (S, current_size) -#define COMSIG_ATOM_SET_LIGHT "atom_set_light" //from base of atom/set_light(): (l_range, l_power, l_color) -#define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" //from base of atom/setDir(): (old_dir, new_dir) -#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" //from base of atom/handle_atom_del(): (atom/deleted) -#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" //from base of atom/has_gravity(): (turf/location, list/forced_gravities) -#define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" //from proc/get_rad_contents(): () - #define COMPONENT_BLOCK_RADIATION 1 -#define COMSIG_ATOM_RAD_CONTAMINATING "atom_rad_contam" //from base of datum/radiation_wave/radiate(): (strength) - #define COMPONENT_BLOCK_CONTAMINATION 1 -#define COMSIG_ATOM_RAD_WAVE_PASSING "atom_rad_wave_pass" //from base of datum/radiation_wave/check_obstructions(): (datum/radiation_wave, width) - #define COMPONENT_RAD_WAVE_HANDLED 1 -#define COMSIG_ATOM_CANREACH "atom_can_reach" //from internal loop in atom/movable/proc/CanReach(): (list/next) - #define COMPONENT_BLOCK_REACH 1 +///from base of atom/ex_act(): (severity, target) +#define COMSIG_ATOM_EX_ACT "atom_ex_act" +///from base of atom/emp_act(): (severity) +#define COMSIG_ATOM_EMP_ACT "atom_emp_act" +///from base of atom/fire_act(): (exposed_temperature, exposed_volume) +#define COMSIG_ATOM_FIRE_ACT "atom_fire_act" +///from base of atom/bullet_act(): (/obj/projectile, def_zone) +#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act" +///from base of atom/blob_act(): (/obj/structure/blob) +#define COMSIG_ATOM_BLOB_ACT "atom_blob_act" +///from base of atom/acid_act(): (acidpwr, acid_volume) +#define COMSIG_ATOM_ACID_ACT "atom_acid_act" +///from base of atom/emag_act(): (/mob/user) +#define COMSIG_ATOM_EMAG_ACT "atom_emag_act" +///from base of atom/rad_act(intensity) +#define COMSIG_ATOM_RAD_ACT "atom_rad_act" +///from base of atom/narsie_act(): () +#define COMSIG_ATOM_NARSIE_ACT "atom_narsie_act" +///from base of atom/ratvar_act(): () +#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" +///from base of atom/rcd_act(): (/mob, /obj/item/construction/rcd, passed_mode) +#define COMSIG_ATOM_RCD_ACT "atom_rcd_act" +///from base of atom/singularity_pull(): (S, current_size) +#define COMSIG_ATOM_SING_PULL "atom_sing_pull" +///from obj/machinery/bsa/full/proc/fire(): () +#define COMSIG_ATOM_BSA_BEAM "atom_bsa_beam_pass" + #define COMSIG_ATOM_BLOCKS_BSA_BEAM (1<<0) +///from base of atom/set_light(): (l_range, l_power, l_color) +#define COMSIG_ATOM_SET_LIGHT "atom_set_light" +///from base of atom/setDir(): (old_dir, new_dir). Called before the direction changes. +#define COMSIG_ATOM_DIR_CHANGE "atom_dir_change" +///from base of atom/handle_atom_del(): (atom/deleted) +#define COMSIG_ATOM_CONTENTS_DEL "atom_contents_del" +///from base of atom/has_gravity(): (turf/location, list/forced_gravities) +#define COMSIG_ATOM_HAS_GRAVITY "atom_has_gravity" +///from proc/get_rad_contents(): () +#define COMSIG_ATOM_RAD_PROBE "atom_rad_probe" + #define COMPONENT_BLOCK_RADIATION (1<<0) +///from base of datum/radiation_wave/radiate(): (strength) +#define COMSIG_ATOM_RAD_CONTAMINATING "atom_rad_contam" + #define COMPONENT_BLOCK_CONTAMINATION (1<<0) +///from base of datum/radiation_wave/check_obstructions(): (datum/radiation_wave, width) +#define COMSIG_ATOM_RAD_WAVE_PASSING "atom_rad_wave_pass" + #define COMPONENT_RAD_WAVE_HANDLED (1<<0) +///from internal loop in atom/movable/proc/CanReach(): (list/next) +#define COMSIG_ATOM_CANREACH "atom_can_reach" + #define COMPONENT_BLOCK_REACH (1<<0) + #define COMSIG_ATOM_SCREWDRIVER_ACT "atom_screwdriver_act" //from base of atom/screwdriver_act(): (mob/living/user, obj/item/I) #define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" //called when teleporting into a protected turf: (channel, turf/origin, turf/destination) #define COMPONENT_BLOCK_TELEPORT 1 diff --git a/code/__DEFINES/footsteps.dm b/code/__DEFINES/footsteps.dm index 2dd66b9833..215e287adf 100644 --- a/code/__DEFINES/footsteps.dm +++ b/code/__DEFINES/footsteps.dm @@ -7,6 +7,8 @@ #define FOOTSTEP_WATER "water" #define FOOTSTEP_LAVA "lava" #define FOOTSTEP_MEAT "meat" +#define FOOTSTEP_RUST "rust" + //barefoot sounds #define FOOTSTEP_WOOD_BAREFOOT "woodbarefoot" #define FOOTSTEP_WOOD_CLAW "woodclaw" @@ -91,7 +93,9 @@ GLOBAL_LIST_INIT(footstep, list( 'sound/effects/footstep/lava2.ogg', 'sound/effects/footstep/lava3.ogg'), 100, 0), FOOTSTEP_MEAT = list(list( - 'sound/effects/meatslap.ogg'), 100, 0) + 'sound/effects/meatslap.ogg'), 100, 0), + FOOTSTEP_RUST = list(list( + 'sound/effects/footstep/rustystep1.ogg'), 100, 0) )) //bare footsteps lists @@ -135,7 +139,9 @@ GLOBAL_LIST_INIT(barefootstep, list( 'sound/effects/footstep/lava2.ogg', 'sound/effects/footstep/lava3.ogg'), 100, 0), FOOTSTEP_MEAT = list(list( - 'sound/effects/meatslap.ogg'), 100, 0) + 'sound/effects/meatslap.ogg'), 100, 0), + FOOTSTEP_RUST = list(list( + 'sound/effects/footstep/rustystep1.ogg'), 100, 0) )) //claw footsteps lists @@ -179,7 +185,9 @@ GLOBAL_LIST_INIT(clawfootstep, list( 'sound/effects/footstep/lava2.ogg', 'sound/effects/footstep/lava3.ogg'), 100, 0), FOOTSTEP_MEAT = list(list( - 'sound/effects/meatslap.ogg'), 100, 0) + 'sound/effects/meatslap.ogg'), 100, 0), + FOOTSTEP_RUST = list(list( + 'sound/effects/footstep/rustystep1.ogg'), 100, 0) )) //heavy footsteps list @@ -197,5 +205,7 @@ GLOBAL_LIST_INIT(heavyfootstep, list( 'sound/effects/footstep/lava2.ogg', 'sound/effects/footstep/lava3.ogg'), 100, 0), FOOTSTEP_MEAT = list(list( - 'sound/effects/meatslap.ogg'), 100, 0) + 'sound/effects/meatslap.ogg'), 100, 0), + FOOTSTEP_RUST = list(list( + 'sound/effects/footstep/rustystep1.ogg'), 150, 2) )) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index d66521d945..55be13834d 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( /turf/open/chasm, /turf/open/lava, /turf/open/water, - /turf/open/openspace + /turf/open/transparent/openspace ))) #define isgroundlessturf(A) (is_type_in_typecache(A, GLOB.turfs_without_ground)) @@ -44,6 +44,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list( #define isplatingturf(A) (istype(A, /turf/open/floor/plating)) +#define istransparentturf(A) (istype(A, /turf/open/transparent)) + //Mobs #define isliving(A) (istype(A, /mob/living)) diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 71de692410..ecb58f1291 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -19,6 +19,7 @@ #define INVESTIGATE_FERMICHEM "fermichem" #define INVESTIGATE_RCD "rcd" #define INVESTIGATE_CRYOGENICS "cryogenics" +#define INVESTIGATE_GHOST "ghost" // Logging types for log_message() #define LOG_ATTACK (1 << 0) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index 24726b8058..370f869c48 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -46,6 +46,8 @@ // Cosecant #define CSC(x) (1 / sin(x)) +#define ATAN2(x, y) ( !(x) && !(y) ? 0 : (y) >= 0 ? arccos((x) / sqrt((x)*(x) + (y)*(y))) : -arccos((x) / sqrt((x)*(x) + (y)*(y))) ) + // Greatest Common Divisor - Euclid's algorithm /proc/Gcd(a, b) return b ? Gcd(b, (a) % (b)) : a diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 782095f883..6ddd269b4b 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -332,3 +332,5 @@ /// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() #define EXAMINE_MORE_TIME 1 SECONDS + +#define SILENCE_RANGED_MESSAGE (1<<0) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 7fabb74157..d7487eb54b 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -17,6 +17,7 @@ #define ROLE_ALIEN "xenomorph" #define ROLE_PAI "pAI" #define ROLE_CULTIST "cultist" +#define ROLE_HERETIC "Heretic" #define ROLE_BLOB "blob" #define ROLE_NINJA "space ninja" #define ROLE_MONKEY "monkey" @@ -64,6 +65,7 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs, ROLE_SENTIENCE, ROLE_GANG = /datum/game_mode/gang, + ROLE_HERETIC = /datum/game_mode/heretics, ROLE_BLOODSUCKER = /datum/game_mode/bloodsucker //ROLE_MONSTERHUNTER Disabled for now )) diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 9a3b5d55f4..bfff1dc629 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -111,6 +111,10 @@ #define STATUS_EFFECT_LIMP /datum/status_effect/limp //For when you have a busted leg (or two!) and want additional slowdown when walking on that leg +#define STATUS_EFFECT_AMOK /datum/status_effect/amok //Makes the target automatically strike out at adjecent non-heretics. + +#define STATUS_EFFECT_CLOUDSTRUCK /datum/status_effect/cloudstruck //blinds and applies an overlay. + /// shoves inflict this to indicate the next shove while this is in effect should disarm guns #define STATUS_EFFECT_OFF_BALANCE /datum/status_effect/off_balance diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 3e5b27d376..674cc16980 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -104,7 +104,9 @@ #define TRAIT_RESISTCOLD "resist_cold" #define TRAIT_RESISTHIGHPRESSURE "resist_high_pressure" #define TRAIT_RESISTLOWPRESSURE "resist_low_pressure" +#define TRAIT_BOMBIMMUNE "bomb_immunity" #define TRAIT_RADIMMUNE "rad_immunity" +#define TRAIT_GENELESS "geneless" #define TRAIT_VIRUSIMMUNE "virus_immunity" #define TRAIT_PIERCEIMMUNE "pierce_immunity" #define TRAIT_NODISMEMBER "dismember_immunity" @@ -178,6 +180,7 @@ #define TRAIT_FREERUNNING "freerunning" #define TRAIT_SKITTISH "skittish" #define TRAIT_POOR_AIM "poor_aim" +#define TRAIT_INSANE_AIM "insane_aim" //they don't miss. they never miss. it was all part of their immaculate plan. #define TRAIT_PROSOPAGNOSIA "prosopagnosia" #define TRAIT_DRUNK_HEALING "drunk_healing" #define TRAIT_TAGGER "tagger" @@ -202,7 +205,7 @@ #define TRAIT_NO_ALCOHOL "alcohol_intolerance" #define TRAIT_MUTATION_STASIS "mutation_stasis" //Prevents processed genetics mutations from processing. #define TRAIT_FAST_PUMP "fast_pump" -#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good.... +#define TRAIT_NICE_SHOT "nice_shot" //hnnnnnnnggggg..... you're pretty good... // mobility flag traits // IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it) @@ -256,6 +259,7 @@ #define CURSED_ITEM_TRAIT "cursed-item" // The item is magically cursed #define ABSTRACT_ITEM_TRAIT "abstract-item" #define STATUS_EFFECT_TRAIT "status-effect" +#define CLOTHING_TRAIT "clothing" #define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention #define GHOSTROLE_TRAIT "ghostrole" #define APHRO_TRAIT "aphro" @@ -310,4 +314,4 @@ #define ACTIVE_BLOCK_TRAIT "active_block" /// This trait is added by the parry system. #define ACTIVE_PARRY_TRAIT "active_parry" -#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name \ No newline at end of file +#define STICKY_NODROP "sticky-nodrop" //sticky nodrop sounds like a bad soundcloud rapper's name diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index a688d20451..4ee622961a 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -128,6 +128,10 @@ GLOBAL_VAR_INIT(cmp_field, "name") else return B.required_temp - A.required_temp //return hottest + +/proc/cmp_mob_realname_dsc(mob/A,mob/B) + return sorttext(A.real_name,B.real_name) + /proc/cmp_job_display_asc(datum/job/A, datum/job/B) return A.display_order - B.display_order @@ -135,4 +139,4 @@ GLOBAL_VAR_INIT(cmp_field, "name") return sorttext(initial(b.name),initial(a.name)) /proc/cmp_typepaths_asc(A, B) - return sorttext("[B]","[A]") \ No newline at end of file + return sorttext("[B]","[A]") diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 01a34c2ab7..960e1f8d59 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -9,4 +9,8 @@ GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report //TODO clear this one up too -GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult) \ No newline at end of file +GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult) + + +///We want reality_smash_tracker to exist only once and be accesable from anywhere. +GLOBAL_DATUM_INIT(reality_smash_track, /datum/reality_smash_tracker, new) diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index a25381f266..7c2ebc5f0c 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -231,4 +231,10 @@ GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitt GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc)) - +//locked parts are those that your picked species requires to have +//unlocked parts are those that anyone can choose on customisation regardless +//parts not in unlocked, but in all, are thus locked +GLOBAL_LIST_INIT(all_mutant_parts, list("tail_lizard" = "Tail", "mam_tail" = "Tail", "tail_human" = "Tail", "snout" = "Snout", "frills" = "Frills", "spines" = "Spines", "body_markings" = "Body Markings", "mam_body_markings" = "Species Markings" , "mam_ears" = "Ears", "ears" = "Ears", "mam_snouts" = "Snout", "legs" = "Legs", "deco_wings" = "Decorative Wings", "insect_wings" = "Insect Wings", "insect_fluff" = "Insect Fluff", "taur" = "Tauric Body", "insect_markings" = "Insect Markings", "wings" = "Wings", "xenohead" = "Caste Head", "xenotail" = "Tail", "xenodorsal" = "Dorsal Spines", "ipc_screen" = "Screen", "ipc_antenna" = "Antenna", "meat_type" = "Meat Type", "horns" = "Horns")) +GLOBAL_LIST_INIT(unlocked_mutant_parts, list("horns", "insect_fluff")) +//parts in either of the above two lists that require a second option that allows them to be coloured +GLOBAL_LIST_INIT(colored_mutant_parts, list("insect_wings" = "wings_color", "deco_wings" = "wings_color", "horns" = "horns_color")) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 8d8b8820b2..605b20a4f3 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -37,7 +37,9 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_RESISTCOLD" = TRAIT_RESISTCOLD, "TRAIT_RESISTHIGHPRESSURE" = TRAIT_RESISTHIGHPRESSURE, "TRAIT_RESISTLOWPRESSURE" = TRAIT_RESISTLOWPRESSURE, + "TRAIT_BOMBIMMUNE" = TRAIT_BOMBIMMUNE, "TRAIT_RADIMMUNE" = TRAIT_RADIMMUNE, + "TRAIT_GENELESS" = TRAIT_GENELESS, "TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_NODISMEMBER" = TRAIT_NODISMEMBER, diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm index 5765f291e9..cc9c067b5d 100644 --- a/code/controllers/configuration/entries/game_options.dm +++ b/code/controllers/configuration/entries/game_options.dm @@ -98,6 +98,11 @@ config_entry_value = 6 min_val = 1 +/datum/config_entry/number/ecult_scaling_coeff //how much does the amount of players get divided by to determine e_cult + config_entry_value = 6 + integer = FALSE + min_val = 1 + /datum/config_entry/number/security_scaling_coeff //how much does the amount of players get divided by to determine open security officer positions config_entry_value = 8 min_val = 1 diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 97addfa131..617004646a 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -113,7 +113,7 @@ SUBSYSTEM_DEF(mapping) // needs to be whitelisted for underground too so place_below ruins work seedRuins(ice_ruins, CONFIG_GET(number/icemoon_budget), list(/area/icemoon/surface/outdoors/unexplored, /area/icemoon/underground/unexplored), ice_ruins_templates) for (var/ice_z in ice_ruins) - spawn_rivers(ice_z, 4, /turf/open/openspace/icemoon, /area/icemoon/surface/outdoors/unexplored/rivers) + spawn_rivers(ice_z, 4, /turf/open/transparent/openspace/icemoon, /area/icemoon/surface/outdoors/unexplored/rivers) var/list/ice_ruins_underground = levels_by_trait(ZTRAIT_ICE_RUINS_UNDERGROUND) if (ice_ruins_underground.len) diff --git a/code/datums/components/crafting/recipes/recipes_misc.dm b/code/datums/components/crafting/recipes/recipes_misc.dm index 214c1b1427..2bfb187018 100644 --- a/code/datums/components/crafting/recipes/recipes_misc.dm +++ b/code/datums/components/crafting/recipes/recipes_misc.dm @@ -126,7 +126,7 @@ /datum/crafting_recipe/brute_pack name = "Suture Pack" - result = /obj/item/stack/medical/suture/one + result = /obj/item/stack/medical/suture/five time = 1 reqs = list(/obj/item/stack/medical/gauze = 1, /datum/reagent/medicine/styptic_powder = 10) @@ -135,7 +135,7 @@ /datum/crafting_recipe/burn_pack name = "Regenerative Mesh" - result = /obj/item/stack/medical/mesh/one + result = /obj/item/stack/medical/mesh/five time = 1 reqs = list(/obj/item/stack/medical/gauze = 1, /datum/reagent/medicine/silver_sulfadiazine = 10) @@ -188,7 +188,7 @@ result = /obj/item/screwdriver/bronze reqs = list(/obj/item/screwdriver = 1, /obj/item/stack/cable_coil = 10, - /obj/item/stack/tile/bronze = 1, + /obj/item/stack/sheet/bronze = 1, /datum/reagent/water = 15) time = 40 subcategory = CAT_TOOL @@ -200,7 +200,7 @@ result = /obj/item/weldingtool/bronze reqs = list(/obj/item/weldingtool = 1, /obj/item/stack/cable_coil = 10, - /obj/item/stack/tile/bronze = 1, + /obj/item/stack/sheet/bronze = 1, /datum/reagent/water = 15) time = 40 subcategory = CAT_TOOL @@ -212,7 +212,7 @@ result = /obj/item/wirecutters/bronze reqs = list(/obj/item/wirecutters = 1, /obj/item/stack/cable_coil = 10, - /obj/item/stack/tile/bronze = 1, + /obj/item/stack/sheet/bronze = 1, /datum/reagent/water = 15) time = 40 subcategory = CAT_TOOL @@ -224,7 +224,7 @@ result = /obj/item/crowbar/bronze reqs = list(/obj/item/crowbar = 1, /obj/item/stack/cable_coil = 10, - /obj/item/stack/tile/bronze = 1, + /obj/item/stack/sheet/bronze = 1, /datum/reagent/water = 15) time = 40 subcategory = CAT_TOOL @@ -236,7 +236,7 @@ result = /obj/item/wrench/bronze reqs = list(/obj/item/wrench = 1, /obj/item/stack/cable_coil = 10, - /obj/item/stack/tile/bronze = 1, + /obj/item/stack/sheet/bronze = 1, /datum/reagent/water = 15) time = 40 subcategory = CAT_TOOL @@ -269,6 +269,19 @@ subcategory = CAT_TOOL category = CAT_MISC +/datum/crafting_recipe/heretic/codex + name = "Codex Cicatrix" + result = /obj/item/forbidden_book + tools = list(/obj/item/pen) + reqs = list(/obj/item/paper = 5, + /obj/item/organ/eyes = 1, + /obj/item/organ/heart = 1, + /obj/item/stack/sheet/animalhide/human = 1) + time = 150 + subcategory = CAT_MISCELLANEOUS + category = CAT_MISC + always_availible = FALSE + //////////// //Vehicles// //////////// diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index 460a73bf5a..4a9c558d39 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -29,8 +29,8 @@ GLOBAL_LIST_EMPTY(uplinks) var/saved_player_population = 0 var/list/filters = list() - -/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate, datum/traitor_class/traitor_class) + +/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/traitor_class/traitor_class) if(!isitem(parent)) return COMPONENT_INCOMPATIBLE @@ -144,6 +144,8 @@ GLOBAL_LIST_EMPTY(uplinks) return COMPONENT_NO_INTERACT /datum/component/uplink/ui_state(mob/user) + if(istype(parent, /obj/item/implant/uplink)) + return GLOB.not_incapacitated_state return GLOB.inventory_state /datum/component/uplink/ui_interact(mob/user, datum/tgui/ui) diff --git a/code/datums/elements/bsa_blocker.dm b/code/datums/elements/bsa_blocker.dm new file mode 100644 index 0000000000..61140ad0ed --- /dev/null +++ b/code/datums/elements/bsa_blocker.dm @@ -0,0 +1,10 @@ +//blocks bluespace artillery beams that try to fly through +//look not all elements need to be fancy +/datum/element/bsa_blocker/Attach(datum/target) + if(!isatom(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_ATOM_BSA_BEAM, .proc/block_bsa) + return ..() + +/datum/element/bsa_blocker/proc/block_bsa() + return COMSIG_ATOM_BLOCKS_BSA_BEAM diff --git a/code/datums/hud.dm b/code/datums/hud.dm index c1811fb9b3..1c23d5f2a0 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -28,7 +28,8 @@ GLOBAL_LIST_INIT(huds, list( ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(), ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(), ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(), - ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag() + ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag(), + ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden() )) /datum/atom_hud diff --git a/code/datums/materials/basemats.dm b/code/datums/materials/basemats.dm index 721af65449..1469965b57 100644 --- a/code/datums/materials/basemats.dm +++ b/code/datums/materials/basemats.dm @@ -287,7 +287,7 @@ Unless you know what you're doing, only use the first three numbers. They're in desc = "Clock Cult? Never heard of it." color = "#92661A" categories = list(MAT_CATEGORY_RIGID = TRUE, MAT_CATEGORY_BASE_RECIPES = TRUE) - sheet_type = /obj/item/stack/tile/bronze + sheet_type = /obj/item/stack/sheet/bronze value_per_unit = 0.025 armor_modifiers = list("melee" = 1, "bullet" = 1, "laser" = 1, "energy" = 1, "bomb" = 1, "bio" = 1, "rad" = 1.5, "fire" = 1.5, "acid" = 1.5) beauty_modifier = 0.2 diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm index 787d6375a7..38b004a657 100644 --- a/code/datums/mood_events/generic_negative_events.dm +++ b/code/datums/mood_events/generic_negative_events.dm @@ -162,6 +162,11 @@ mood_change = -8 timeout = 3 MINUTES +/datum/mood_event/gates_of_mansus + description = "LIVING IN A PERFORMANCE IS WORSE THAN DEATH\n" + mood_change = -25 + timeout = 4 MINUTES + //These are unused so far but I want to remember them to use them later /datum/mood_event/cloned_corpse diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm index 8f98917ed3..b73756c2b1 100644 --- a/code/datums/mood_events/generic_positive_events.dm +++ b/code/datums/mood_events/generic_positive_events.dm @@ -70,6 +70,11 @@ mood_change = 40 //maybe being a cultist isnt that bad after all hidden = TRUE +/datum/mood_event/heretics + description = "THE HIGHER I RISE, THE MORE I SEE.\n" + mood_change = 12 //maybe being a cultist isnt that bad after all + hidden = TRUE + /datum/mood_event/family_heirloom description = "My family heirloom is safe with me.\n" mood_change = 1 diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm index 5d86290f8f..64b8692ccd 100644 --- a/code/datums/ruins/space.dm +++ b/code/datums/ruins/space.dm @@ -325,6 +325,12 @@ name = "Abductor Replication Lab" description = "Some scientists tried and almost succeeded to recreate abductor tools. Somewhat slower and a bit less modern than their originals, these tools are the best you can get if you aren't an alien." +/datum/map_template/ruin/space/spacediner + id = "spacediner" + suffix = "spacediner.dmm" + name = "Space Diner" + description = "Come, traveler of the bluespace planes. Sit, enjoy a drink and take one of the fair maidens for a night. The exit is the way you came in, via that teleporter thingy, but do remember to stay safe." + //Space ruins for the station z /datum/map_template/ruin/spacenearstation prefix = "_maps/RandomRuins/SpaceRuinsStation/" diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 6e59d4fa2c..9cd47403d1 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -403,6 +403,197 @@ owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point. ..() +/datum/status_effect/eldritch + duration = 15 SECONDS + status_type = STATUS_EFFECT_REPLACE + alert_type = null + on_remove_on_mob_delete = TRUE + ///underlay used to indicate that someone is marked + var/mutable_appearance/marked_underlay + ///path for the underlay + var/effect_sprite = "" + +/datum/status_effect/eldritch/on_creation(mob/living/new_owner, ...) + marked_underlay = mutable_appearance('icons/effects/effects.dmi', effect_sprite,BELOW_MOB_LAYER) + return ..() + +/datum/status_effect/eldritch/on_apply() + . = ..() + if(owner.mob_size >= MOB_SIZE_HUMAN) + RegisterSignal(owner,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/update_owner_underlay) + owner.update_icon() + return TRUE + return FALSE + +/datum/status_effect/eldritch/on_remove() + UnregisterSignal(owner,COMSIG_ATOM_UPDATE_OVERLAYS) + owner.update_icon() + return ..() + +/datum/status_effect/eldritch/proc/update_owner_underlay(atom/source, list/overlays) + overlays += marked_underlay + +/datum/status_effect/eldritch/Destroy() + QDEL_NULL(marked_underlay) + return ..() + +/** + * What happens when this mark gets popped + * + * Adds actual functionality to each mark + */ +/datum/status_effect/eldritch/proc/on_effect() + playsound(owner, 'sound/magic/repulse.ogg', 75, TRUE) + qdel(src) //what happens when this is procced. + +//Each mark has diffrent effects when it is destroyed that combine with the mansus grasp effect. +/datum/status_effect/eldritch/flesh + id = "flesh_mark" + effect_sprite = "emark1" + +/datum/status_effect/eldritch/flesh/on_effect() + + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + var/obj/item/bodypart/bodypart = pick(H.bodyparts) + var/datum/wound/slash/severe/crit_wound = new + crit_wound.apply_wound(bodypart) + return ..() + +/datum/status_effect/eldritch/ash + id = "ash_mark" + effect_sprite = "emark2" + ///Dictates how much damage and stamina loss this mark will cause. + var/repetitions = 1 + +/datum/status_effect/eldritch/ash/on_creation(mob/living/new_owner, _repetition = 5) + . = ..() + repetitions = min(1,_repetition) + +/datum/status_effect/eldritch/ash/on_effect() + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.adjustStaminaLoss(10 * repetitions) + carbon_owner.adjustFireLoss(5 * repetitions) + for(var/mob/living/carbon/victim in range(1,carbon_owner)) + if(IS_HERETIC(victim) || victim == carbon_owner) + continue + victim.apply_status_effect(type,repetitions-1) + break + return ..() + +/datum/status_effect/eldritch/rust + id = "rust_mark" + effect_sprite = "emark3" + +/datum/status_effect/eldritch/rust/on_effect() + if(!iscarbon(owner)) + return + var/mob/living/carbon/carbon_owner = owner + for(var/obj/item/I in carbon_owner.get_all_gear()) //Affects roughly 75% of items + if(!QDELETED(I) && prob(75)) //Just in case + I.take_damage(100) + return ..() + +/datum/status_effect/corrosion_curse + id = "corrosion_curse" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + tick_interval = 1 SECONDS + +/datum/status_effect/corrosion_curse/on_creation(mob/living/new_owner, ...) + . = ..() + to_chat(owner, "Your feel your body starting to break apart...") + +/datum/status_effect/corrosion_curse/tick() + . = ..() + if(!ishuman(owner)) + return + var/mob/living/carbon/human/H = owner + var/chance = rand(0,100) + switch(chance) + if(0 to 19) + H.vomit() + if(20 to 29) + H.Dizzy(10) + if(30 to 39) + H.adjustOrganLoss(ORGAN_SLOT_LIVER,5) + if(40 to 49) + H.adjustOrganLoss(ORGAN_SLOT_HEART,5) + if(50 to 59) + H.adjustOrganLoss(ORGAN_SLOT_STOMACH,5) + if(60 to 69) + H.adjustOrganLoss(ORGAN_SLOT_EYES,10) + if(70 to 79) + H.adjustOrganLoss(ORGAN_SLOT_EARS,10) + if(80 to 89) + H.adjustOrganLoss(ORGAN_SLOT_LUNGS,10) + if(90 to 99) + H.adjustOrganLoss(ORGAN_SLOT_TONGUE,10) + if(100) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN,20) + +/datum/status_effect/amok + id = "amok" + status_type = STATUS_EFFECT_REPLACE + alert_type = null + duration = 10 SECONDS + tick_interval = 1 SECONDS + +/datum/status_effect/amok/on_apply(mob/living/afflicted) + . = ..() + to_chat(owner, "Your feel filled with a rage that is not your own!") + +/datum/status_effect/amok/tick() + . = ..() + var/prev_intent = owner.a_intent + owner.a_intent = INTENT_HARM + + var/list/mob/living/targets = list() + for(var/mob/living/potential_target in oview(owner, 1)) + if(IS_HERETIC(potential_target) || potential_target.mind?.has_antag_datum(/datum/antagonist/heretic_monster)) + continue + targets += potential_target + if(LAZYLEN(targets)) + owner.log_message(" attacked someone due to the amok debuff.", LOG_ATTACK) //the following attack will log itself + owner.ClickOn(pick(targets)) + owner.a_intent = prev_intent + +/datum/status_effect/cloudstruck + id = "cloudstruck" + status_type = STATUS_EFFECT_REPLACE + duration = 3 SECONDS + on_remove_on_mob_delete = TRUE + ///This overlay is applied to the owner for the duration of the effect. + var/mutable_appearance/mob_overlay + +/datum/status_effect/cloudstruck/on_creation(mob/living/new_owner, set_duration) + if(isnum(set_duration)) + duration = set_duration + . = ..() + +/datum/status_effect/cloudstruck/on_apply() + . = ..() + mob_overlay = mutable_appearance('icons/effects/eldritch.dmi', "cloud_swirl", ABOVE_MOB_LAYER) + owner.overlays += mob_overlay + owner.update_icon() + ADD_TRAIT(owner, TRAIT_BLIND, "cloudstruck") + return TRUE + +/datum/status_effect/cloudstruck/on_remove() + . = ..() + if(QDELETED(owner)) + return + REMOVE_TRAIT(owner, TRAIT_BLIND, "cloudstruck") + if(owner) + owner.overlays -= mob_overlay + owner.update_icon() + +/datum/status_effect/cloudstruck/Destroy() + . = ..() + QDEL_NULL(mob_overlay) + + /datum/status_effect/stacking/saw_bleed id = "saw_bleed" tick_interval = 6 diff --git a/code/game/atoms.dm b/code/game/atoms.dm index dfa9776489..6e1692c17e 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1121,3 +1121,11 @@ max_grav = max(G.setting,max_grav) return max_grav return SSmapping.level_trait(T.z, ZTRAIT_GRAVITY) + +/** + * Causes effects when the atom gets hit by a rust effect from heretics + * + * Override this if you want custom behaviour in whatever gets hit by the rust + */ +/atom/proc/rust_heretic_act() + return diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index da33da5f43..63574c6475 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -191,6 +191,24 @@ SSticker.mode_result = "loss - rev heads killed" SSticker.news_report = REVS_LOSE +////////////////////////////////////////////// +// // +// HERETIC SMUGGLER // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/latejoin/heretic_smuggler + name = "Heretic Smuggler" + antag_datum = /datum/antagonist/heretic + antag_flag = ROLE_HERETIC + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") + restricted_roles = list("AI","Cyborg") + required_candidates = 1 + weight = 4 + cost = 10 + requirements = list(40,30,20,10,10,10,10,10,10,10) + repeatable = TRUE + ////////////////////////////////////////////// // // // BLOODSUCKERS // diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index edaadeae1c..766ddcefc7 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -143,6 +143,46 @@ changeling.add_antag_datum(new_antag) return TRUE +////////////////////////////////////////////// +// // +// ELDRITCH CULT // +// // +////////////////////////////////////////////// + +/datum/dynamic_ruleset/roundstart/heretics + name = "Heretics" + antag_flag = ROLE_HERETIC + antag_datum = /datum/antagonist/heretic + protected_roles = list("Prisoner","Security Officer", "Warden", "Detective", "Head of Security", "Captain") + restricted_roles = list("AI", "Cyborg") + required_candidates = 1 + weight = 3 + cost = 20 + scaling_cost = 15 + requirements = list(50,45,45,40,35,20,20,15,10,10) + antag_cap = list(1,1,1,1,2,2,2,2,3,3) + + +/datum/dynamic_ruleset/roundstart/heretics/pre_execute() + . = ..() + var/num_ecult = antag_cap[indice_pop] * (scaled_times + 1) + + for (var/i = 1 to num_ecult) + var/mob/picked_candidate = pick_n_take(candidates) + assigned += picked_candidate.mind + picked_candidate.mind.restricted_roles = restricted_roles + picked_candidate.mind.special_role = ROLE_HERETIC + return TRUE + +/datum/dynamic_ruleset/roundstart/heretics/execute() + + for(var/c in assigned) + var/datum/mind/cultie = c + var/datum/antagonist/heretic/new_antag = new antag_datum() + cultie.add_antag_datum(new_antag) + + return TRUE + ////////////////////////////////////////////// // // // WIZARDS // diff --git a/code/game/gamemodes/eldritch_cult/eldritch_cult.dm b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm new file mode 100644 index 0000000000..20a129a5a3 --- /dev/null +++ b/code/game/gamemodes/eldritch_cult/eldritch_cult.dm @@ -0,0 +1,67 @@ +/datum/game_mode/heretics + name = "heresy" + config_tag = "heresy" + antag_flag = ROLE_HERETIC + false_report_weight = 5 + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster") //citadel change - adds HoP, CE, CMO, and RD to heretic role blacklist + required_players = 15 + required_enemies = 1 + recommended_enemies = 4 + reroll_friendly = 1 + enemy_minimum_age = 0 + round_ends_with_antag_death = 0 + + announce_span = "danger" + announce_text = "Heretics have been spotted on the station!\n\ + Heretics: Accomplish your objectives.\n\ + Crew: Do not let the madman succeed!" + + var/ecult_possible = 4 //hard limit on culties if scaling is turned off + var/num_ecult = 1 + var/list/culties = list() + +/datum/game_mode/heretics/pre_setup() + + if(CONFIG_GET(flag/protect_roles_from_antagonist)) + restricted_jobs += protected_jobs + + if(CONFIG_GET(flag/protect_assistant_from_antagonist)) + restricted_jobs += "Assistant" + + + var/esc = CONFIG_GET(number/ecult_scaling_coeff) + if(esc) + num_ecult = min(max(1, min(round(num_players() / (esc * 2)) + 2, round(num_players() / esc))),4) + else + num_ecult = max(1, min(num_players(), ecult_possible)) + + for(var/i in 1 to num_ecult) + if(!antag_candidates.len) + break + var/datum/mind/cultie = antag_pick(antag_candidates) + antag_candidates -= cultie + cultie.special_role = ROLE_HERETIC + cultie.restricted_roles = restricted_jobs + culties += cultie + + var/enough_heretics = culties.len > 0 + + if(!enough_heretics) + setup_error = "Not enough heretic candidates" + return FALSE + else + for(var/antag in culties) + return TRUE + +/datum/game_mode/heretics/post_setup() + for(var/c in culties) + var/datum/mind/cultie = c + log_game("[key_name(cultie)] has been selected as a heretic!") + var/datum/antagonist/heretic/new_antag = new() + cultie.add_antag_datum(new_antag) + return ..() + +/datum/game_mode/heretics/generate_report() + return "Cybersun Industries has announced that they have successfully raided a high-security library. The library contained a very dangerous book that was \ + shown to posses anomalous properties. We suspect that the book has been copied over, Stay vigilant!" diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 2415770e66..047e6c955f 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -12,9 +12,9 @@ density = FALSE state_open = TRUE circuit = /obj/item/circuitboard/machine/sleeper - req_access = list(ACCESS_CMO) //Used for reagent deletion and addition of non medicines + var/efficiency = 1 - var/min_health = 30 + var/min_health = -25 var/list/available_chems var/controls_inside = FALSE var/list/possible_chems = list( @@ -31,32 +31,13 @@ /obj/machinery/sleeper/Initialize() . = ..() - create_reagents(500, NO_REACT) + // if(mapload) + // component_parts -= circuit + // QDEL_NULL(circuit) occupant_typecache = GLOB.typecache_living update_icon() reset_chem_buttons() RefreshParts() - add_inital_chems() - new_occupant_dir = dir - -/obj/machinery/sleeper/setDir(newdir) - . = ..() - new_occupant_dir = dir - -/obj/machinery/sleeper/on_deconstruction() - var/obj/item/reagent_containers/sleeper_buffer/buffer = new (loc) - buffer.volume = reagents.maximum_volume - buffer.reagents.maximum_volume = reagents.maximum_volume - reagents.trans_to(buffer.reagents, reagents.total_volume) - -/obj/machinery/sleeper/proc/add_inital_chems() - for(var/i in available_chems) - var/datum/reagent/R = reagents.has_reagent(i) - if(!R) - reagents.add_reagent(i, (20)) - continue - if(R.volume < 20) - reagents.add_reagent(i, (20 - R.volume)) /obj/machinery/sleeper/RefreshParts() var/E @@ -73,15 +54,11 @@ available_chems |= possible_chems[i] reset_chem_buttons() - //Total container size 500 - 2000u - if(reagents) - reagents.maximum_volume = (500*E) - - /obj/machinery/sleeper/update_icon_state() - icon_state = initial(icon_state) if(state_open) - icon_state += "-open" + icon_state = "[initial(icon_state)]-open" + else + icon_state = initial(icon_state) /obj/machinery/sleeper/container_resist(mob/living/user) visible_message("[occupant] emerges from [src]!", @@ -98,10 +75,12 @@ /obj/machinery/sleeper/open_machine() if(!state_open && !panel_open) + // flick("[initial(icon_state)]-anim", src) ..() /obj/machinery/sleeper/close_machine(mob/user) if((isnull(user) || istype(user)) && state_open && !panel_open) + // flick("[initial(icon_state)]-anim", src) ..(user) var/mob/living/mob_occupant = occupant if(mob_occupant && mob_occupant.stat != DEAD) @@ -112,46 +91,23 @@ if (. & EMP_PROTECT_SELF) return if(is_operational() && occupant) - var/datum/reagent/R = pick(reagents.reagent_list) + var/datum/reagent/R = pick(reagents.reagent_list) //cit specific inject_chem(R.type, occupant) open_machine() - //Is this too much? + //Is this too much? Cit specific if(severity == EMP_HEAVY) var/chem = pick(available_chems) available_chems -= chem available_chems += get_random_reagent_id() reset_chem_buttons() -/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/sleeper_buffer)) - var/obj/item/reagent_containers/sleeper_buffer/SB = I - if((SB.reagents.total_volume + reagents.total_volume) < reagents.maximum_volume) - SB.reagents.trans_to(reagents, SB.reagents.total_volume) - visible_message("[user] places the [SB] into the [src].") - qdel(SB) - return - else - SB.reagents.trans_to(reagents, SB.reagents.total_volume) - visible_message("[user] adds as much as they can to the [src] from the [SB].") - return - if(istype(I, /obj/item/reagent_containers)) - var/obj/item/reagent_containers/RC = I - if(RC.reagents.total_volume == 0) - to_chat(user, "The [I] is empty!") - for(var/datum/reagent/R in RC.reagents.reagent_list) - if((obj_flags & EMAGGED) || (allowed(usr))) - break - if(!istype(R, /datum/reagent/medicine)) - visible_message("The [src] gives out a hearty boop and rejects the [I]. The Sleeper's screen flashes with a pompous \"Medicines only, please.\"") - return - RC.reagents.trans_to(reagents, 1000) - visible_message("[user] adds as much as they can to the [src] from the [I].") - return - - /obj/machinery/sleeper/MouseDrop_T(mob/target, mob/user) - if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) + if(user.stat || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser()) return + if(isliving(user)) + var/mob/living/L = user + if(!(L.mobility_flags & MOBILITY_STAND)) + return close_machine(target) /obj/machinery/sleeper/screwdriver_act(mob/living/user, obj/item/I) @@ -187,23 +143,9 @@ visible_message("[usr] pries open [src].", "You pry open [src].") open_machine() -/obj/machinery/sleeper/AltClick(mob/user) - . = ..() - if(!user.canUseTopic(src, !hasSiliconAccessInArea(user))) - return - if(state_open) - close_machine() - else - open_machine() - return TRUE - -/obj/machinery/sleeper/examine(mob/user) - . = ..() - . += "Alt-click [src] to [state_open ? "close" : "open"] it." - /obj/machinery/sleeper/ui_state(mob/user) if(controls_inside) - return GLOB.notcontained_state + return GLOB.contained_state return GLOB.default_state /obj/machinery/sleeper/ui_interact(mob/user, datum/tgui/ui) @@ -212,6 +154,18 @@ ui = new(user, src, "Sleeper", name) ui.open() +/obj/machinery/sleeper/AltClick(mob/user) + if(!user.canUseTopic(src, !issilicon(user))) + return + if(state_open) + close_machine() + else + open_machine() + +/obj/machinery/sleeper/examine(mob/user) + . = ..() + . += "Alt-click [src] to [state_open ? "close" : "open"] it." + /obj/machinery/sleeper/process() ..() check_nap_violations() @@ -221,22 +175,13 @@ /obj/machinery/sleeper/ui_data() var/list/data = list() - var/chemical_list = list() - var/blood_percent = 0 - data["occupied"] = occupant ? 1 : 0 data["open"] = state_open - data["blood_levels"] = blood_percent - data["blood_status"] = "Patient either has no blood, or does not require it to function." - data["chemical_list"] = chemical_list data["chems"] = list() for(var/chem in available_chems) - var/datum/reagent/R = reagents.has_reagent(chem) - R = GLOB.chemical_reagents_list[chem] - data["synthchems"] += list(list("name" = R.name, "id" = R.type, "synth_allowed" = synth_allowed(chem))) - for(var/datum/reagent/R in reagents.reagent_list) - data["chems"] += list(list("name" = R.name, "id" = R.type, "vol" = R.volume, "purity" = R.purity, "allowed" = chem_allowed(R.type))) + var/datum/reagent/R = GLOB.chemical_reagents_list[chem] + data["chems"] += list(list("name" = R.name, "id" = R.type, "allowed" = chem_allowed(chem))) data["occupant"] = list() var/mob/living/mob_occupant = occupant @@ -264,40 +209,10 @@ data["occupant"]["fireLoss"] = mob_occupant.getFireLoss() data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss() data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN) - - if(mob_occupant.reagents.reagent_list.len) + data["occupant"]["reagents"] = list() + if(mob_occupant.reagents && mob_occupant.reagents.reagent_list.len) for(var/datum/reagent/R in mob_occupant.reagents.reagent_list) - chemical_list += list(list("name" = R.name, "volume" = R.volume)) - else - chemical_list = "Patient has no reagents." - - data["occupant"]["failing_organs"] = list() - var/mob/living/carbon/C = mob_occupant - if(C) - for(var/obj/item/organ/Or in C.getFailingOrgans()) - if(istype(Or, /obj/item/organ/brain)) - continue - data["occupant"]["failing_organs"] += list(list("name" = Or.name)) - - if(istype(C)) //Non-carbons shouldn't be able to enter sleepers, but this is to prevent runtimes if something ever breaks - if(mob_occupant.has_dna()) // Blood-stuff is mostly a copy-paste from the healthscanner. - blood_percent = round((C.blood_volume / BLOOD_VOLUME_NORMAL)*100) - var/blood_id = C.get_blood_id() - var/blood_warning = "" - if(blood_percent < 80) - blood_warning = "Patient has low blood levels." - if(blood_percent < 60) - blood_warning = "Patient has DANGEROUSLY low blood levels." - if(blood_id) - var/blood_type = C.dna.blood_type - if(!(blood_id in GLOB.blood_reagent_types)) // special blood substance - var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] - if(R) - blood_type = R.name - else - blood_type = blood_id - data["blood_status"] = "Patient has [blood_type] type blood. [blood_warning]" - data["blood_levels"] = blood_percent + data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume)) return data /obj/machinery/sleeper/ui_act(action, params) @@ -314,37 +229,14 @@ . = TRUE if("inject") var/chem = text2path(params["chem"]) - var/amount = text2num(params["volume"]) if(!is_operational() || !mob_occupant || isnull(chem)) return if(mob_occupant.health < min_health && chem != /datum/reagent/medicine/epinephrine) return - if(inject_chem(chem, usr, amount)) + if(inject_chem(chem, usr)) . = TRUE if(scrambled_chems && prob(5)) to_chat(usr, "Chemical system re-route detected, results may not be as expected!") - if("synth") - var/chem = text2path(params["chem"]) - if(!is_operational()) - return - reagents.add_reagent(chem_buttons[chem], 10) //other_purity = 0.75 for when the mechanics are in - if("purge") - var/chem = text2path(params["chem"]) - if(allowed(usr)) - if(!is_operational()) - return - reagents.remove_reagent(chem, 1000) - return - if(chem in available_chems) - if(!is_operational()) - return - /*var/datum/reagent/R = reagents.has_reagent(chem) //For when purity effects are in - if(R.purity < 0.8)*/ - reagents.remove_reagent(chem, 1000) - else - visible_message("Access Denied.") - playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - /obj/machinery/sleeper/emag_act(mob/user) . = ..() @@ -353,10 +245,9 @@ to_chat(user, "You scramble the sleeper's user interface!") return TRUE -//trans to -/obj/machinery/sleeper/proc/inject_chem(chem, mob/user, volume = 10) - if(chem_allowed(chem)) - reagents.trans_id_to(occupant, chem, volume)//emag effect kicks in here so that the "intended" chem is used for all checks, for extra FUUU +/obj/machinery/sleeper/proc/inject_chem(chem, mob/user) + if((chem in available_chems) && chem_allowed(chem)) + occupant.reagents.add_reagent(chem_buttons[chem], 10) //emag effect kicks in here so that the "intended" chem is used for all checks, for extra FUUU if(user) log_combat(user, occupant, "injected [chem] into", addition = "via [src]") return TRUE @@ -369,14 +260,6 @@ var/occ_health = mob_occupant.health > min_health || chem == /datum/reagent/medicine/epinephrine return amount && occ_health -/obj/machinery/sleeper/proc/synth_allowed(chem) - var/datum/reagent/R = reagents.has_reagent(chem) - if(!R) - return TRUE - if(R.volume < 50) - return TRUE - return FALSE - /obj/machinery/sleeper/proc/reset_chem_buttons() scrambled_chems = FALSE LAZYINITLIST(chem_buttons) @@ -416,23 +299,6 @@ component_parts += new /obj/item/stack/cable_coil(null) RefreshParts() -/obj/machinery/sleeper/clockwork - name = "soothing sleeper" - desc = "A large cryogenics unit built from brass. Its surface is pleasantly cool the touch." - icon_state = "sleeper_clockwork" - enter_message = "You hear the gentle hum and click of machinery, and are lulled into a sense of peace." - possible_chems = list(list(/datum/reagent/medicine/epinephrine, /datum/reagent/medicine/salbutamol, /datum/reagent/medicine/bicaridine, - /datum/reagent/medicine/kelotane, /datum/reagent/medicine/oculine, /datum/reagent/medicine/inacusiate, /datum/reagent/medicine/mannitol)) - -/obj/machinery/sleeper/clockwork/process() - if(occupant && isliving(occupant)) - var/mob/living/L = occupant - if(GLOB.clockwork_vitality) //If there's Vitality, the sleeper has passive healing - GLOB.clockwork_vitality = max(0, GLOB.clockwork_vitality - 1) - L.adjustBruteLoss(-1) - L.adjustFireLoss(-1) - L.adjustOxyLoss(-5) - /obj/machinery/sleeper/old icon_state = "oldpod" @@ -443,8 +309,6 @@ idle_power_usage = 3000 circuit = /obj/item/circuitboard/machine/sleeper/party var/leddit = FALSE //Get it like reddit and lead alright fine - ui_x = 310 - ui_y = 400 controls_inside = TRUE possible_chems = list( @@ -477,3 +341,23 @@ /obj/machinery/sleeper/party/emag_act(mob/user) ..() leddit = TRUE + +/obj/machinery/sleeper/clockwork + name = "soothing sleeper" + desc = "A large cryogenics unit built from brass. Its surface is pleasantly cool the touch." + icon_state = "sleeper_clockwork" + enter_message = "You hear the gentle hum and click of machinery, and are lulled into a sense of peace." + possible_chems = list( + list(/datum/reagent/medicine/epinephrine, /datum/reagent/medicine/salbutamol, /datum/reagent/medicine/bicaridine, /datum/reagent/medicine/kelotane, /datum/reagent/medicine/oculine, /datum/reagent/medicine/inacusiate, /datum/reagent/medicine/mannitol) + ) //everything is available at start + fair_market_price = 0 //it's free + +/obj/machinery/sleeper/clockwork/process() + ..() + if(occupant && isliving(occupant)) + var/mob/living/L = occupant + if(GLOB.clockwork_vitality) //If there's Vitality, the sleeper has passive healing + GLOB.clockwork_vitality = max(0, GLOB.clockwork_vitality - 1) + L.adjustBruteLoss(-1) + L.adjustFireLoss(-1) + L.adjustOxyLoss(-5) diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index 3faf6d5727..bd4ac5f2d1 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -92,6 +92,7 @@ Class Procs: pressure_resistance = 15 max_integrity = 200 layer = BELOW_OBJ_LAYER //keeps shit coming out of the machine from ending up underneath it. + flags_1 = DEFAULT_RICOCHET_1 flags_ricochet = RICOCHET_HARD ricochet_chance_mod = 0.3 @@ -546,3 +547,6 @@ Class Procs: . = . % 9 AM.pixel_x = -8 + ((.%3)*8) AM.pixel_y = -8 + (round( . / 3)*8) + +/obj/machinery/rust_heretic_act() + take_damage(500, BRUTE, "melee", 1) diff --git a/code/game/machinery/doors/airlock_types.dm b/code/game/machinery/doors/airlock_types.dm index 530a287134..b34e97d374 100644 --- a/code/game/machinery/doors/airlock_types.dm +++ b/code/game/machinery/doors/airlock_types.dm @@ -310,6 +310,17 @@ opacity = 0 glass = TRUE +/obj/machinery/door/airlock/bronze + name = "bronze airlock" + icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi' + overlays_file = 'icons/obj/doors/airlocks/clockwork/overlays.dmi' + assemblytype = /obj/structure/door_assembly/door_assembly_bronze + +/obj/machinery/door/airlock/bronze/seethru + assemblytype = /obj/structure/door_assembly/door_assembly_bronze/seethru + opacity = 0 + glass = TRUE + ////////////////////////////////// /* Station2 Airlocks diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 4248e1684f..d4ba70bb6c 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -11,7 +11,7 @@ max_integrity = 350 armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) CanAtmosPass = ATMOS_PASS_DENSITY - flags_1 = PREVENT_CLICK_UNDER_1 + flags_1 = PREVENT_CLICK_UNDER_1|DEFAULT_RICOCHET_1 ricochet_chance_mod = 0.8 interaction_flags_atom = INTERACT_ATOM_UI_INTERACT diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 2d41575e70..228c2e1f52 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -298,7 +298,7 @@ /obj/machinery/door/firedoor/border_only icon = 'icons/obj/doors/edge_Doorfire.dmi' - flags_1 = ON_BORDER_1 + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 CanAtmosPass = ATMOS_PASS_PROC /obj/machinery/door/firedoor/border_only/closed @@ -320,7 +320,7 @@ to_chat(M, "You pull [M.pulling] through [src] right as it closes") M.pulling.forceMove(T1) M.start_pulling(M2) - + for(var/mob/living/M in T2) if(M.stat == CONSCIOUS && M.pulling && M.pulling.loc == T1 && !M.pulling.anchored && M.pulling.move_resist <= M.move_force) var/mob/living/M2 = M.pulling diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index c9c577231e..22bacf6aa1 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -11,7 +11,7 @@ integrity_failure = 0 armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) visible = FALSE - flags_1 = ON_BORDER_1 + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 opacity = 0 CanAtmosPass = ATMOS_PASS_PROC interaction_flags_machine = INTERACT_MACHINE_WIRES_IF_OPEN | INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN_SILICON | INTERACT_MACHINE_REQUIRES_SILICON | INTERACT_MACHINE_OPEN diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 5b691607db..f16a5f9292 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -153,6 +153,9 @@ /obj/mecha/get_cell() return cell +/obj/mecha/rust_heretic_act() + take_damage(500, BRUTE) + /obj/mecha/Destroy() go_out() var/mob/living/silicon/ai/AI diff --git a/code/game/objects/effects/spawners/bundle.dm b/code/game/objects/effects/spawners/bundle.dm index 3f2701c03f..b75df644eb 100644 --- a/code/game/objects/effects/spawners/bundle.dm +++ b/code/game/objects/effects/spawners/bundle.dm @@ -168,3 +168,26 @@ items = list( /obj/item/clothing/mask/gas/sexymime, /obj/item/clothing/under/rank/civilian/mime/sexy) + +/obj/effect/spawner/bundle/crate/Initialize(mapload) + if(items && items.len) + var/turf/T = get_turf(src) + var/obj/structure/closet/LC = locate(/obj/structure/closet) in T + if(LC) + for(var/path in items) + new path(LC) + return INITIALIZE_HINT_QDEL + +/obj/effect/spawner/bundle/crate/mosin + name = "Mosin-Nagant spawner" + items = list( + /obj/item/gun/ballistic/shotgun/boltaction, + /obj/item/ammo_box/a762 + ) + +/obj/effect/spawner/bundle/crate/surplusrifle + name = "surplus rifle spawner" + items = list( + /obj/item/gun/ballistic/automatic/surplus, + /obj/item/ammo_box/magazine/m10mm/rifle + ) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 396f463136..8d5458333c 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -139,6 +139,23 @@ /obj/effect/temp_visual/dir_setting/curse/hand icon_state = "cursehand" +/obj/effect/temp_visual/bsa_splash + name = "\improper Bluespace energy wave" + desc = "A massive, rippling wave of bluepace energy, all rapidly exhausting itself the moment it leaves the concentrated beam of light." + icon = 'icons/effects/beam_splash.dmi' + icon_state = "beam_splash_l" + layer = ABOVE_ALL_MOB_LAYER + pixel_y = -16 + duration = 50 + +/obj/effect/temp_visual/bsa_splash/Initialize(mapload, dir) + . = ..() + switch(dir) + if(WEST) + icon_state = "beam_splash_w" + if(EAST) + icon_state = "beam_splash_e" + /obj/effect/temp_visual/wizard name = "water" icon = 'icons/mob/mob.dmi' diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm index 9db9449fcb..cf5c3d4fc5 100644 --- a/code/game/objects/items/dualsaber.dm +++ b/code/game/objects/items/dualsaber.dm @@ -51,6 +51,10 @@ // no attacking while blocking block_lock_attacking = TRUE block_projectile_mitigation = 75 + // more efficient vs projectiles + block_stamina_efficiency_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 4 + ) parry_time_windup = 0 parry_time_active = 8 @@ -65,14 +69,20 @@ parry_imperfect_falloff_percent = 10 parry_efficiency_to_counterattack = 100 parry_efficiency_considered_successful = 25 // VERY generous - parry_efficiency_perfect = 90 parry_failed_stagger_duration = 3 SECONDS parry_failed_clickcd_duration = CLICK_CD_MELEE - // more efficient vs projectiles - block_stamina_efficiency_override = list( - TEXT_ATTACK_TYPE_PROJECTILE = 4 - ) +/obj/item/dualsaber/active_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return, override_direction) + if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object)) + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER + return BLOCK_SUCCESS | BLOCK_REDIRECTED | BLOCK_SHOULD_REDIRECT + return ..() + +/obj/item/dualsaber/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time) + . = ..() + if(parry_efficiency >= 90) // perfect parry + block_return[BLOCK_RETURN_REDIRECT_METHOD] = REDIRECT_METHOD_RETURN_TO_SENDER + . |= BLOCK_SHOULD_REDIRECT /obj/item/dualsaber/Initialize() . = ..() @@ -342,12 +352,30 @@ name = "divine lightblade" desc = "A giant blade of bright and holy light, said to cut down the wicked with ease." force = 5 - block_chance = 50 armour_penetration = 0 + block_parry_data = /datum/block_parry_data/chaplain var/chaplain_spawnable = TRUE can_reflect = FALSE obj_flags = UNIQUE_RENAME +/datum/block_parry_data/chaplain + parry_stamina_cost = 12 + parry_time_windup = 2 + parry_time_active = 5 + parry_time_spindown = 3 + // parry_flags = PARRY_DEFAULT_HANDLE_FEEDBACK + parry_time_perfect = 1 + parry_time_perfect_leeway = 1 + parry_imperfect_falloff_percent = 7.5 + parry_efficiency_to_counterattack = 100 + parry_efficiency_considered_successful = 80 + parry_efficiency_perfect = 120 + parry_efficiency_perfect_override = list( + TEXT_ATTACK_TYPE_PROJECTILE = 30, + ) + parry_failed_stagger_duration = 3 SECONDS + parry_failed_clickcd_duration = 2 SECONDS + /obj/item/dualsaber/hypereutactic/chaplain/ComponentInitialize() . = ..() AddComponent(/datum/component/two_handed, force_unwielded=5, force_wielded=20, \ diff --git a/code/game/objects/items/granters.dm b/code/game/objects/items/granters.dm index 609cc3b719..979fa7e958 100644 --- a/code/game/objects/items/granters.dm +++ b/code/game/objects/items/granters.dm @@ -253,7 +253,7 @@ user.set_nutrition(NUTRITION_LEVEL_STARVING + 50) /obj/item/book/granter/spell/blind - spell = /obj/effect/proc_holder/spell/targeted/trigger/blind + spell = /obj/effect/proc_holder/spell/pointed/trigger/blind spellname = "blind" icon_state ="bookblind" desc = "This book looks blurry, no matter how you look at it." @@ -265,7 +265,7 @@ user.blind_eyes(10) /obj/item/book/granter/spell/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell = /obj/effect/proc_holder/spell/pointed/mind_transfer spellname = "mindswap" icon_state ="bookmindswap" desc = "This book's cover is pristine, though its pages look ragged and torn." @@ -289,7 +289,7 @@ if(stored_swap == user) to_chat(user,"You stare at the book some more, but there doesn't seem to be anything else to learn...") return - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new if(swapper.cast(list(stored_swap), user, TRUE, TRUE)) to_chat(user,"You're suddenly somewhere else... and someone else?!") to_chat(stored_swap,"Suddenly you're staring at [src] again... where are you, who are you?!") @@ -324,7 +324,7 @@ user.DefaultCombatKnockdown(40) /obj/item/book/granter/spell/barnyard - spell = /obj/effect/proc_holder/spell/targeted/barnyardcurse + spell = /obj/effect/proc_holder/spell/pointed/barnyardcurse spellname = "barnyard" icon_state ="bookhorses" desc = "This book is more horse than your mind has room for." diff --git a/code/game/objects/items/implants/implant_uplink.dm b/code/game/objects/items/implants/implant_uplink.dm index 9895c1e34c..0cac8f838a 100644 --- a/code/game/objects/items/implants/implant_uplink.dm +++ b/code/game/objects/items/implants/implant_uplink.dm @@ -9,7 +9,7 @@ /obj/item/implant/uplink/Initialize(mapload, _owner) . = ..() - AddComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, starting_tc, GLOB.not_incapacitated_state) + AddComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, starting_tc) /obj/item/implanter/uplink name = "implanter (uplink)" diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm index c6b5a7e224..a4b12dd629 100644 --- a/code/game/objects/items/pet_carrier.dm +++ b/code/game/objects/items/pet_carrier.dm @@ -240,7 +240,31 @@ custom_materials = list(/datum/material/glass = 1000, /datum/material/bluespace = 600) escape_time = 200 //equal to the time of a bluespace bodybag alternate_escape_time = 100 + + ///gas supply for simplemobs so they don't die var/datum/gas_mixture/occupant_gas_supply + ///level until the reagent gets INGEST ed instead of TOUCH + var/sipping_level = 150 + ///prob50 level of sipping + var/sipping_probably = 99 + ///chem transfer rate / second + var/transfer_rate = 5 + +/obj/item/pet_carrier/bluespace/Initialize() + . = ..() + create_reagents(300, OPENCONTAINER, DEFAULT_REAGENTS_VALUE) //equivalent of bsbeakers + +/obj/item/pet_carrier/bluespace/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/pet_carrier/bluespace/attack_self(mob/living/user) + ..() + if(reagents) + if(open) + reagents.reagents_holder_flags = OPENCONTAINER + else + reagents.reagents_holder_flags = NONE /obj/item/pet_carrier/bluespace/update_icon_state() if(open) @@ -248,11 +272,28 @@ else icon_state = "bluespace_jar" -/obj/item/pet_carrier/bluespace/throw_impact() +/obj/item/pet_carrier/bluespace/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) . = ..() //delete the item upon impact, releasing the creature inside (this is handled by its deletion) if(occupants.len) loc.visible_message("The bluespace jar smashes, releasing [occupants[1]]!") + + if(reagents?.total_volume && ismob(hit_atom) && hit_atom.reagents) + reagents.total_volume *= rand(5,10) * 0.1 //Not all of it makes contact with the target + var/mob/M = hit_atom + var/R = reagents.log_list() + hit_atom.visible_message("[M] has been splashed with something!", \ + "[M] has been splashed with something!") + var/turf/TT = get_turf(hit_atom) + var/throwerstring + if(thrownby) + log_combat(thrownby, M, "splashed", R) + var/turf/AT = get_turf(thrownby) + throwerstring = " THROWN BY [key_name(thrownby)] at [AT] (AREACOORD(AT)]" + log_reagent("SPLASH: [src] mob throw_impact() onto [key_name(hit_atom)] at [TT] ([AREACOORD(TT)])[throwerstring] - [R]") + reagents.reaction(hit_atom, TOUCH) + reagents.clear_reagents() + playsound(src, "shatter", 70, 1) qdel(src) @@ -260,21 +301,24 @@ . = ..() if(!occupant_gas_supply) occupant_gas_supply = new + if(isanimal(occupant)) var/mob/living/simple_animal/animal = occupant occupant_gas_supply[/datum/gas/oxygen] = 0.0064 //make sure it has some gas in so it isn't depressurized occupant_gas_supply.set_temperature(animal.minbodytemp) //simple animals only care about temperature/pressure when their turf isnt a location - else - if(ishuman(occupant)) //humans require resistance to cold/heat and living in no air while inside, and lose this when outside - ADD_TRAIT(occupant, TRAIT_RESISTCOLD, "bluespace_container_cold_resist") - ADD_TRAIT(occupant, TRAIT_RESISTHEAT, "bluespace_container_heat_resist") - ADD_TRAIT(occupant, TRAIT_NOBREATH, "bluespace_container_no_breath") - ADD_TRAIT(occupant, TRAIT_RESISTHIGHPRESSURE, "bluespace_container_resist_high_pressure") - ADD_TRAIT(occupant, TRAIT_RESISTLOWPRESSURE, "bluespace_container_resist_low_pressure") + + if(ishuman(occupant)) //humans require resistance to cold/heat and living in no air while inside, and lose this when outside + START_PROCESSING(SSobj, src) + ADD_TRAIT(occupant, TRAIT_RESISTCOLD, "bluespace_container_cold_resist") + ADD_TRAIT(occupant, TRAIT_RESISTHEAT, "bluespace_container_heat_resist") + ADD_TRAIT(occupant, TRAIT_NOBREATH, "bluespace_container_no_breath") + ADD_TRAIT(occupant, TRAIT_RESISTHIGHPRESSURE, "bluespace_container_resist_high_pressure") + ADD_TRAIT(occupant, TRAIT_RESISTLOWPRESSURE, "bluespace_container_resist_low_pressure") /obj/item/pet_carrier/bluespace/remove_occupant(mob/living/occupant) . = ..() if(ishuman(occupant)) + STOP_PROCESSING(SSobj, src) REMOVE_TRAIT(occupant, TRAIT_RESISTCOLD, "bluespace_container_cold_resist") REMOVE_TRAIT(occupant, TRAIT_RESISTHEAT, "bluespace_container_heat_resist") REMOVE_TRAIT(occupant, TRAIT_NOBREATH, "bluespace_container_no_breath") @@ -287,6 +331,18 @@ occupant_gas_supply = new return occupant_gas_supply +/obj/item/pet_carrier/bluespace/process() + if(!reagents) + return + for(var/mob/living/L in occupants) + if(!ishuman(L)) + continue + if((reagents.total_volume >= sipping_level) || ((reagents.total_volume >= sipping_probably) && prob(50))) //sipp + reagents.reaction(L, INGEST) //consume + reagents.trans_to(L, transfer_rate) + else + reagents.reaction(L, TOUCH, show_message = FALSE) + /obj/item/pet_carrier/bluespace/load_occupant(mob/living/user, mob/living/target) if(..()) name = "[initial(name)] ([target])" diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm index 44205f4884..a3da49ed89 100644 --- a/code/game/objects/items/plushes.dm +++ b/code/game/objects/items/plushes.dm @@ -688,6 +688,18 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths()) icon_state = "scrubpuppy" item_state = "scrubpuppy" +/obj/item/toy/plush/borgplushie/meddrake + name = "MediDrake Plushie" + desc = "An adorable stuffed toy of a Medidrake." + icon_state = "meddrake" + item_state = "meddrake" + +/obj/item/toy/plush/borgplushie/secdrake + name = "SecDrake Plushie" + desc = "An adorable stuffed toy of a Secdrake." + icon_state = "secdrake" + item_state = "secdrake" + /obj/item/toy/plush/aiplush name = "AI plushie" desc = "A little stuffed toy AI core... it appears to be malfunctioning." diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 11df83230c..9ea8c9c689 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -239,6 +239,9 @@ /obj/item/stack/medical/suture/one amount = 1 +/obj/item/stack/medical/suture/five + amount = 5 + /obj/item/stack/medical/suture/medicated name = "medicated suture" icon_state = "suture_purp" @@ -319,6 +322,9 @@ /obj/item/stack/medical/mesh/one amount = 1 +/obj/item/stack/medical/mesh/five + amount = 5 + /obj/item/stack/medical/mesh/advanced name = "advanced regenerative mesh" desc = "An advanced mesh made with aloe extracts and sterilizing chemicals, used to treat burns." diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 5f6341ea21..305c2ab276 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -672,6 +672,10 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \ GLOBAL_LIST_INIT(bronze_recipes, list ( \ new/datum/stack_recipe("wall gear", /obj/structure/girder/bronze, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \ null, + new/datum/stack_recipe("directional bronze window", /obj/structure/window/bronze/unanchored, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe("fulltile bronze window", /obj/structure/window/bronze/fulltile/unanchored, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe("pinion airlock assembly", /obj/structure/door_assembly/door_assembly_bronze, 4, time = 50, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("bronze pinion airlock assembly", /obj/structure/door_assembly/door_assembly_bronze/seethru, 4, time = 50, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("bronze hat", /obj/item/clothing/head/bronze), \ new/datum/stack_recipe("bronze suit", /obj/item/clothing/suit/bronze), \ new/datum/stack_recipe("bronze boots", /obj/item/clothing/shoes/bronze), \ @@ -679,9 +683,10 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ new/datum/stack_recipe("bronze chair", /obj/structure/chair/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("bronze bar stool", /obj/structure/chair/stool/bar/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("bronze stool", /obj/structure/chair/stool/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \ + new /datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \ )) -/obj/item/stack/tile/bronze +/obj/item/stack/sheet/bronze name = "brass" desc = "On closer inspection, what appears to be wholly-unsuitable-for-building brass is actually more structurally stable bronze." singular_name = "bronze sheet" @@ -690,27 +695,21 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \ icon = 'icons/obj/stack_objects.dmi' custom_materials = list(/datum/material/bronze = MINERAL_MATERIAL_AMOUNT) resistance_flags = FIRE_PROOF | ACID_PROOF - throwforce = 10 - max_amount = 50 - throw_speed = 1 - throw_range = 3 - turf_type = /turf/open/floor/bronze - novariants = FALSE grind_results = list(/datum/reagent/iron = 5, /datum/reagent/copper = 3) //we have no "tin" reagent so this is the closest thing - merge_type = /obj/item/stack/tile/bronze + merge_type = /obj/item/stack/sheet/bronze tableVariant = /obj/structure/table/bronze material_type = /datum/material/bronze -/obj/item/stack/tile/bronze/attack_self(mob/living/user) +/obj/item/stack/sheet/bronze/attack_self(mob/living/user) if(is_servant_of_ratvar(user)) //still lets them build with it, just gives a message to_chat(user, "Wha... what is this cheap imitation crap? This isn't brass at all!") ..() -/obj/item/stack/tile/bronze/get_main_recipes() +/obj/item/stack/sheet/bronze/get_main_recipes() . = ..() . += GLOB.bronze_recipes -/obj/item/stack/tile/bronze/thirty +/obj/item/stack/sheet/bronze/thirty amount = 30 /* diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index 13fca1e8fe..0d85e897d2 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -513,3 +513,12 @@ icon_state = "material_tile" turf_type = /turf/open/floor/material material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS + +/obj/item/stack/tile/bronze + name = "bronze tile" + singular_name = "bronze floor tile" + desc = "A tile made out of bronze. Looks like clockwork." + icon_state = "material_tile" + color = "#92661A" + turf_type = /turf/open/floor/bronze + custom_materials = list(/datum/material/bronze = 250) diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index 7532aacbfa..28850e79a2 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -47,41 +47,58 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible", user.visible_message("[user] is offering [user.p_them()]self to [deity_name]! It looks like [user.p_theyre()] trying to commit suicide!") return (BRUTELOSS) -/obj/item/storage/book/bible/attack_self(mob/living/carbon/human/H) - if(!istype(H)) +/obj/item/storage/book/bible/attack_self(mob/living/carbon/human/user) + if(!istype(user)) return - // If H is the Chaplain, we can set the icon_state of the bible (but only once!) - if(!GLOB.bible_icon_state && H.job == "Chaplain") - var/dat = "Pick Bible Style

Pick a bible style

" - for(var/i in 1 to GLOB.biblestates.len) - var/icon/bibleicon = icon('icons/obj/storage.dmi', GLOB.biblestates[i]) - var/nicename = GLOB.biblenames[i] - H << browse_rsc(bibleicon, nicename) - dat += {""} - dat += "
[nicename]
" - H << browse(dat, "window=editicon;can_close=0;can_minimize=0;size=250x650") + if(GLOB.bible_icon_state) // if there is already a bible icon return FALSE + return FALSE + if(user.job != "Chaplain") // if the user is not the chaplain, return FALSE + return FALSE -/obj/item/storage/book/bible/Topic(href, href_list) - if(!usr.canUseTopic(src)) - return - if(href_list["seticon"] && GLOB && !GLOB.bible_icon_state) - var/iconi = text2num(href_list["seticon"]) - var/biblename = GLOB.biblenames[iconi] - var/obj/item/storage/book/bible/B = locate(href_list["src"]) - B.icon_state = GLOB.biblestates[iconi] - B.item_state = GLOB.bibleitemstates[iconi] + var/list/skins = list() + for(var/i in 1 to GLOB.biblestates.len) + var/image/bible_image = image(icon = 'icons/obj/storage.dmi', icon_state = GLOB.biblestates[i]) + skins += list("[GLOB.biblenames[i]]" = bible_image) - if(B.icon_state == "honk1" || B.icon_state == "honk2") - var/mob/living/carbon/human/H = usr - H.dna.add_mutation(CLOWNMUT) - H.dna.add_mutation(SMILE) - H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), SLOT_WEAR_MASK) + var/choice = show_radial_menu(user, src, skins, custom_check = CALLBACK(src, .proc/check_menu, user), radius = 40, require_near = TRUE) + if(!choice) + return FALSE + var/bible_index = GLOB.biblenames.Find(choice) + if(!bible_index) + return FALSE + icon_state = GLOB.biblestates[bible_index] + item_state = GLOB.bibleitemstates[bible_index] - GLOB.bible_icon_state = B.icon_state - GLOB.bible_item_state = B.item_state + if(icon_state == "honk1" || icon_state == "honk2") + var/mob/living/carbon/human/H = usr + H.dna.add_mutation(CLOWNMUT) + H.dna.add_mutation(SMILE) + H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), SLOT_WEAR_MASK) - SSblackbox.record_feedback("text", "religion_book", 1, "[biblename]") - usr << browse(null, "window=editicon") + GLOB.bible_icon_state = icon_state + GLOB.bibleitemstates = item_state + SSblackbox.record_feedback("text", "religion_book", 1, "[choice]") + +/** + * Checks if we are allowed to interact with the radial + * + * Arguements: user The mob interacting with the menu + */ + +/obj/item/storage/book/bible/proc/check_menu(mob/living/carbon/human/user) + if(GLOB.bible_icon_state) + return FALSE + if(!istype(user)) + return FALSE + if(!user.is_holding(src)) + return FALSE + if(!user.can_read(src)) + return FALSE + if(user.incapacitated()) + return FALSE + if(user.job != "Chaplain") + return FALSE + return TRUE /obj/item/storage/book/bible/proc/bless(mob/living/carbon/human/H, mob/living/user) for(var/X in H.bodyparts) diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm index ed0ee8ad07..a6b34c59e3 100644 --- a/code/game/objects/items/storage/toolbox.dm +++ b/code/game/objects/items/storage/toolbox.dm @@ -245,19 +245,20 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons) new /obj/item/stack/cable_coil/white(src) /obj/item/storage/toolbox/ammo - name = "ammo box" - desc = "It contains a few clips." + name = "ammunition case (7.62mm stripper clips)" + desc = "It contains a few 7.62 stripper clips." icon_state = "ammobox" item_state = "ammobox" + var/ammotype = /obj/item/ammo_box/a762 // make sure this is a typepath thanks /obj/item/storage/toolbox/ammo/PopulateContents() - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) - new /obj/item/ammo_box/a762(src) + for (var/i = 0, i < 7, i++) + new ammotype(src) + +/obj/item/storage/toolbox/ammo/surplus + name = "ammunition case (10mm rifle magazines)" + desc = "It contains a few 10mm rifle magazines." + ammotype = /obj/item/ammo_box/magazine/m10mm/rifle /obj/item/storage/toolbox/infiltrator name = "insidious case" diff --git a/code/game/objects/items/teleportation.dm b/code/game/objects/items/teleportation.dm index 939b9e4905..f9ede407ca 100644 --- a/code/game/objects/items/teleportation.dm +++ b/code/game/objects/items/teleportation.dm @@ -122,11 +122,14 @@ return TRUE return ..() -/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user) - if(is_parent_of_portal(target)) +/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user, delay = 30) + var/datum/beam/B = user.Beam(target, icon_state = "rped_upgrade", maxdistance = 50) + if(is_parent_of_portal(target) && (!delay || do_after(user, delay, target = target))) qdel(target) to_chat(user, "You dispel [target] with \the [src]!") + qdel(B) return TRUE + qdel(B) return FALSE /obj/item/hand_tele/afterattack(atom/target, mob/user) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index b1f8eac1da..b5f8612244 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -252,6 +252,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/katana/timestop name = "temporal katana" desc = "Delicately balanced, this finely-crafted blade hums with barely-restrained potential." + block_chance = 0 // oops + force = 27.5 // oops item_flags = ITEM_CAN_PARRY block_parry_data = /datum/block_parry_data/bokken/quick_parry/proj @@ -259,7 +261,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 if(ishuman(owner)) var/mob/living/carbon/human/flynn = owner flynn.emote("smirk") - new /obj/effect/timestop(get_turf(owner), 2, 50, list(owner)) + new /obj/effect/timestop/magic(get_turf(owner), 1, 50, list(owner)) // null roddies counter /obj/item/melee/bokken // parrying stick name = "bokken" @@ -295,7 +297,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 parry_time_perfect = 1.5 parry_time_perfect_leeway = 1 parry_imperfect_falloff_percent = 7.5 - parry_efficiency_to_counterattack = 100 + parry_efficiency_to_counterattack = 120 parry_efficiency_considered_successful = 65 // VERY generous parry_efficiency_perfect = 120 parry_efficiency_perfect_override = list( @@ -304,13 +306,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 parry_failed_stagger_duration = 3 SECONDS parry_data = list( PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25 - ) + ) /datum/block_parry_data/bokken/quick_parry // emphasizing REALLY SHORT PARRIES parry_stamina_cost = 6 // still more costly than most parries, but less than a full bokken parry parry_time_active = 5 // REALLY small parry window parry_time_perfect = 2.5 // however... - parry_time_perfect_leeway = 2.5 // the entire time, the parry is perfect + parry_time_perfect_leeway = 2 // the entire time, the parry is perfect parry_failed_stagger_duration = 1 SECONDS parry_failed_clickcd_duration = 1 SECONDS // more forgiving punishments for missed parries // still, don't fucking miss your parries or you're down stamina and staggered to shit diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 39f2d276a1..52036427c8 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -9,8 +9,10 @@ var/mob/living/structureclimber var/broken = 0 //similar to machinery's stat BROKEN layer = BELOW_OBJ_LAYER - flags_ricochet = RICOCHET_HARD - ricochet_chance_mod = 0.5 + //ricochets on structures commented out for now because there's a lot of structures that /shouldnt/ be ricocheting and those need to be reviewed first + //flags_1 = DEFAULT_RICOCHET_1 + //flags_ricochet = RICOCHET_HARD + //ricochet_chance_mod = 0.5 /obj/structure/Initialize() if (!armor) @@ -110,3 +112,6 @@ if(0 to 25) if(!broken) return "It's falling apart!" + +/obj/structure/rust_heretic_act() + take_damage(500, BRUTE, "melee", 1) diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index f3c4679d21..39bef1a9c0 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -487,7 +487,7 @@ desc = "A bronze bar stool with red silk for a pillow." icon_state = "barbrass" item_chair = /obj/item/chair/stool/bar/bronze - buildstacktype = /obj/item/stack/tile/bronze + buildstacktype = /obj/item/stack/sheet/bronze buildstackamount = 1 /obj/structure/chair/stool/brass @@ -503,7 +503,7 @@ desc = "A bronze stool with a silk top for comfort." icon_state = "stoolbrass" item_chair = /obj/item/chair/stool/bronze - buildstacktype = /obj/item/stack/tile/bronze + buildstacktype = /obj/item/stack/sheet/bronze buildstackamount = 1 /obj/item/chair/stool/brass @@ -569,7 +569,6 @@ buildstacktype = /obj/item/stack/tile/brass buildstackamount = 1 item_chair = null - var/turns = 0 /obj/structure/chair/brass/ComponentInitialize() return //it spins with the power of ratvar, not components. @@ -581,16 +580,12 @@ /obj/structure/chair/brass/process() setDir(turn(dir,-90)) playsound(src, 'sound/effects/servostep.ogg', 50, FALSE) - turns++ - if(turns >= 8) - STOP_PROCESSING(SSfastprocess, src) /obj/structure/chair/brass/ratvar_act() return /obj/structure/chair/brass/AltClick(mob/living/user) . = ..() - turns = 0 if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user))) return if(!(datum_flags & DF_ISPROCESSING)) @@ -608,7 +603,7 @@ desc = "A spinny chair made of bronze. It has little cogs for wheels!" anchored = FALSE icon_state = "brass_chair" - buildstacktype = /obj/item/stack/tile/bronze + buildstacktype = /obj/item/stack/sheet/bronze buildstackamount = 1 item_chair = null diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 043232e9e3..5c5b2bb132 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -111,6 +111,9 @@ var/mob/living/L = user if(HAS_TRAIT(L, TRAIT_SKITTISH)) . += "Ctrl-Shift-click [src] to jump inside." + if(isobserver(user)) + . += "It contains: [english_list(contents)]." + investigate_log("had its contents examined by [user] as a ghost.", INVESTIGATE_GHOST) /obj/structure/closet/CanPass(atom/movable/mover, turf/target) if(wall_mounted) diff --git a/code/game/objects/structures/door_assembly_types.dm b/code/game/objects/structures/door_assembly_types.dm index fe9c4e7bbc..5fb454b5bc 100644 --- a/code/game/objects/structures/door_assembly_types.dm +++ b/code/game/objects/structures/door_assembly_types.dm @@ -240,3 +240,14 @@ airlock_type = /obj/machinery/door/airlock/wood mineral = "wood" glass_type = /obj/machinery/door/airlock/wood/glass + +/obj/structure/door_assembly/door_assembly_bronze + name = "bronze airlock assembly" + icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi' + base_name = "bronze airlock" + airlock_type = /obj/machinery/door/airlock/bronze + noglass = TRUE + material_type = /obj/item/stack/tile/bronze + +/obj/structure/door_assembly/door_assembly_bronze/seethru + airlock_type = /obj/machinery/door/airlock/bronze/seethru diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index ff62b9cc48..87cfbb56e0 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -442,19 +442,19 @@ to_chat(user, "You start slicing apart [src]...") if(W.use_tool(src, user, 40, volume=50)) to_chat(user, "You slice apart [src].") - var/obj/item/stack/tile/bronze/B = new(drop_location(), 2) + var/obj/item/stack/sheet/bronze/B = new(drop_location(), 2) transfer_fingerprints_to(B) qdel(src) else if(istype(W, /obj/item/pickaxe/drill/jackhammer)) to_chat(user, "Your jackhammer smashes through the girder!") - var/obj/item/stack/tile/bronze/B = new(drop_location(), 2) + var/obj/item/stack/sheet/bronze/B = new(drop_location(), 2) transfer_fingerprints_to(B) W.play_tool_sound(src) qdel(src) - else if(istype(W, /obj/item/stack/tile/bronze)) - var/obj/item/stack/tile/bronze/B = W + else if(istype(W, /obj/item/stack/sheet/bronze)) + var/obj/item/stack/sheet/bronze/B = W if(B.get_amount() < 2) to_chat(user, "You need at least two bronze sheets to build a bronze wall!") return 0 diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm index d1a2a6f9f6..1358b2e296 100644 --- a/code/game/objects/structures/stairs.dm +++ b/code/game/objects/structures/stairs.dm @@ -3,7 +3,7 @@ #define STAIR_TERMINATOR_YES 2 // dir determines the direction of travel to go upwards (due to lack of sprites, currently only 1 and 2 make sense) -// stairs require /turf/open/openspace as the tile above them to work +// stairs require /turf/open/transparent/openspace as the tile above them to work // multiple stair objects can be chained together; the Z level transition will happen on the final stair object in the chain /obj/structure/stairs @@ -12,7 +12,7 @@ icon_state = "stairs" anchored = TRUE - var/force_open_above = FALSE // replaces the turf above this stair obj with /turf/open/openspace + var/force_open_above = FALSE // replaces the turf above this stair obj with /turf/open/transparent/openspace var/terminator_mode = STAIR_TERMINATOR_AUTOMATIC var/turf/listeningTo @@ -95,20 +95,20 @@ /obj/structure/stairs/proc/build_signal_listener() if(listeningTo) UnregisterSignal(listeningTo, COMSIG_TURF_MULTIZ_NEW) - var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP) + var/turf/open/transparent/openspace/T = get_step_multiz(get_turf(src), UP) RegisterSignal(T, COMSIG_TURF_MULTIZ_NEW, .proc/on_multiz_new) listeningTo = T /obj/structure/stairs/proc/force_open_above() - var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP) + var/turf/open/transparent/openspace/T = get_step_multiz(get_turf(src), UP) if(T && !istype(T)) - T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR) + T.ChangeTurf(/turf/open/transparent/openspace, flags = CHANGETURF_INHERIT_AIR) /obj/structure/stairs/proc/on_multiz_new(turf/source, dir) if(dir == UP) - var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP) + var/turf/open/transparent/openspace/T = get_step_multiz(get_turf(src), UP) if(T && !istype(T)) - T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR) + T.ChangeTurf(/turf/open/transparent/openspace, flags = CHANGETURF_INHERIT_AIR) /obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1) . = ..() diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index a553e63b0f..9f91d2c5e0 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -597,7 +597,7 @@ icon = 'icons/obj/smooth_structures/brass_table.dmi' icon_state = "brass_table" resistance_flags = FIRE_PROOF | ACID_PROOF - buildstack = /obj/item/stack/tile/bronze + buildstack = /obj/item/stack/sheet/bronze canSmoothWith = list(/obj/structure/table/reinforced/brass, /obj/structure/table/bronze) /obj/structure/table/bronze/tablelimbsmash(mob/living/user, mob/living/pushed_mob) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 6aae4955f1..74f006296f 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -17,7 +17,6 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) layer = ABOVE_OBJ_LAYER //Just above doors pressure_resistance = 4*ONE_ATMOSPHERE anchored = TRUE //initially is 0 for tile smoothing - flags_1 = ON_BORDER_1 max_integrity = 25 var/ini_dir = null var/state = WINDOW_OUT_OF_FRAME @@ -38,7 +37,8 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) var/hitsound = 'sound/effects/Glasshit.ogg' rad_insulation = RAD_VERY_LIGHT_INSULATION rad_flags = RAD_PROTECT_CONTENTS - flags_ricochet = RICOCHET_HARD + flags_1 = ON_BORDER_1|DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_HARD ricochet_chance_mod = 0.4 attack_hand_speed = CLICK_CD_MELEE attack_hand_is_action = TRUE @@ -875,3 +875,26 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup) return ..() update_icon() + +/obj/structure/window/bronze + name = "brass window" + desc = "A paper-thin pane of translucent yet reinforced brass. Nevermind, this is just weak bronze!" + icon = 'icons/obj/smooth_structures/clockwork_window.dmi' + icon_state = "clockwork_window_single" + glass_type = /obj/item/stack/tile/bronze + +/obj/structure/window/bronze/unanchored + anchored = FALSE + +/obj/structure/window/bronze/fulltile + icon_state = "clockwork_window" + canSmoothWith = null + smooth = SMOOTH_TRUE + fulltile = TRUE + flags_1 = PREVENT_CLICK_UNDER_1 + dir = FULLTILE_WINDOW_DIR + max_integrity = 50 + glass_amount = 2 + +/obj/structure/window/bronze/fulltile/unanchored + anchored = FALSE diff --git a/code/game/sound.dm b/code/game/sound.dm index 577b058178..0feb01c223 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -226,5 +226,9 @@ soundin = pick('sound/effects/clangsmall1.ogg', 'sound/effects/clangsmall2.ogg') if("slosh") soundin = pick('sound/effects/slosh1.ogg', 'sound/effects/slosh2.ogg') + if("smcalm") + soundin = pick('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("smdelam") + soundin = pick('sound/machines/sm/accent/delam/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') //END OF CIT CHANGES return soundin diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm index b78826b23b..be0c444541 100644 --- a/code/game/turfs/closed.dm +++ b/code/game/turfs/closed.dm @@ -29,6 +29,9 @@ icon = 'icons/turf/walls.dmi' explosion_block = 50 +/turf/closed/indestructible/rust_heretic_act() + return + /turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE) return @@ -198,13 +201,13 @@ /turf/closed/indestructible/rock/glacierrock name = "unaturally hard ice wall" - desc = "Ice, hardened over thousands of years, you're not breaking through this." + desc = "Ice, hardened over thousands of years, you're not breaking through this." icon = 'icons/turf/walls.dmi' icon_state = "snow_rock" /turf/closed/indestructible/rock/glacierrock/blue name = "blue ice wall" - desc = "The incredible compressive forces that formed this sturdy ice wall gave it a blue color." + desc = "The incredible compressive forces that formed this sturdy ice wall gave it a blue color." icon = 'icons/turf/walls.dmi' icon_state = "ice" canSmoothWith = list(/turf/closed/indestructible/rock/glacierrock/blue) diff --git a/code/game/turfs/openspace/openspace.dm b/code/game/turfs/openspace/openspace.dm index 90fab29611..ea539ce5b5 100644 --- a/code/game/turfs/openspace/openspace.dm +++ b/code/game/turfs/openspace/openspace.dm @@ -11,66 +11,47 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr mouse_opacity = MOUSE_OPACITY_TRANSPARENT layer = SPLASHSCREEN_LAYER -/turf/open/openspace +/turf/open/transparent/openspace name = "open space" desc = "Watch your step!" icon_state = "transparent" - baseturfs = /turf/open/openspace + baseturfs = /turf/open/transparent/openspace CanAtmosPassVertical = ATMOS_PASS_YES //mouse_opacity = MOUSE_OPACITY_TRANSPARENT var/can_cover_up = TRUE var/can_build_on = TRUE -/turf/open/openspace/debug/update_multiz() +/turf/open/transparent/openspace/airless + initial_gas_mix = AIRLESS_ATMOS + +/turf/open/transparent/openspace/debug/update_multiz() ..() return TRUE -/turf/open/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker +///No bottom level for openspace. +/turf/open/transparent/openspace/show_bottom_level() + return FALSE + +/turf/open/transparent/openspace/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker . = ..() - plane = OPENSPACE_PLANE - layer = OPENSPACE_LAYER + vis_contents += GLOB.openspace_backdrop_one_for_all //Special grey square for projecting backdrop darkness filter on it. - return INITIALIZE_HINT_LATELOAD -/turf/open/openspace/LateInitialize() - update_multiz(TRUE, TRUE) +/turf/open/transparent/openspace/can_have_cabling() + if(locate(/obj/structure/lattice/catwalk, src)) + return TRUE + return FALSE -/turf/open/openspace/Destroy() - vis_contents.len = 0 - return ..() - -/turf/open/openspace/update_multiz(prune_on_fail = FALSE, init = FALSE) - . = ..() - var/turf/T = below() - if(!T) - vis_contents.len = 0 - if(prune_on_fail) - ChangeTurf(/turf/open/floor/plating) - return FALSE - if(init) - vis_contents += T +/turf/open/transparent/openspace/zAirIn() return TRUE -/turf/open/openspace/multiz_turf_del(turf/T, dir) - if(dir != DOWN) - return - update_multiz() - -/turf/open/openspace/multiz_turf_new(turf/T, dir) - if(dir != DOWN) - return - update_multiz() - -/turf/open/openspace/zAirIn() +/turf/open/transparent/openspace/zAirOut() return TRUE -/turf/open/openspace/zAirOut() +/turf/open/transparent/openspace/zPassIn(atom/movable/A, direction, turf/source) return TRUE -/turf/open/openspace/zPassIn(atom/movable/A, direction, turf/source) - return TRUE - -/turf/open/openspace/zPassOut(atom/movable/A, direction, turf/destination) +/turf/open/transparent/openspace/zPassOut(atom/movable/A, direction, turf/destination) if(A.anchored) return FALSE for(var/obj/O in contents) @@ -78,13 +59,13 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr return FALSE return TRUE -/turf/open/openspace/proc/CanCoverUp() +/turf/open/transparent/openspace/proc/CanCoverUp() return can_cover_up -/turf/open/openspace/proc/CanBuildHere() +/turf/open/transparent/openspace/proc/CanBuildHere() return can_build_on -/turf/open/openspace/attackby(obj/item/C, mob/user, params) +/turf/open/transparent/openspace/attackby(obj/item/C, mob/user, params) ..() if(!CanBuildHere()) return @@ -98,14 +79,14 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr if(L) if(R.use(1)) to_chat(user, "You construct a catwalk.") - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) new/obj/structure/lattice/catwalk(src) else to_chat(user, "You need two rods to build a catwalk!") return if(R.use(1)) to_chat(user, "You construct a lattice.") - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) ReplaceWithLattice() else to_chat(user, "You need one rod to build a lattice.") @@ -118,7 +99,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr var/obj/item/stack/tile/plasteel/S = C if(S.use(1)) qdel(L) - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE) to_chat(user, "You build a floor.") PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) else @@ -126,7 +107,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr else to_chat(user, "The plating is going to need some support! Place metal rods first.") -/turf/open/openspace/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) +/turf/open/transparent/openspace/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd) if(!CanBuildHere()) return FALSE @@ -139,7 +120,7 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr return list("mode" = RCD_FLOORWALL, "delay" = 0, "cost" = 3) return FALSE -/turf/open/openspace/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) +/turf/open/transparent/openspace/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode) switch(passed_mode) if(RCD_FLOORWALL) to_chat(user, "You build a floor.") @@ -147,12 +128,12 @@ GLOBAL_DATUM_INIT(openspace_backdrop_one_for_all, /atom/movable/openspace_backdr return TRUE return FALSE -/turf/open/openspace/icemoon +/turf/open/transparent/openspace/icemoon name = "ice chasm" - baseturfs = /turf/open/openspace/icemoon - can_cover_up = FALSE - can_build_on = FALSE + baseturfs = /turf/open/transparent/openspace/icemoon + can_cover_up = TRUE + can_build_on = TRUE initial_gas_mix = ICEMOON_DEFAULT_ATMOS -/turf/open/openspace/icemoon/can_zFall(atom/movable/A, levels = 1, turf/target) +/turf/open/transparent/openspace/icemoon/can_zFall(atom/movable/A, levels = 1, turf/target) return TRUE diff --git a/code/game/turfs/openspace/transparent.dm b/code/game/turfs/openspace/transparent.dm new file mode 100644 index 0000000000..8efc60a15b --- /dev/null +++ b/code/game/turfs/openspace/transparent.dm @@ -0,0 +1,95 @@ +/turf/open/transparent + baseturfs = /turf/open/transparent/openspace + intact = FALSE //this means wires go on top + +/turf/open/transparent/Initialize() // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker + . = ..() + plane = OPENSPACE_PLANE + layer = OPENSPACE_LAYER + + return INITIALIZE_HINT_LATELOAD + +/turf/open/transparent/LateInitialize() + update_multiz(TRUE, TRUE) + +/turf/open/transparent/Destroy() + vis_contents.len = 0 + return ..() + +/turf/open/transparent/update_multiz(prune_on_fail = FALSE, init = FALSE) + . = ..() + var/turf/T = below() + if(!T) + vis_contents.len = 0 + if(!show_bottom_level() && prune_on_fail) //If we cant show whats below, and we prune on fail, change the turf to plating as a fallback + ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR) + return FALSE + if(init) + vis_contents += T + return TRUE + +/turf/open/transparent/multiz_turf_del(turf/T, dir) + if(dir != DOWN) + return + update_multiz() + +/turf/open/transparent/multiz_turf_new(turf/T, dir) + if(dir != DOWN) + return + update_multiz() + +///Called when there is no real turf below this turf +/turf/open/transparent/proc/show_bottom_level() + var/turf/path = SSmapping.level_trait(z, ZTRAIT_BASETURF) || /turf/open/space + if(!ispath(path)) + path = text2path(path) + if(!ispath(path)) + warning("Z-level [z] has invalid baseturf '[SSmapping.level_trait(z, ZTRAIT_BASETURF)]'") + path = /turf/open/space + var/mutable_appearance/underlay_appearance = mutable_appearance(initial(path.icon), initial(path.icon_state), layer = TURF_LAYER, plane = PLANE_SPACE) + underlays += underlay_appearance + return TRUE + + +/turf/open/transparent/glass + name = "Glass floor" + desc = "Dont jump on it, or do, I'm not your mom." + icon = 'icons/turf/floors/glass.dmi' + icon_state = "floor_glass" + smooth = SMOOTH_MORE + var/floor_tile = /obj/item/stack/sheet/glass + canSmoothWith = list(/turf/open/transparent/glass, /turf/open/transparent/glass/reinforced) + footstep = FOOTSTEP_PLATING + barefootstep = FOOTSTEP_HARD_BAREFOOT + clawfootstep = FOOTSTEP_HARD_CLAW + heavyfootstep = FOOTSTEP_GENERIC_HEAVY + +/turf/open/transparent/glass/Initialize() + icon_state = "" //Prevent the normal icon from appearing behind the smooth overlays + return ..() + +/turf/open/transparent/glass/wrench_act(mob/living/user, obj/item/I) + to_chat(user, "You begin removing glass...") + if(I.use_tool(src, user, 30, volume=80)) + if(!istype(src, /turf/open/transparent/glass)) + return TRUE + if(floor_tile) + new floor_tile(src, 2) + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) + return TRUE + +/turf/open/transparent/glass/reinforced + name = "Reinforced glass floor" + desc = "Do jump on it, it can take it." + icon = 'icons/turf/floors/reinf_glass.dmi' + floor_tile = /obj/item/stack/sheet/rglass + +/turf/open/transparent/glass/reinforced/wrench_act(mob/living/user, obj/item/I) + to_chat(user, "You begin removing reinforced glass...") + if(I.use_tool(src, user, 30, volume=80)) + if(!istype(src, /turf/open/transparent/glass/reinforced)) + return TRUE + if(floor_tile) + new floor_tile(src, 2) + ScrapeAway(flags = CHANGETURF_INHERIT_AIR) + return TRUE diff --git a/code/game/turfs/simulated/floor/fancy_floor.dm b/code/game/turfs/simulated/floor/fancy_floor.dm index 946401ba18..5024a4bada 100644 --- a/code/game/turfs/simulated/floor/fancy_floor.dm +++ b/code/game/turfs/simulated/floor/fancy_floor.dm @@ -63,6 +63,11 @@ to_chat(user, "You forcefully pry off the planks, destroying them in the process.") return make_plating() +/turf/open/floor/wood/rust_heretic_act() + if(prob(70)) + new /obj/effect/temp_visual/glowing_rune(src) + ChangeTurf(/turf/open/floor/plating/rust) + /turf/open/floor/wood/cold temperature = 255.37 diff --git a/code/game/turfs/simulated/floor/mineral_floor.dm b/code/game/turfs/simulated/floor/mineral_floor.dm index 8e0230a2b0..fe6ed2da9b 100644 --- a/code/game/turfs/simulated/floor/mineral_floor.dm +++ b/code/game/turfs/simulated/floor/mineral_floor.dm @@ -58,6 +58,12 @@ if(exposed_temperature > 300) PlasmaBurn(exposed_temperature) +// Plasma floor that can't be removed, for disco + +/turf/open/floor/mineral/plasma/disco + name = "disco floor" +/turf/open/floor/mineral/plasma/disco/crowbar_act(mob/living/user, obj/item/I) + return //GOLD diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index f6e70f8ef5..c70332eacb 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -259,7 +259,7 @@ desc = "Some heavy bronze tiles." icon = 'icons/obj/clockwork_objects.dmi' icon_state = "clockwork_floor" - floor_tile = /obj/item/stack/tile/bronze + floor_tile = /obj/item/stack/sheet/bronze /turf/open/floor/padded name = "padded floor" @@ -267,3 +267,15 @@ icon = 'icons/turf/floors.dmi' icon_state = "floor_padded" floor_tile = /obj/item/stack/tile/padded + +/turf/open/floor/plating/rust + name = "rusted plating" + desc = "Corrupted steel." + icon_state = "plating_rust" + footstep = FOOTSTEP_RUST + barefootstep = FOOTSTEP_RUST + clawfootstep = FOOTSTEP_RUST + heavyfootstep = FOOTSTEP_RUST + +/turf/open/floor/plating/rust/rust_heretic_act() + return diff --git a/code/game/turfs/simulated/floor/plasteel_floor.dm b/code/game/turfs/simulated/floor/plasteel_floor.dm index af48160ef1..614c63a14c 100644 --- a/code/game/turfs/simulated/floor/plasteel_floor.dm +++ b/code/game/turfs/simulated/floor/plasteel_floor.dm @@ -8,6 +8,11 @@ . = ..() . += "There's a small crack on the edge of it." +/turf/open/floor/plasteel/rust_heretic_act() + if(prob(70)) + new /obj/effect/temp_visual/glowing_rune(src) + ChangeTurf(/turf/open/floor/plating/rust) + /turf/open/floor/plasteel/update_icon() if(!..()) return 0 diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index ac80bddd00..35ee06a584 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -66,6 +66,40 @@ R.use(2) to_chat(user, "You reinforce the floor.") return + if(istype(C, /obj/item/stack/sheet/glass)) + if(broken || burnt) + to_chat(user, "Repair the plating first!") + return + var/obj/item/stack/sheet/glass/G = C + if (G.get_amount() < 2) + to_chat(user, "You need two glass sheets to make a glass floor!") + return + else + to_chat(user, "You begin adding glass to the floor...") + if(do_after(user, 5, target = src)) + if (G.get_amount() >= 2 && !istype(src, /turf/open/transparent/glass)) + PlaceOnTop(/turf/open/transparent/glass, flags = CHANGETURF_INHERIT_AIR) + playsound(src, 'sound/items/deconstruct.ogg', 80, 1) + G.use(2) + to_chat(user, "You add glass to the floor.") + return + if(istype(C, /obj/item/stack/sheet/rglass)) + if(broken || burnt) + to_chat(user, "Repair the plating first!") + return + var/obj/item/stack/sheet/rglass/RG = C + if (RG.get_amount() < 2) + to_chat(user, "You need two reinforced glass sheets to make a reinforced glass floor!") + return + else + to_chat(user, "You begin adding reinforced glass to the floor...") + if(do_after(user, 10, target = src)) + if (RG.get_amount() >= 2 && !istype(src, /turf/open/transparent/glass/reinforced)) + PlaceOnTop(/turf/open/transparent/glass/reinforced, flags = CHANGETURF_INHERIT_AIR) + playsound(src, 'sound/items/deconstruct.ogg', 80, 1) + RG.use(2) + to_chat(user, "You add reinforced glass to the floor.") + return else if(istype(C, /obj/item/stack/tile)) if(!broken && !burnt) for(var/obj/O in src) @@ -98,6 +132,11 @@ return TRUE +/turf/open/floor/plating/rust_heretic_act() + if(prob(70)) + new /obj/effect/temp_visual/glowing_rune(src) + ChangeTurf(/turf/open/floor/plating/rust) + /turf/open/floor/plating/make_plating() return diff --git a/code/game/turfs/simulated/wall/mineral_walls.dm b/code/game/turfs/simulated/wall/mineral_walls.dm index 4466511acd..ed48c24462 100644 --- a/code/game/turfs/simulated/wall/mineral_walls.dm +++ b/code/game/turfs/simulated/wall/mineral_walls.dm @@ -192,6 +192,7 @@ icon_state = "map-shuttle" explosion_block = 3 flags_1 = CAN_BE_DIRTY_1 | DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD sheet_type = /obj/item/stack/sheet/mineral/titanium smooth = SMOOTH_MORE|SMOOTH_DIAGONAL canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) diff --git a/code/game/turfs/simulated/wall/misc_walls.dm b/code/game/turfs/simulated/wall/misc_walls.dm index d445be91cb..e4d991a082 100644 --- a/code/game/turfs/simulated/wall/misc_walls.dm +++ b/code/game/turfs/simulated/wall/misc_walls.dm @@ -188,17 +188,25 @@ icon = 'icons/turf/walls/rusty_wall.dmi' hardness = 45 +/turf/closed/wall/rust/rust_heretic_act() + ScrapeAway() + /turf/closed/wall/r_wall/rust name = "rusted reinforced wall" desc = "A huge chunk of rusted reinforced metal." icon = 'icons/turf/walls/rusty_reinforced_wall.dmi' hardness = 15 +/turf/closed/wall/r_wall/rust/rust_heretic_act() + if(prob(50)) + return + ScrapeAway() + /turf/closed/wall/mineral/bronze name = "clockwork wall" desc = "A huge chunk of bronze, decorated like gears and cogs." icon = 'icons/turf/walls/clockwork_wall.dmi' icon_state = "clockwork_wall" - sheet_type = /obj/item/stack/tile/bronze + sheet_type = /obj/item/stack/sheet/bronze sheet_amount = 2 girder_type = /obj/structure/girder/bronze diff --git a/code/game/turfs/simulated/wall/reinf_walls.dm b/code/game/turfs/simulated/wall/reinf_walls.dm index 6b41f41763..5e60f65ccc 100644 --- a/code/game/turfs/simulated/wall/reinf_walls.dm +++ b/code/game/turfs/simulated/wall/reinf_walls.dm @@ -235,6 +235,13 @@ if(the_rcd.canRturf) return ..() +/turf/closed/wall/r_wall/rust_heretic_act() + if(prob(50)) + return + if(prob(70)) + new /obj/effect/temp_visual/glowing_rune(src) + ChangeTurf(/turf/closed/wall/r_wall/rust) + /turf/closed/wall/r_wall/syndicate name = "hull" desc = "The armored hull of an ominous looking ship." diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 0623f2b9d2..9ff4f668a4 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -6,7 +6,8 @@ icon = 'icons/turf/walls/wall.dmi' icon_state = "wall" explosion_block = 1 - + flags_1 = DEFAULT_RICOCHET_1 + flags_ricochet = RICOCHET_HARD thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall attack_hand_speed = 8 @@ -324,4 +325,9 @@ add_overlay(dent_decals) +/turf/closed/wall/rust_heretic_act() + if(prob(70)) + new /obj/effect/temp_visual/glowing_rune(src) + ChangeTurf(/turf/closed/wall/rust) + #undef MAX_DENT_DECALS diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 8bff37ab03..c5f880c955 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -14,6 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists) var/list/objectives = list() var/antag_memory = ""//These will be removed with antag datum var/antag_moodlet //typepath of moodlet that the mob will gain with their status + var/antag_hud_type + var/antag_hud_name /// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed(). var/hijack_speed = 0 @@ -77,6 +79,17 @@ GLOBAL_LIST_EMPTY(antagonists) hud.leave_hud(mob_override) set_antag_hud(mob_override, null) +// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects +/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE) + var/mob/living/carbon/human/H = mob_override + if(H && istype(H) && owner.assigned_role == "Clown") + if(removing) // They're a clown becoming an antag, remove clumsy + H.dna.remove_mutation(CLOWNMUT) + if(!silent && message) + to_chat(H, "[message]") + else + H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy + //Assign default team and creates one for one of a kind team antagonists /datum/antagonist/proc/create_team(datum/team/team) return diff --git a/code/modules/antagonists/abductor/equipment/orderable_gear.dm b/code/modules/antagonists/abductor/equipment/orderable_gear.dm index d15e450c99..f21294b041 100644 --- a/code/modules/antagonists/abductor/equipment/orderable_gear.dm +++ b/code/modules/antagonists/abductor/equipment/orderable_gear.dm @@ -37,14 +37,14 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras." id = "science_tool" build_path = /obj/item/abductor/gizmo -/* + /datum/abductor_gear/advanced_baton name = "Advanced Baton" description = "A quad-mode baton used for incapacitation and restraining of specimens." id = "advanced_baton" cost = 2 - build_path = /obj/item/melee/baton/abductor //does not exist? -*/ + build_path = /obj/item/abductor/baton //does not exist? + /datum/abductor_gear/superlingual_matrix name = "Superlingual Matrix" description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something." diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm index f06f4fe68a..2e244fa272 100644 --- a/code/modules/antagonists/abductor/machinery/console.dm +++ b/code/modules/antagonists/abductor/machinery/console.dm @@ -30,6 +30,22 @@ /// Possible gear to be dispensed var/list/possible_gear +/obj/machinery/abductor/console/Initialize(mapload) + . = ..() + possible_gear = get_abductor_gear() + +/** + * get_abductor_gear: Returns a list of a filtered abductor gear sorted by categories + */ +/obj/machinery/abductor/console/proc/get_abductor_gear() + var/list/filtered_modules = list() + for(var/path in GLOB.abductor_gear) + var/datum/abductor_gear/AG = new path + if(!filtered_modules[AG.category]) + filtered_modules[AG.category] = list() + filtered_modules[AG.category][AG] = AG + return filtered_modules + /obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING)) to_chat(user, "You start mashing alien buttons at random!") diff --git a/code/modules/antagonists/blob/blob/blobs/shield.dm b/code/modules/antagonists/blob/blob/blobs/shield.dm index a2a6ce94d3..a3a1403e58 100644 --- a/code/modules/antagonists/blob/blob/blobs/shield.dm +++ b/code/modules/antagonists/blob/blob/blobs/shield.dm @@ -45,11 +45,8 @@ desc = "A solid wall of slightly twitching tendrils with a reflective glow." damaged_desc = "A wall of twitching tendrils with a reflective glow." icon_state = "blob_glow" + flags_ricochet = RICOCHET_SHINY point_return = 8 max_integrity = 100 brute_resist = 1 explosion_block = 2 - -/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P) - return PROJECTILE_RICOCHET_FORCE - diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm index a81f702a75..4117fc2b36 100644 --- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm +++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm @@ -28,7 +28,6 @@ if(istype(owner.current.loc, /obj/structure/closet/crate/coffin)) Torpor_Begin() // Wait before next pass - FreeAllVassals() // Free my Vassals! (if I haven't yet) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -308,6 +307,7 @@ + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HUMAN FOOD diff --git a/code/modules/antagonists/bloodsucker/powers/fortitude.dm b/code/modules/antagonists/bloodsucker/powers/fortitude.dm index 76f3cc77a4..24cad2a188 100644 --- a/code/modules/antagonists/bloodsucker/powers/fortitude.dm +++ b/code/modules/antagonists/bloodsucker/powers/fortitude.dm @@ -23,6 +23,7 @@ ADD_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude") ADD_TRAIT(user, TRAIT_NODISMEMBER, "fortitude") ADD_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude") + ADD_TRAIT(user, TRAIT_NORUNNING, "fortitude") if(ishuman(owner)) var/mob/living/carbon/human/H = owner fortitude_resist = max(0.3, 0.7 - level_current * 0.1) @@ -53,6 +54,7 @@ REMOVE_TRAIT(user, TRAIT_PIERCEIMMUNE, "fortitude") REMOVE_TRAIT(user, TRAIT_NODISMEMBER, "fortitude") REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude") + REMOVE_TRAIT(user, TRAIT_STUNIMMUNE, "fortitude") if(!ishuman(owner)) return var/mob/living/carbon/human/H = owner diff --git a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm index 19c29762bf..daee9f5c2c 100644 --- a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm +++ b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm @@ -89,17 +89,17 @@ creation_message = "The cogscarab clicks and whirrs as it hops up and springs to life!" construct_type = /mob/living/simple_animal/drone/cogscarab w_class = WEIGHT_CLASS_SMALL - var/infinite_resources = TRUE + var/infinite_resources = FALSE //No. var/static/obj/item/seasonal_hat //Share it with all other scarabs, since we're from the same cult! /obj/item/clockwork/construct_chassis/cogscarab/Initialize() . = ..() if(GLOB.servants_active) - infinite_resources = FALSE //For any that are somehow spawned in late + infinite_resources = FALSE //This check is relatively irrelevant until *someone* makes the infinite resources var default to true again, so, leaving it in. /obj/item/clockwork/construct_chassis/cogscarab/pre_spawn() if(infinite_resources) - //During rounds where they can't interact with the station, let them experiment with builds + //During rounds where they can't interact with the station, let them experiment with builds, if an admin allows them to. construct_type = /mob/living/simple_animal/drone/cogscarab/ratvar if(!seasonal_hat) var/obj/item/drone_shell/D = locate() in GLOB.poi_list diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm index 00c4d73428..b7c94d56df 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_drivers.dm @@ -97,7 +97,7 @@ desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light." invocations = list("Divinity, show them your light!") whispered = TRUE - channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time. + channel_time = 25 //2.5 seconds should be a okay compromise between being able to use it when needed, and not being able to just pause in combat for a second and hardstunning your enemy power_cost = 125 usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration." tier = SCRIPTURE_DRIVER @@ -137,6 +137,30 @@ quickbind_desc = "Applies handcuffs to a struck target." +//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned. +/datum/clockwork_scripture/channeled/belligerent + descname = "Channeled, Area Slowdown" + name = "Belligerent" + desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds." + chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!") + chant_amount = 15 + chant_interval = 20 + channel_time = 20 + power_cost = 300 + usage_tip = "Useful for crowd control in a populated area and disrupting mass movement." + tier = SCRIPTURE_DRIVER + primary_component = BELLIGERENT_EYE + sort_priority = 7 + quickbind = TRUE + quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.
Maximum 15 chants." + +/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number) + for(var/mob/living/carbon/C in hearers(7, invoker)) + C.apply_status_effect(STATUS_EFFECT_BELLIGERENT) + new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker)) + return TRUE + + //Vanguard: Provides twenty seconds of greatly increased stamina regeneration and stun immunity. At the end of the twenty seconds, 25% of all stuns absorbed aswell as 50% of healed stamloss are applied to the invoker. /datum/clockwork_scripture/vanguard descname = "Self Stun Immunity" @@ -149,7 +173,7 @@ usage_tip = "You cannot reactivate Vanguard while still shielded by it." tier = SCRIPTURE_DRIVER primary_component = VANGUARD_COGWHEEL - sort_priority = 7 + sort_priority = 8 quickbind = TRUE quickbind_desc = "Allows you to temporarily have quickly regenerating stamina and absorb stuns. Part of the stuns absorbed and staminaloss healed will affect you when disabled." @@ -181,7 +205,7 @@ usage_tip = "The Compromise is very fast to invoke, and will remove holy water from the target Servant." tier = SCRIPTURE_DRIVER primary_component = VANGUARD_COGWHEEL - sort_priority = 8 + sort_priority = 9 quickbind = TRUE quickbind_desc = "Allows you to convert a Servant's brute, burn, and oxygen damage to half toxin damage.
Click your slab to disable." slab_overlay = "compromise" @@ -255,7 +279,7 @@ tier = SCRIPTURE_DRIVER space_allowed = TRUE primary_component = GEIS_CAPACITOR - sort_priority = 10 + sort_priority = 11 important = TRUE quickbind = TRUE quickbind_desc = "Creates a new Clockwork Slab." @@ -276,7 +300,7 @@ tier = SCRIPTURE_DRIVER space_allowed = TRUE primary_component = GEIS_CAPACITOR - sort_priority = 11 + sort_priority = 12 quickbind = TRUE quickbind_desc = "Creates a pair of Wraith Spectacles, which grant true sight but cause gradual vision loss." @@ -295,7 +319,7 @@ usage_tip = "This gateway is strictly one-way and will only allow things through the invoker's portal." tier = SCRIPTURE_DRIVER primary_component = GEIS_CAPACITOR - sort_priority = 9 + sort_priority = 10 quickbind = TRUE quickbind_desc = "Allows you to create a one-way Spatial Gateway to a living Servant or Clockwork Obelisk." diff --git a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm index 8819544928..eaec652f68 100644 --- a/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm +++ b/code/modules/antagonists/clockcult/clock_scriptures/scripture_scripts.dm @@ -459,26 +459,3 @@ invoker.light_range = 0 invoker.update_light() return ..() - -//Belligerent: Channeled for up to fifteen times over thirty seconds. Forces non-servants that can hear the chant to walk, doing minor damage. Nar-Sian cultists are burned. -/datum/clockwork_scripture/channeled/belligerent - descname = "Channeled, Area Slowdown" - name = "Belligerent" - desc = "Forces all nearby non-servants to walk rather than run, doing minor damage. Chanted every two seconds for up to thirty seconds." - chant_invocations = list("Punish their blindness!", "Take time, make slow!", "Kneel before The Justiciar!", "Halt their charges!", "Cease the tides!") - chant_amount = 15 - chant_interval = 20 - channel_time = 20 - power_cost = 300 - usage_tip = "Useful for crowd control in a populated area and disrupting mass movement." - tier = SCRIPTURE_DRIVER - primary_component = BELLIGERENT_EYE - sort_priority = 1 - quickbind = TRUE - quickbind_desc = "Forces nearby non-Servants to walk, doing minor damage with each chant.
Maximum 15 chants." - -/datum/clockwork_scripture/channeled/belligerent/chant_effects(chant_number) - for(var/mob/living/carbon/C in hearers(7, invoker)) - C.apply_status_effect(STATUS_EFFECT_BELLIGERENT) - new /obj/effect/temp_visual/ratvar/belligerent(get_turf(invoker)) - return TRUE diff --git a/code/modules/antagonists/eldritch_cult/eldritch_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm new file mode 100644 index 0000000000..367710b110 --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_antag.dm @@ -0,0 +1,229 @@ +/datum/antagonist/heretic + name = "Heretic" + roundend_category = "Heretics" + antagpanel_category = "Heretic" + antag_moodlet = /datum/mood_event/heretics + job_rank = ROLE_HERETIC + antag_hud_type = ANTAG_HUD_HERETIC + antag_hud_name = "heretic" + var/give_equipment = TRUE + var/list/researched_knowledge = list() + var/total_sacrifices = 0 + var/ascended = FALSE + +/datum/antagonist/heretic/admin_add(datum/mind/new_owner,mob/admin) + give_equipment = TRUE + new_owner.add_antag_datum(src) + message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].") + log_admin("[key_name(admin)] has heresized [key_name(new_owner)].") + +/datum/antagonist/heretic/greet() + owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change + to_chat(owner, "You are the Heretic!
\ + The old ones gave you these tasks to fulfill:") + owner.announce_objectives() + to_chat(owner, "The book whispers, the forbidden knowledge walks once again!
\ + Your book allows you to research abilities, but be careful, you cannot undo what has been done.
\ + You gain charges by either collecting influences or sacrificing people tracked by the living heart
\ + You can find a basic guide at : https://tgstation13.org/wiki/Heresy_101
") + +/datum/antagonist/heretic/on_gain() + var/mob/living/current = owner.current + if(ishuman(current)) + forge_primary_objectives() + gain_knowledge(/datum/eldritch_knowledge/spell/basic) + gain_knowledge(/datum/eldritch_knowledge/living_heart) + gain_knowledge(/datum/eldritch_knowledge/codex_cicatrix) + gain_knowledge(/datum/eldritch_knowledge/eldritch_blade) + current.log_message("has been converted to the cult of the forgotten ones!", LOG_ATTACK, color="#960000") + GLOB.reality_smash_track.AddMind(owner) + START_PROCESSING(SSprocessing,src) + if(give_equipment) + equip_cultist() + owner.teach_crafting_recipe(/datum/crafting_recipe/heretic/codex) + return ..() + +/datum/antagonist/heretic/on_removal() + + for(var/X in researched_knowledge) + var/datum/eldritch_knowledge/EK = researched_knowledge[X] + EK.on_lose(owner.current) + + if(!silent) + to_chat(owner.current, "Your mind begins to flare as the otherwordly knowledge escapes your grasp!") + owner.current.log_message("has renounced the cult of the old ones!", LOG_ATTACK, color="#960000") + GLOB.reality_smash_track.RemoveMind(owner) + STOP_PROCESSING(SSprocessing,src) + + return ..() + + +/datum/antagonist/heretic/proc/equip_cultist() + var/mob/living/carbon/H = owner.current + if(!istype(H)) + return + . += ecult_give_item(/obj/item/forbidden_book, H) + . += ecult_give_item(/obj/item/living_heart, H) + +/datum/antagonist/heretic/proc/ecult_give_item(obj/item/item_path, mob/living/carbon/human/H) + var/list/slots = list( + "backpack" = SLOT_IN_BACKPACK, + "left pocket" = SLOT_L_STORE, + "right pocket" = SLOT_R_STORE + ) + + var/T = new item_path(H) + var/item_name = initial(item_path.name) + var/where = H.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(H, "Unfortunately, you weren't able to get a [item_name]. This is very bad and you should adminhelp immediately (press F1).") + return FALSE + else + to_chat(H, "You have a [item_name] in your [where].") + if(where == "backpack") + SEND_SIGNAL(H.back, COMSIG_TRY_STORAGE_SHOW, H) + return TRUE + +/datum/antagonist/heretic/process() + + for(var/X in researched_knowledge) + var/datum/eldritch_knowledge/EK = researched_knowledge[X] + EK.on_life(owner.current) + +/datum/antagonist/heretic/proc/forge_primary_objectives() + var/list/assasination = list() + var/list/protection = list() + for(var/i in 1 to 2) + var/pck = pick("assasinate","protect") + switch(pck) + if("assasinate") + var/datum/objective/assassinate/A = new + A.owner = owner + var/list/owners = A.get_owners() + A.find_target(owners,protection) + assasination += A.target + objectives += A + if("protect") + var/datum/objective/protect/P = new + P.owner = owner + var/list/owners = P.get_owners() + P.find_target(owners,assasination) + protection += P.target + objectives += P + + + var/datum/objective/sacrifice_ecult/SE = new + SE.owner = owner + SE.update_explanation_text() + objectives += SE + + var/datum/objective/escape/escape_objective = new + escape_objective.owner = owner + objectives += escape_objective + +/datum/antagonist/heretic/apply_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + if(mob_override) + current = mob_override + add_antag_hud(antag_hud_type, antag_hud_name, current) + handle_clown_mutation(current, mob_override ? null : "Knowledge described in the book allowed you to overcome your clownish nature, allowing you to use complex items effectively.") + current.faction |= "heretics" + +/datum/antagonist/heretic/remove_innate_effects(mob/living/mob_override) + . = ..() + var/mob/living/current = owner.current + if(mob_override) + current = mob_override + remove_antag_hud(antag_hud_type, current) + handle_clown_mutation(current, removing = FALSE) + current.faction -= "heretics" + +/datum/antagonist/heretic/get_admin_commands() + . = ..() + .["Equip"] = CALLBACK(src,.proc/equip_cultist) + +/datum/antagonist/heretic/roundend_report() + var/list/parts = list() + + var/cultiewin = TRUE + + parts += printplayer(owner) + parts += "Sacrifices Made: [total_sacrifices]" + + if(length(objectives)) + var/count = 1 + for(var/o in objectives) + var/datum/objective/objective = o + if(objective.check_completion()) + parts += "Objective #[count]: [objective.explanation_text] Success!" + else + parts += "Objective #[count]: [objective.explanation_text] Fail." + cultiewin = FALSE + count++ + if(ascended) + parts += "HERETIC HAS ASCENDED!" + else + if(cultiewin) + parts += "The heretic was successful!" + else + parts += "The heretic has failed." + + parts += "Knowledge Researched: " + + var/list/knowledge_message = list() + var/list/knowledge = get_all_knowledge() + for(var/X in knowledge) + var/datum/eldritch_knowledge/EK = knowledge[X] + knowledge_message += "[EK.name]" + parts += knowledge_message.Join(", ") + + return parts.Join("
") +//////////////// +// Knowledge // +//////////////// + +/datum/antagonist/heretic/proc/gain_knowledge(datum/eldritch_knowledge/EK) + if(get_knowledge(EK)) + return FALSE + var/datum/eldritch_knowledge/initialized_knowledge = new EK + researched_knowledge[initialized_knowledge.type] = initialized_knowledge + initialized_knowledge.on_gain(owner.current) + return TRUE + +/datum/antagonist/heretic/proc/get_researchable_knowledge() + var/list/researchable_knowledge = list() + var/list/banned_knowledge = list() + for(var/X in researched_knowledge) + var/datum/eldritch_knowledge/EK = researched_knowledge[X] + researchable_knowledge |= EK.next_knowledge + banned_knowledge |= EK.banned_knowledge + banned_knowledge |= EK.type + researchable_knowledge -= banned_knowledge + return researchable_knowledge + +/datum/antagonist/heretic/proc/get_knowledge(wanted) + return researched_knowledge[wanted] + +/datum/antagonist/heretic/proc/get_all_knowledge() + return researched_knowledge + +//////////////// +// Objectives // +//////////////// + +/datum/objective/sacrifice_ecult + name = "sacrifice" + +/datum/objective/sacrifice_ecult/update_explanation_text() + . = ..() + target_amount = rand(2,4) + explanation_text = "Sacrifice at least [target_amount] people." + +/datum/objective/sacrifice_ecult/check_completion() + if(!owner) + return FALSE + var/datum/antagonist/heretic/cultie = owner.has_antag_datum(/datum/antagonist/heretic) + if(!cultie) + return FALSE + return cultie.total_sacrifices >= target_amount diff --git a/code/modules/antagonists/eldritch_cult/eldritch_book.dm b/code/modules/antagonists/eldritch_cult/eldritch_book.dm new file mode 100644 index 0000000000..839150d37d --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_book.dm @@ -0,0 +1,145 @@ +/obj/item/forbidden_book + name = "Codex Cicatrix" + desc = "Book describing the secrets of the veil." + icon = 'icons/obj/eldritch.dmi' + icon_state = "book" + item_state = "book" + w_class = WEIGHT_CLASS_SMALL + ///Last person that touched this + var/mob/living/last_user + ///how many charges do we have? + var/charge = 0 + ///Where we cannot create the rune? + var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava)) + +/obj/item/forbidden_book/Destroy() + last_user = null + . = ..() + + +/obj/item/forbidden_book/examine(mob/user) + . = ..() + if(!IS_HERETIC(user)) + return + . += "The Tome holds [charge] charges." + . += "Use it on the floor to create a transmutation rune, used to perform rituals." + . += "Hit an influence in the black part with it to gain a charge." + . += "Hit a transmutation rune to destroy it." + +/obj/item/forbidden_book/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!proximity_flag || !IS_HERETIC(user)) + return + if(istype(target,/obj/effect/eldritch)) + remove_rune(target,user) + if(istype(target,/obj/effect/reality_smash)) + get_power_from_influence(target,user) + if(istype(target,/turf/open)) + draw_rune(target,user) + +///Gives you a charge and destroys a corresponding influence +/obj/item/forbidden_book/proc/get_power_from_influence(atom/target, mob/user) + var/obj/effect/reality_smash/RS = target + to_chat(target, "You start drawing power from influence...") + if(do_after(user,10 SECONDS,TRUE,RS)) + qdel(RS) + charge += 1 + +///Draws a rune on a selected turf +/obj/item/forbidden_book/proc/draw_rune(atom/target,mob/user) + + for(var/turf/T in range(1,target)) + if(is_type_in_typecache(T, blacklisted_turfs)) + to_chat(target, "The terrain doesn't support runes!") + return + var/A = get_turf(target) + to_chat(user, "You start drawing a rune...") + + if(do_after(user,30 SECONDS,FALSE, user)) + + new /obj/effect/eldritch/big(A) + +///Removes runes from the selected turf +/obj/item/forbidden_book/proc/remove_rune(atom/target,mob/user) + + to_chat(user, "You start removing a rune...") + if(do_after(user,2 SECONDS,FALSE, user)) + qdel(target) + +/obj/item/forbidden_book/ui_interact(mob/user, datum/tgui/ui = null) + if(!IS_HERETIC(user)) + return FALSE + last_user = user + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + icon_state = "book_open" + flick("book_opening", src) + ui = new(user, src, "ForbiddenLore", name) + ui.open() + +/obj/item/forbidden_book/ui_data(mob/user) + var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic) + var/list/to_know = list() + for(var/Y in cultie.get_researchable_knowledge()) + to_know += new Y + var/list/known = cultie.get_all_knowledge() + var/list/data = list() + var/list/lore = list() + + data["charges"] = charge + + for(var/X in to_know) + lore = list() + var/datum/eldritch_knowledge/EK = X + lore["type"] = EK.type + lore["name"] = EK.name + lore["cost"] = EK.cost + lore["disabled"] = EK.cost <= charge ? FALSE : TRUE + lore["path"] = EK.route + lore["state"] = "Research" + lore["flavour"] = EK.gain_text + lore["desc"] = EK.desc + data["to_know"] += list(lore) + + for(var/X in known) + lore = list() + var/datum/eldritch_knowledge/EK = known[X] + lore["name"] = EK.name + lore["cost"] = EK.cost + lore["disabled"] = TRUE + lore["path"] = EK.route + lore["state"] = "Researched" + lore["flavour"] = EK.gain_text + lore["desc"] = EK.desc + data["to_know"] += list(lore) + + if(!length(data["to_know"])) + data["to_know"] = null + + return data + +/obj/item/forbidden_book/ui_act(action, params) + . = ..() + if(.) + return + switch(action) + if("research") + var/datum/antagonist/heretic/cultie = last_user.mind.has_antag_datum(/datum/antagonist/heretic) + var/ekname = params["name"] + for(var/X in cultie.get_researchable_knowledge()) + var/datum/eldritch_knowledge/EK = X + if(initial(EK.name) != ekname) + continue + if(cultie.gain_knowledge(EK)) + charge -= text2num(params["cost"]) + return TRUE + + update_icon() // Not applicable to all objects. + +/obj/item/forbidden_book/ui_close(mob/user) + flick("book_closing",src) + icon_state = initial(icon_state) + return ..() + +/obj/item/forbidden_book/debug + charge = 100 diff --git a/code/modules/antagonists/eldritch_cult/eldritch_effects.dm b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm new file mode 100644 index 0000000000..899e588bda --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_effects.dm @@ -0,0 +1,289 @@ +/obj/effect/eldritch + name = "Generic rune" + desc = "Weird combination of shapes and symbols etched into the floor itself. The indentation is filled with thick black tar-like fluid." + anchored = TRUE + icon_state = "" + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + layer = SIGIL_LAYER + ///Used mainly for summoning ritual to prevent spamming the rune to create millions of monsters. + var/is_in_use = FALSE + +/obj/effect/eldritch/attack_hand(mob/living/user) + . = ..() + if(.) + return + try_activate(user) + +/obj/effect/eldritch/proc/try_activate(mob/living/user) + if(!IS_HERETIC(user)) + return + if(!is_in_use) + INVOKE_ASYNC(src, .proc/activate , user) + +/obj/effect/eldritch/attacked_by(obj/item/I, mob/living/user) + . = ..() + if(istype(I,/obj/item/nullrod)) + qdel(src) + +/obj/effect/eldritch/proc/activate(mob/living/user) + is_in_use = TRUE + // Have fun trying to read this proc. + var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic) + var/list/knowledge = cultie.get_all_knowledge() + var/list/atoms_in_range = list() + + for(var/A in range(1, src)) + var/atom/atom_in_range = A + if(istype(atom_in_range,/area)) + continue + if(istype(atom_in_range,/turf)) // we dont want turfs + continue + if(istype(atom_in_range,/mob/living)) + var/mob/living/living_in_range = atom_in_range + if(living_in_range.stat != DEAD || living_in_range == user) // we only accept corpses, no living beings allowed. + continue + atoms_in_range += atom_in_range + for(var/X in knowledge) + var/datum/eldritch_knowledge/current_eldritch_knowledge = knowledge[X] + + //has to be done so that we can freely edit the local_required_atoms without fucking up the eldritch knowledge + var/list/local_required_atoms = list() + + if(!current_eldritch_knowledge.required_atoms || current_eldritch_knowledge.required_atoms.len == 0) + continue + + local_required_atoms += current_eldritch_knowledge.required_atoms + + var/list/selected_atoms = list() + + if(!current_eldritch_knowledge.recipe_snowflake_check(atoms_in_range,drop_location(),selected_atoms)) + continue + + for(var/LR in local_required_atoms) + var/list/local_required_atom_list = LR + + for(var/LAIR in atoms_in_range) + var/atom/local_atom_in_range = LAIR + if(is_type_in_list(local_atom_in_range,local_required_atom_list)) + selected_atoms |= local_atom_in_range + local_required_atoms -= list(local_required_atom_list) + + if(length(local_required_atoms) > 0) + continue + + flick("[icon_state]_active",src) + playsound(user, 'sound/magic/castsummon.ogg', 75, TRUE) + //we are doing this since some on_finished_recipe subtract the atoms from selected_atoms making them invisible permanently. + var/list/atoms_to_disappear = selected_atoms.Copy() + for(var/to_disappear in atoms_to_disappear) + var/atom/atom_to_disappear = to_disappear + //temporary so we dont have to deal with the bs of someone picking those up when they may be deleted + atom_to_disappear.invisibility = INVISIBILITY_ABSTRACT + if(current_eldritch_knowledge.on_finished_recipe(user,selected_atoms,loc)) + current_eldritch_knowledge.cleanup_atoms(selected_atoms) + is_in_use = FALSE + + for(var/to_appear in atoms_to_disappear) + var/atom/atom_to_appear = to_appear + //we need to reappear the item just in case the ritual didnt consume everything... or something. + atom_to_appear.invisibility = initial(atom_to_appear.invisibility) + + return + is_in_use = FALSE + to_chat(user,"Your ritual failed! You used either wrong components or are missing something important!") + +/obj/effect/eldritch/big + name = "transmutation circle" + icon = 'icons/effects/96x96.dmi' + icon_state = "eldritch_rune1" + pixel_x = -32 //So the big ol' 96x96 sprite shows up right + pixel_y = -32 + +/** + * #Reality smash tracker + * + * Stupid fucking list holder, DONT create new ones, it will break the game, this is automnatically created whenever eldritch cultists are created. + * + * Tracks relevant data, generates relevant data, useful tool + */ +/datum/reality_smash_tracker + ///list of tracked reality smashes + var/list/smashes = list() + ///List of mobs with ability to see the smashes + var/list/targets = list() + +/datum/reality_smash_tracker/Destroy(force, ...) + if(GLOB.reality_smash_track == src) + stack_trace("/datum/reality_smash_tracker was deleted. Heretics may no longer access any influences. Fix it or call coder support") + QDEL_LIST(smashes) + targets.Cut() + return ..() + +/** + * Automatically fixes the target and smash network + * + * Fixes any bugs that are caused by late Generate() or exchanging clients + */ +/datum/reality_smash_tracker/proc/ReworkNetwork() + listclearnulls(smashes) + for(var/mind in targets) + if(isnull(mind)) + stack_trace("A null somehow landed in a list of minds") + continue + for(var/X in smashes) + var/obj/effect/reality_smash/reality_smash = X + reality_smash.AddMind(mind) + +/** + * Generates a set amount of reality smashes based on the N value + * + * Automatically creates more reality smashes + */ +/datum/reality_smash_tracker/proc/_Generate() + var/targ_len = length(targets) + var/smash_len = length(smashes) + var/number = targ_len * 6 - smash_len + + for(var/i in 0 to number) + + var/turf/chosen_location = get_safe_random_station_turf() + //we also dont want them close to each other, at least 1 tile of seperation + var/obj/effect/reality_smash/what_if_i_have_one = locate() in range(1, chosen_location) + var/obj/effect/broken_illusion/what_if_i_had_one_but_got_used = locate() in range(1, chosen_location) + if(what_if_i_have_one || what_if_i_had_one_but_got_used) //we dont want to spawn + continue + var/obj/effect/reality_smash/RS = new/obj/effect/reality_smash(chosen_location) + smashes += RS + ReworkNetwork() + + +/** + * Adds a mind to the list of people that can see the reality smashes + * + * Use this whenever you want to add someone to the list + */ +/datum/reality_smash_tracker/proc/AddMind(var/datum/mind/M) + RegisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN,.proc/ReworkNetwork) + targets |= M + _Generate() + for(var/X in smashes) + var/obj/effect/reality_smash/reality_smash = X + reality_smash.AddMind(M) + + +/** + * Removes a mind from the list of people that can see the reality smashes + * + * Use this whenever you want to remove someone from the list + */ +/datum/reality_smash_tracker/proc/RemoveMind(var/datum/mind/M) + UnregisterSignal(M.current,COMSIG_MOB_CLIENT_LOGIN) + targets -= M + for(var/obj/effect/reality_smash/RS in smashes) + RS.RemoveMind(M) + +/obj/effect/broken_illusion + name = "pierced reality" + icon = 'icons/effects/eldritch.dmi' + icon_state = "pierced_illusion" + anchored = TRUE + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/broken_illusion/attack_hand(mob/living/user) + if(!ishuman(user)) + return ..() + var/mob/living/carbon/human/human_user = user + if(IS_HERETIC(human_user)) + to_chat(human_user,"You know better than to tempt forces out of your control.") + else + var/obj/item/bodypart/arm = human_user.get_active_hand() + if(prob(25)) + to_chat(human_user,"An otherwordly presence tears your arm apart into atoms as you try to touch the hole in the very fabric of reality!") + arm.dismember() + qdel(arm) + else + to_chat(human_user,"You pull your hand away from the hole as eldritch energy flails out, trying to latch onto existence itself!") + +/obj/effect/broken_illusion/attack_tk(mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + if(IS_HERETIC(human_user)) + to_chat(human_user,"You know better than to tempt forces out of your control.") + else + //a very elaborate way to suicide + to_chat(human_user,"Eldritch energy lashes out, piercing your fragile mind, tearing it to pieces!") + human_user.ghostize() + var/obj/item/bodypart/head/head = locate() in human_user.bodyparts + if(head) + head.dismember() + qdel(head) + else + human_user.gib() + + var/datum/effect_system/reagents_explosion/explosion = new() + explosion.set_up(1, get_turf(human_user), 1, 0) + explosion.start() + +/obj/effect/broken_illusion/examine(mob/user) + if(!IS_HERETIC(user) && ishuman(user)) + var/mob/living/carbon/human/human_user = user + to_chat(human_user,"Your brain hurts when you look at this!") + human_user.adjustOrganLoss(ORGAN_SLOT_BRAIN,30) + . = ..() + +/obj/effect/reality_smash + name = "/improper reality smash" + icon = 'icons/effects/eldritch.dmi' + anchored = TRUE + resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///We cannot use icon_state since this is invisible, functions the same way but with custom behaviour. + var/image_state = "reality_smash" + ///Who can see us? + var/list/minds = list() + ///Tracked image + var/image/img + +/obj/effect/reality_smash/Initialize() + . = ..() + img = image(icon, src, image_state, OBJ_LAYER) + generate_name() + +/obj/effect/reality_smash/Destroy() + on_destroy() + return ..() + +///Custom effect that happens on destruction +/obj/effect/reality_smash/proc/on_destroy() + for(var/cm in minds) + var/datum/mind/cultie = cm + if(cultie.current?.client) + cultie.current.client.images -= img + //clear the list + minds -= cultie + GLOB.reality_smash_track.smashes -= src + img = null + new /obj/effect/broken_illusion(drop_location()) + +///Makes the mind able to see this effect +/obj/effect/reality_smash/proc/AddMind(var/datum/mind/cultie) + minds |= cultie + if(cultie.current.client) + cultie.current.client.images |= img + + + +///Makes the mind not able to see this effect +/obj/effect/reality_smash/proc/RemoveMind(var/datum/mind/cultie) + minds -= cultie + if(cultie.current.client) + cultie.current.client.images -= img + + + +///Generates random name +/obj/effect/reality_smash/proc/generate_name() + var/static/list/prefix = list("Omniscient","Thundering","Enlightening","Intrusive","Rejectful","Atomized","Subtle","Rising","Lowering","Fleeting","Towering","Blissful","Arrogant","Threatening","Peaceful","Aggressive") + var/static/list/postfix = list("Flaw","Presence","Crack","Heat","Cold","Memory","Reminder","Breeze","Grasp","Sight","Whisper","Flow","Touch","Veil","Thought","Imperfection","Blemish","Blush") + + name = pick(prefix) + " " + pick(postfix) diff --git a/code/modules/antagonists/eldritch_cult/eldritch_items.dm b/code/modules/antagonists/eldritch_cult/eldritch_items.dm new file mode 100644 index 0000000000..4dab6d789d --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_items.dm @@ -0,0 +1,142 @@ +/obj/item/living_heart + name = "living heart" + desc = "Link to the worlds beyond." + icon = 'icons/obj/eldritch.dmi' + icon_state = "living_heart" + w_class = WEIGHT_CLASS_SMALL + ///Target + var/mob/living/carbon/human/target + +/obj/item/living_heart/attack_self(mob/user) + . = ..() + if(!IS_HERETIC(user)) + return + if(!target) + to_chat(user,"No target could be found. Put the living heart on the rune and use the rune to recieve a target.") + return + var/dist = get_dist(user.loc,target.loc) + var/dir = get_dir(user.loc,target.loc) + + switch(dist) + if(0 to 15) + to_chat(user,"[target.real_name] is near you. They are to the [dir2text(dir)] of you!") + if(16 to 31) + to_chat(user,"[target.real_name] is somewhere in your vicinty. They are to the [dir2text(dir)] of you!") + if(32 to 127) + to_chat(user,"[target.real_name] is far away from you. They are to the [dir2text(dir)] of you!") + else + to_chat(user,"[target.real_name] is beyond our reach.") + + if(target.stat == DEAD) + to_chat(user,"[target.real_name] is dead. Bring them onto a transmutation rune!") + +/obj/item/melee/sickly_blade + name = "eldritch blade" + desc = "A sickly green crescent blade, decorated with an ornamental eye. You feel like you're being watched..." + icon = 'icons/obj/eldritch.dmi' + icon_state = "eldritch_blade" + item_state = "eldritch_blade" + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + flags_1 = CONDUCT_1 + sharpness = SHARP_EDGED + w_class = WEIGHT_CLASS_NORMAL + force = 17 + throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "tore", "lacerated", "ripped", "diced", "rended") + +/obj/item/melee/sickly_blade/attack(mob/living/M, mob/living/user) + if(!IS_HERETIC(user)) + to_chat(user,"You feel a pulse of some alien intellect lash out at your mind!") + var/mob/living/carbon/human/human_user = user + human_user.AdjustParalyzed(5 SECONDS) + return FALSE + return ..() + +/obj/item/melee/sickly_blade/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + var/datum/antagonist/heretic/cultie = user.mind.has_antag_datum(/datum/antagonist/heretic) + if(!cultie || !proximity_flag) + return + var/list/knowledge = cultie.get_all_knowledge() + for(var/X in knowledge) + var/datum/eldritch_knowledge/eldritch_knowledge_datum = knowledge[X] + eldritch_knowledge_datum.on_eldritch_blade(target,user,proximity_flag,click_parameters) + +/obj/item/melee/sickly_blade/rust + name = "rusted blade" + desc = "This crescent blade is decrepit, wasting to dust. Yet still it bites, catching flesh with jagged, rotten teeth." + icon_state = "rust_blade" + item_state = "rust_blade" + embedding = list("pain_mult" = 4, "embed_chance" = 75, "fall_chance" = 10, "ignore_throwspeed_threshold" = TRUE) + throwforce = 17 + +/obj/item/melee/sickly_blade/ash + name = "ashen blade" + desc = "Molten and unwrought, a hunk of metal warped to cinders and slag. Unmade, it aspires to be more than it is, and shears soot-filled wounds with a blunt edge." + icon_state = "ash_blade" + item_state = "ash_blade" + force = 20 + +/obj/item/melee/sickly_blade/flesh + name = "flesh blade" + desc = "A crescent blade born from a fleshwarped creature. Keenly aware, it seeks to spread to others the excruciations it has endured from dead origins." + icon_state = "flesh_blade" + item_state = "flesh_blade" + wound_bonus = 5 + bare_wound_bonus = 15 + +/obj/item/clothing/neck/eldritch_amulet + name = "warm eldritch medallion" + desc = "A strange medallion. Peering through the crystalline surface, the world around you melts away. You see your own beating heart, and the pulse of a thousand others." + icon = 'icons/obj/eldritch.dmi' + icon_state = "eye_medalion" + w_class = WEIGHT_CLASS_SMALL + ///What trait do we want to add upon equipiing + var/trait = TRAIT_THERMAL_VISION + +/obj/item/clothing/neck/eldritch_amulet/equipped(mob/user, slot) + . = ..() + if(ishuman(user) && user.mind && slot == SLOT_NECK && IS_HERETIC(user)) + ADD_TRAIT(user, trait, CLOTHING_TRAIT) + user.update_sight() + +/obj/item/clothing/neck/eldritch_amulet/dropped(mob/user) + . = ..() + REMOVE_TRAIT(user, trait, CLOTHING_TRAIT) + user.update_sight() + +/obj/item/clothing/neck/eldritch_amulet/piercing + name = "piercing eldritch medallion" + desc = "A strange medallion. Peering through the crystalline surface, the light refracts into new and terrifying spectrums of color. You see yourself, reflected off cascading mirrors, warped into improbable shapes." + trait = TRAIT_XRAY_VISION + +/obj/item/clothing/head/hooded/cult_hoodie/eldritch + name = "ominous hood" + icon_state = "eldritch" + desc = "A torn, dust-caked hood. Strange eyes line the inside." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + flash_protect = 2 + +/obj/item/clothing/suit/hooded/cultrobes/eldritch + name = "ominous armor" + desc = "A ragged, dusty set of robes. Strange eyes line the inside." + icon_state = "eldritch_armor" + item_state = "eldritch_armor" + flags_inv = HIDESHOES|HIDEJUMPSUIT + body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS + allowed = list(/obj/item/melee/sickly_blade, /obj/item/forbidden_book) + hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/eldritch + // slightly better than normal cult robes + armor = list("melee" = 50, "bullet" = 50, "laser" = 50,"energy" = 50, "bomb" = 35, "bio" = 20, "rad" = 0, "fire" = 20, "acid" = 20) + +/obj/item/reagent_containers/glass/beaker/eldritch + name = "flask of eldritch essence" + desc = "Toxic to the close minded. Healing to those with knowledge of the beyond." + icon = 'icons/obj/eldritch.dmi' + icon_state = "eldrich_flask" + list_reagents = list(/datum/reagent/eldritch = 50) diff --git a/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm new file mode 100644 index 0000000000..065844bedf --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_knowledge.dm @@ -0,0 +1,303 @@ + +/** + * #Eldritch Knwoledge + * + * Datum that makes eldritch cultist interesting. + * + * Eldritch knowledge aren't instantiated anywhere roundstart, and are initalized and destroyed as the round goes on. + */ +/datum/eldritch_knowledge + ///Name of the knowledge + var/name = "Basic knowledge" + ///Description of the knowledge + var/desc = "Basic knowledge of forbidden arts." + ///What shows up + var/gain_text = "" + ///Cost of knowledge in souls + var/cost = 0 + ///Next knowledge in the research tree + var/list/next_knowledge = list() + ///What knowledge is incompatible with this. This will simply make it impossible to research knowledges that are in banned_knowledge once this gets researched. + var/list/banned_knowledge = list() + ///Used with rituals, how many items this needs + var/list/required_atoms = list() + ///What do we get out of this + var/list/result_atoms = list() + ///What path is this on defaults to "Side" + var/route = PATH_SIDE + +/datum/eldritch_knowledge/New() + . = ..() + var/list/temp_list + for(var/X in required_atoms) + var/atom/A = X + temp_list += list(typesof(A)) + required_atoms = temp_list + +/** + * What happens when this is assigned to an antag datum + * + * This proc is called whenever a new eldritch knowledge is added to an antag datum + */ +/datum/eldritch_knowledge/proc/on_gain(mob/user) + to_chat(user, "[gain_text]") + return +/** + * What happens when you loose this + * + * This proc is called whenever antagonist looses his antag datum, put cleanup code in here + */ +/datum/eldritch_knowledge/proc/on_lose(mob/user) + return +/** + * What happens every tick + * + * This proc is called on SSprocess in eldritch cultist antag datum. SSprocess happens roughly every second + */ +/datum/eldritch_knowledge/proc/on_life(mob/user) + return + +/** + * Special check for recipes + * + * If you are adding a more complex summoning or something that requires a special check that parses through all the atoms in an area override this. + */ +/datum/eldritch_knowledge/proc/recipe_snowflake_check(list/atoms,loc) + return TRUE + +/** + * What happens once the recipe is succesfully finished + * + * By default this proc creates atoms from result_atoms list. Override this is you want something else to happen. + */ +/datum/eldritch_knowledge/proc/on_finished_recipe(mob/living/user,list/atoms,loc) + if(result_atoms.len == 0) + return FALSE + + for(var/A in result_atoms) + new A(loc) + + return TRUE + +/** + * Used atom cleanup + * + * Overide this proc if you dont want ALL ATOMS to be destroyed. useful in many situations. + */ +/datum/eldritch_knowledge/proc/cleanup_atoms(list/atoms) + for(var/X in atoms) + var/atom/A = X + if(!isliving(A)) + atoms -= A + qdel(A) + return + +/** + * Mansus grasp act + * + * Gives addtional effects to mansus grasp spell + */ +/datum/eldritch_knowledge/proc/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters) + return FALSE + + +/** + * Sickly blade act + * + * Gives addtional effects to sickly blade weapon + */ +/datum/eldritch_knowledge/proc/on_eldritch_blade(target,user,proximity_flag,click_parameters) + return + +////////////// +///Subtypes/// +////////////// + +/datum/eldritch_knowledge/spell + var/obj/effect/proc_holder/spell/spell_to_add + +/datum/eldritch_knowledge/spell/on_gain(mob/user) + var/obj/effect/proc_holder/S = new spell_to_add + user.mind.AddSpell(S) + return ..() + +/datum/eldritch_knowledge/spell/on_lose(mob/user) + user.mind.RemoveSpell(spell_to_add) + return ..() + +/datum/eldritch_knowledge/curse + var/timer = 5 MINUTES + var/list/fingerprints = list() + +/datum/eldritch_knowledge/curse/recipe_snowflake_check(list/atoms, loc) + fingerprints = list() + for(var/X in atoms) + var/atom/A = X + fingerprints |= A.fingerprints + listclearnulls(fingerprints) + if(fingerprints.len == 0) + return FALSE + return TRUE + +/datum/eldritch_knowledge/curse/on_finished_recipe(mob/living/user,list/atoms,loc) + + var/list/compiled_list = list() + + for(var/H in GLOB.human_list) + var/mob/living/carbon/human/human_to_check = H + if(fingerprints[md5(human_to_check.dna.uni_identity)]) + compiled_list |= human_to_check.real_name + compiled_list[human_to_check.real_name] = human_to_check + + if(compiled_list.len == 0) + to_chat(user, "The items don't posses required fingerprints.") + return FALSE + + var/chosen_mob = input("Select the person you wish to curse","Your target") as null|anything in sortList(compiled_list, /proc/cmp_mob_realname_dsc) + if(!chosen_mob) + return FALSE + curse(compiled_list[chosen_mob]) + addtimer(CALLBACK(src, .proc/uncurse, compiled_list[chosen_mob]),timer) + return TRUE + +/datum/eldritch_knowledge/curse/proc/curse(mob/living/chosen_mob) + return + +/datum/eldritch_knowledge/curse/proc/uncurse(mob/living/chosen_mob) + return + +/datum/eldritch_knowledge/summon + //Mob to summon + var/mob/living/mob_to_summon + + +/datum/eldritch_knowledge/summon/on_finished_recipe(mob/living/user,list/atoms,loc) + //we need to spawn the mob first so that we can use it in pollCandidatesForMob, we will move it from nullspace down the code + var/mob/living/summoned = new mob_to_summon(loc) + message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]") + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [summoned.name]", ROLE_HERETIC, null, FALSE, 100, summoned) + if(!LAZYLEN(candidates)) + to_chat(user,"No ghost could be found...") + qdel(summoned) + return FALSE + var/mob/dead/observer/C = pick(candidates) + log_game("[key_name_admin(C)] has taken control of ([key_name_admin(summoned)]), their master is [user.real_name]") + summoned.ghostize(FALSE) + summoned.key = C.key + summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic) + heretic_monster.set_owner(master) + return TRUE + +//Ascension knowledge +/datum/eldritch_knowledge/final + var/finished = FALSE + +/datum/eldritch_knowledge/final/recipe_snowflake_check(list/atoms, loc,selected_atoms) + if(finished) + return FALSE + var/counter = 0 + for(var/mob/living/carbon/human/H in atoms) + selected_atoms |= H + counter++ + if(counter == 3) + return TRUE + return FALSE + +/datum/eldritch_knowledge/final/on_finished_recipe( mob/living/user, list/atoms, loc) + finished = TRUE + return TRUE + +/datum/eldritch_knowledge/final/cleanup_atoms(list/atoms) + . = ..() + for(var/mob/living/carbon/human/H in atoms) + atoms -= H + H.gib() + + +/////////////// +///Base lore/// +/////////////// + +/datum/eldritch_knowledge/spell/basic + name = "Break of Dawn" + desc = "Starts your journey in the mansus. Allows you to select a target using a living heart on a transmutation rune." + gain_text = "Gates of Mansus open up to your mind." + next_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh) + cost = 0 + spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/mansus_grasp + required_atoms = list(/obj/item/living_heart) + route = "Start" + +/datum/eldritch_knowledge/spell/basic/recipe_snowflake_check(list/atoms, loc) + . = ..() + for(var/obj/item/living_heart/LH in atoms) + if(!LH.target) + return TRUE + if(LH.target in atoms) + return TRUE + return FALSE + +/datum/eldritch_knowledge/spell/basic/on_finished_recipe(mob/living/user, list/atoms, loc) + . = TRUE + var/mob/living/carbon/carbon_user = user + for(var/obj/item/living_heart/LH in atoms) + + if(LH.target && LH.target.stat == DEAD) + to_chat(carbon_user,"Your patrons accepts your offer...") + var/mob/living/carbon/human/H = LH.target + H.become_husk() + LH.target = null + var/datum/antagonist/heretic/EC = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic) + + EC.total_sacrifices++ + for(var/X in carbon_user.get_all_gear()) + if(!istype(X,/obj/item/forbidden_book)) + continue + var/obj/item/forbidden_book/FB = X + FB.charge++ + FB.charge++ + break + + if(!LH.target) + var/datum/objective/A = new + A.owner = user.mind + var/datum/mind/targeted = A.find_target()//easy way, i dont feel like copy pasting that entire block of code + LH.target = targeted.current + qdel(A) + if(LH.target) + to_chat(user,"Your new target has been selected, go and sacrifice [LH.target.real_name]!") + + else + to_chat(user,"target could not be found for living heart.") + +/datum/eldritch_knowledge/spell/basic/cleanup_atoms(list/atoms) + return + +/datum/eldritch_knowledge/living_heart + name = "Living Heart" + desc = "Allows you to create additional living hearts, using a heart, a pool of blood and a poppy. Living hearts when used on a transmutation rune will grant you a person to hunt and sacrifice on the rune. Every sacrifice gives you an additional charge in the book." + gain_text = "Disconnected, yet it still beats." + cost = 0 + required_atoms = list(/obj/item/organ/heart,/obj/effect/decal/cleanable/blood,/obj/item/reagent_containers/food/snacks/grown/poppy) + result_atoms = list(/obj/item/living_heart) + route = "Start" + +/datum/eldritch_knowledge/codex_cicatrix + name = "Codex Cicatrix" + desc = "Allows you to create a spare Codex Cicatrix if you have lost one, using a bible, human skin, a pen and a pair of eyes." + gain_text = "Their hands are at your throat, yet you see them not." + cost = 0 + required_atoms = list(/obj/item/organ/eyes,/obj/item/stack/sheet/animalhide/human,/obj/item/storage/book/bible,/obj/item/pen) + result_atoms = list(/obj/item/forbidden_book) + route = "Start" + +/datum/eldritch_knowledge/eldritch_blade + name = "Eldritch Blade" + desc = "Allows you to create a sickly, eldritch blade by transmuting a glass shard and a metal rod atop a transmutation rune." + gain_text = "The first step starts with sacrifice." + cost = 0 + required_atoms = list(/obj/item/shard,/obj/item/stack/rods) + result_atoms = list(/obj/item/melee/sickly_blade) + route = "Start" diff --git a/code/modules/antagonists/eldritch_cult/eldritch_magic.dm b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm new file mode 100644 index 0000000000..d84a997e28 --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_magic.dm @@ -0,0 +1,668 @@ +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash + name = "Ashen Passage" + desc = "Low range spell allowing you to pass through a few walls." + school = "transmutation" + invocation = "DULK'ES PRE'ZIMAS" + invocation_type = "whisper" + charge_max = 150 + range = -1 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "ash_shift" + action_background_icon_state = "bg_ecult" + jaunt_in_time = 13 + jaunt_duration = 10 + jaunt_in_type = /obj/effect/temp_visual/dir_setting/ash_shift + jaunt_out_type = /obj/effect/temp_visual/dir_setting/ash_shift/out + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/long + jaunt_duration = 50 + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/play_sound() + return + +/obj/effect/temp_visual/dir_setting/ash_shift + name = "ash_shift" + icon = 'icons/mob/mob.dmi' + icon_state = "ash_shift2" + duration = 13 + +/obj/effect/temp_visual/dir_setting/ash_shift/out + icon_state = "ash_shift" + +/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp + name = "Mansus Grasp" + desc = "Touch spell that allows you to channel the power of the Old Gods through you." + hand_path = /obj/item/melee/touch_attack/mansus_fist + school = "evocation" + charge_max = 150 + clothes_req = FALSE + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "mansus_grasp" + action_background_icon_state = "bg_ecult" + +/obj/item/melee/touch_attack/mansus_fist + name = "Mansus Grasp" + desc = "A sinister looking aura that distorts the flow of reality around it. Causes knockdown, major stamina damage aswell as some Brute. It gains additional beneficial effects with certain knowledges you can research." + icon_state = "disintegrate" + item_state = "disintegrate" + catchphrase = "T'IESA SIE'KTI VISATA" + +/obj/item/melee/touch_attack/mansus_fist/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + + if(!proximity_flag || target == user) + return + playsound(user, 'sound/items/welder.ogg', 75, TRUE) + if(ishuman(target)) + var/mob/living/carbon/human/tar = target + if(tar.anti_magic_check()) + tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!") + return ..() + var/datum/mind/M = user.mind + var/datum/antagonist/heretic/cultie = M.has_antag_datum(/datum/antagonist/heretic) + + var/use_charge = FALSE + if(iscarbon(target)) + use_charge = TRUE + var/mob/living/carbon/C = target + C.adjustBruteLoss(15) + C.DefaultCombatKnockdown(50, override_stamdmg = 0) + C.adjustStaminaLoss(60) + var/list/knowledge = cultie.get_all_knowledge() + + for(var/X in knowledge) + var/datum/eldritch_knowledge/EK = knowledge[X] + if(EK.on_mansus_grasp(target, user, proximity_flag, click_parameters)) + use_charge = TRUE + if(use_charge) + return ..() + +/obj/effect/proc_holder/spell/aoe_turf/rust_conversion + name = "Aggressive Spread" + desc = "Spreads rust onto nearby turfs." + school = "transmutation" + charge_max = 300 //twice as long as mansus grasp + clothes_req = FALSE + invocation = "PLI'STI MINO DOMI'KA" + invocation_type = "whisper" + range = 3 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "corrode" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/cast(list/targets, mob/user = usr) + playsound(user, 'sound/items/welder.ogg', 75, TRUE) + for(var/turf/T in targets) + ///What we want is the 3 tiles around the user and the tile under him to be rusted, so min(dist,1)-1 causes us to get 0 for these tiles, rest of the tiles are based on chance + var/chance = 100 - (max(get_dist(T,user),1)-1)*100/(range+1) + if(!prob(chance)) + continue + T.rust_heretic_act() + +/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small + name = "Rust Conversion" + desc = "Spreads rust onto nearby turfs." + range = 2 + +/obj/effect/proc_holder/spell/targeted/touch/blood_siphon + name = "Blood Siphon" + desc = "Touch spell that heals you while damaging the enemy, has a chance to transfer wounds between you and your enemy." + hand_path = /obj/item/melee/touch_attack/blood_siphon + school = "evocation" + charge_max = 150 + clothes_req = FALSE + invocation_type = "none" + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "blood_siphon" + action_background_icon_state = "bg_ecult" + +/obj/item/melee/touch_attack/blood_siphon + name = "Blood Siphon" + desc = "A sinister looking aura that distorts the flow of reality around it." + icon_state = "disintegrate" + item_state = "disintegrate" + catchphrase = "SUN'AI'KINI'MAS" + +/obj/item/melee/touch_attack/blood_siphon/afterattack(atom/target, mob/user, proximity_flag, proximity) + if(!proximity_flag) + return + playsound(user, 'sound/effects/curseattack.ogg', 75, TRUE) + if(ishuman(target)) + var/mob/living/carbon/human/tar = target + if(tar.anti_magic_check()) + tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!") + return ..() + var/mob/living/carbon/C2 = user + if(isliving(target)) + var/mob/living/L = target + L.adjustBruteLoss(20) + C2.adjustBruteLoss(-20) + if(iscarbon(target)) + var/mob/living/carbon/C1 = target + for(var/obj/item/bodypart/bodypart in C2.bodyparts) + for(var/i in bodypart.wounds) + var/datum/wound/iter_wound = i + if(prob(50)) + continue + var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in C1.bodyparts + if(!target_bodypart) + continue + iter_wound.remove_wound() + iter_wound.apply_wound(target_bodypart) + + C1.blood_volume -= 20 + if(C2.blood_volume < BLOOD_VOLUME_MAXIMUM) //we dont want to explode after all + C2.blood_volume += 20 + return ..() + +/obj/effect/proc_holder/spell/aimed/rust_wave + name = "Patron's Reach" + desc = "Channels energy into your gauntlet - firing it results in a wave of rust being created in it's wake." + projectile_type = /obj/item/projectile/magic/spell/rust_wave + charge_max = 350 + clothes_req = FALSE + action_icon = 'icons/mob/actions/actions_ecult.dmi' + base_icon_state = "rust_wave" + action_icon_state = "rust_wave" + action_background_icon_state = "bg_ecult" + sound = 'sound/effects/curse5.ogg' + active_msg = "You extend your hand out, preparing to send out a wave of rust." + deactive_msg = "You extinguish that energy, for now..." + invocation = "RUD'ZI VAR'ZTAS" + invocation_type = "whisper" + +/obj/item/projectile/magic/spell/rust_wave + name = "rust bolt" + icon_state = "eldritch_projectile" + alpha = 180 + damage = 30 + damage_type = TOX + nodamage = 0 + hitsound = 'sound/effects/curseattack.ogg' + range = 15 + +/obj/item/projectile/magic/spell/rust_wave/Moved(atom/OldLoc, Dir) + . = ..() + playsound(src, 'sound/items/welder.ogg', 75, TRUE) + var/list/turflist = list() + var/turf/T1 + turflist += get_turf(src) + T1 = get_step(src,turn(dir,90)) + turflist += T1 + turflist += get_step(T1,turn(dir,90)) + T1 = get_step(src,turn(dir,-90)) + turflist += T1 + turflist += get_step(T1,turn(dir,-90)) + for(var/X in turflist) + if(!X || prob(25)) + continue + var/turf/T = X + T.rust_heretic_act() + +/obj/effect/proc_holder/spell/aimed/rust_wave/short + name = "Small Patron's Reach" + projectile_type = /obj/item/projectile/magic/spell/rust_wave/short + +/obj/item/projectile/magic/spell/rust_wave/short + range = 7 + +/obj/effect/proc_holder/spell/pointed/cleave + name = "Cleave" + desc = "Causes severe bleeding on a target and people around them" + school = "transmutation" + charge_max = 350 + clothes_req = FALSE + invocation = "PLES'TI VI'RIBUS" + invocation_type = "whisper" + range = 9 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "cleave" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/pointed/cleave/cast(list/targets, mob/user) + if(!targets.len) + to_chat(user, "No target found in range!") + return FALSE + if(!can_target(targets[1], user)) + return FALSE + + for(var/mob/living/carbon/human/C in range(1,targets[1])) + targets |= C + + + for(var/X in targets) + var/mob/living/carbon/human/target = X + if(target == user) + continue + if(target.anti_magic_check()) + to_chat(user, "The spell had no effect!") + target.visible_message("[target]'s veins flash with fire, but their magic protection repulses the blaze!", \ + "Your veins flash with fire, but your magic protection repels the blaze!") + continue + + target.visible_message("[target]'s veins are shredded from within as an unholy blaze erupts from their blood!", \ + "Your veins burst from within and unholy flame erupts from your blood!") + var/obj/item/bodypart/bodypart = pick(target.bodyparts) + var/datum/wound/slash/critical/crit_wound = new + crit_wound.apply_wound(bodypart) + target.adjustFireLoss(20) + new /obj/effect/temp_visual/cleave(target.drop_location()) + +/obj/effect/proc_holder/spell/pointed/cleave/can_target(atom/target, mob/user, silent) + . = ..() + if(!.) + return FALSE + if(!istype(target,/mob/living/carbon/human)) + if(!silent) + to_chat(user, "You are unable to cleave [target]!") + return FALSE + return TRUE + +/obj/effect/proc_holder/spell/pointed/cleave/long + charge_max = 650 + +/obj/effect/proc_holder/spell/pointed/touch/mad_touch + name = "Touch of Madness" + desc = "Touch spell that drains your enemies sanity." + school = "transmutation" + charge_max = 150 + clothes_req = FALSE + invocation_type = "none" + range = 2 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "mad_touch" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/pointed/touch/mad_touch/can_target(atom/target, mob/user, silent) + . = ..() + if(!.) + return FALSE + if(!istype(target,/mob/living/carbon/human)) + if(!silent) + to_chat(user, "You are unable to touch [target]!") + return FALSE + return TRUE + +/obj/effect/proc_holder/spell/pointed/touch/mad_touch/cast(list/targets, mob/user) + . = ..() + for(var/mob/living/carbon/target in targets) + if(ishuman(targets)) + var/mob/living/carbon/human/tar = target + if(tar.anti_magic_check()) + tar.visible_message("Spell bounces off of [target]!","The spell bounces off of you!") + return + if(target.mind && !target.mind.has_antag_datum(/datum/antagonist/heretic)) + to_chat(user,"[target.name] has been cursed!") + SEND_SIGNAL(target, COMSIG_ADD_MOOD_EVENT, "gates_of_mansus", /datum/mood_event/gates_of_mansus) + +/obj/effect/proc_holder/spell/pointed/ash_final + name = "Nightwatcher's Rite" + desc = "Powerful spell that releases 5 streams of fire away from you." + school = "transmutation" + invocation = "IGNIS'INTI" + invocation_type = "whisper" + charge_max = 300 + range = 15 + clothes_req = FALSE + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "flames" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/pointed/ash_final/cast(list/targets, mob/user) + for(var/X in targets) + var/T + T = line_target(-25, range, X, user) + INVOKE_ASYNC(src, .proc/fire_line, user,T) + T = line_target(10, range, X, user) + INVOKE_ASYNC(src, .proc/fire_line, user,T) + T = line_target(0, range, X, user) + INVOKE_ASYNC(src, .proc/fire_line, user,T) + T = line_target(-10, range, X, user) + INVOKE_ASYNC(src, .proc/fire_line, user,T) + T = line_target(25, range, X, user) + INVOKE_ASYNC(src, .proc/fire_line, user,T) + return ..() + +/obj/effect/proc_holder/spell/pointed/ash_final/proc/line_target(offset, range, atom/at , atom/user) + if(!at) + return + var/angle = ATAN2(at.x - user.x, at.y - user.y) + offset + var/turf/T = get_turf(user) + for(var/i in 1 to range) + var/turf/check = locate(user.x + cos(angle) * i, user.y + sin(angle) * i, user.z) + if(!check) + break + T = check + return (getline(user, T) - get_turf(user)) + +/obj/effect/proc_holder/spell/pointed/ash_final/proc/fire_line(atom/source, list/turfs) + var/list/hit_list = list() + for(var/turf/T in turfs) + if(istype(T, /turf/closed)) + break + + for(var/mob/living/L in T.contents) + if(L.anti_magic_check()) + L.visible_message("Spell bounces off of [L]!","The spell bounces off of you!") + continue + if(L in hit_list || L == source) + continue + hit_list += L + L.adjustFireLoss(20) + to_chat(L, "You're hit by [source]'s fire breath!") + + new /obj/effect/hotspot(T) + T.hotspot_expose(700,50,1) + // deals damage to mechs + for(var/obj/mecha/M in T.contents) + if(M in hit_list) + continue + hit_list += M + M.take_damage(45, BURN, "melee", 1) + sleep(1.5) + +/obj/effect/proc_holder/spell/targeted/shapeshift/eldritch + invocation_type = "none" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + sound = 'sound/magic/enter_blood.ogg' + possible_shapes = list(/mob/living/simple_animal/mouse,\ + /mob/living/simple_animal/pet/dog/corgi,\ + /mob/living/simple_animal/hostile/carp,\ + /mob/living/simple_animal/bot/secbot, \ + /mob/living/simple_animal/pet/fox,\ + /mob/living/simple_animal/pet/cat ) + +/obj/effect/proc_holder/spell/targeted/emplosion/eldritch + name = "Energetic Pulse" + invocation_type = "none" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + range = -1 + include_user = TRUE + charge_max = 300 + emp_heavy = 6 + emp_light = 10 + sound = 'sound/effects/lingscreech.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/fire_cascade + name = "Fire Cascade" + desc = "creates hot turfs around you." + school = "transmutation" + charge_max = 300 //twice as long as mansus grasp + clothes_req = FALSE + invocation = "IGNIS'SAVARIN" + invocation_type = "whisper" + range = 4 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "fire_ring" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/cast(list/targets, mob/user = usr) + INVOKE_ASYNC(src, .proc/fire_cascade, user,range) + +/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/proc/fire_cascade(atom/centre,max_range) + playsound(get_turf(centre), 'sound/items/welder.ogg', 75, TRUE) + var/_range = 1 + for(var/i = 0, i <= max_range,i++) + for(var/turf/T in spiral_range_turfs(_range,centre)) + new /obj/effect/hotspot(T) + T.hotspot_expose(700,50,1) + for(var/mob/living/livies in T.contents - centre) + livies.adjustFireLoss(10) + _range++ + sleep(3) + +/obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big + range = 6 + +/obj/effect/proc_holder/spell/targeted/telepathy/eldritch + invocation = "" + invocation_type = "whisper" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/targeted/fire_sworn + name = "Oath of Fire" + desc = "For a minute you will passively create a ring of fire around you." + invocation = "IGNIS'AISTRA'LISTRE" + invocation_type = "whisper" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + range = -1 + include_user = TRUE + charge_max = 700 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "fire_ring" + ///how long it lasts + var/duration = 1 MINUTES + ///who casted it right now + var/mob/current_user + ///Determines if you get the fire ring effect + var/has_fire_ring = FALSE + +/obj/effect/proc_holder/spell/targeted/fire_sworn/cast(list/targets, mob/user) + . = ..() + current_user = user + has_fire_ring = TRUE + addtimer(CALLBACK(src, .proc/remove, user), duration, TIMER_OVERRIDE|TIMER_UNIQUE) + +/obj/effect/proc_holder/spell/targeted/fire_sworn/proc/remove() + has_fire_ring = FALSE + +/obj/effect/proc_holder/spell/targeted/fire_sworn/process() + . = ..() + if(!has_fire_ring) + return + for(var/turf/T in range(1,current_user)) + new /obj/effect/hotspot(T) + T.hotspot_expose(700,50,1) + for(var/mob/living/livies in T.contents - current_user) + livies.adjustFireLoss(5) + + +/obj/effect/proc_holder/spell/targeted/worm_contract + name = "Force Contract" + desc = "Forces all the worm parts to collapse onto a single turf" + invocation_type = "none" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + range = -1 + include_user = TRUE + charge_max = 300 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "worm_contract" + +/obj/effect/proc_holder/spell/targeted/worm_contract/cast(list/targets, mob/user) + . = ..() + if(!istype(user,/mob/living/simple_animal/hostile/eldritch/armsy)) + to_chat(user, "You try to contract your muscles but nothing happens...") + var/mob/living/simple_animal/hostile/eldritch/armsy/armsy = user + armsy.contract_next_chain_into_single_tile() + +/obj/effect/temp_visual/cleave + icon = 'icons/effects/eldritch.dmi' + icon_state = "cleave" + duration = 6 + +/obj/effect/temp_visual/eldritch_smoke + icon = 'icons/effects/eldritch.dmi' + icon_state = "smoke" + duration = 10 + +/obj/effect/proc_holder/spell/targeted/fiery_rebirth + name = "Nightwatcher's Rebirth" + desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off." + invocation = "PETHRO'MINO'IGNI" + invocation_type = "whisper" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + range = -1 + include_user = TRUE + charge_max = 600 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "smoke" + +/obj/effect/proc_holder/spell/targeted/fiery_rebirth/cast(list/targets, mob/user) + if(!ishuman(user)) + return + var/mob/living/carbon/human/human_user = user + for(var/mob/living/carbon/target in view(7,user)) + if(target.stat == DEAD || !target.on_fire) + continue + //This is essentially a death mark, use this to finish your opponent quicker. + if(target.InCritical()) + target.death() + target.adjustFireLoss(20) + new /obj/effect/temp_visual/eldritch_smoke(target.drop_location()) + human_user.ExtinguishMob() + human_user.adjustBruteLoss(-10, FALSE) + human_user.adjustFireLoss(-10, FALSE) + human_user.adjustStaminaLoss(-10, FALSE) + human_user.adjustToxLoss(-10, FALSE) + human_user.adjustOxyLoss(-10) + +/obj/effect/proc_holder/spell/pointed/manse_link + name = "Mansus Link" + desc = "Piercing through reality, connecting minds. This spell allows you to add people to a mansus net, allowing them to communicate with eachother" + school = "transmutation" + charge_max = 300 + clothes_req = FALSE + invocation = "SUSEI' METO MIN'TIS" + invocation_type = "whisper" + range = 10 + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "mansus_link" + action_background_icon_state = "bg_ecult" + +/obj/effect/proc_holder/spell/pointed/manse_link/can_target(atom/target, mob/user, silent) + if(!isliving(target)) + return FALSE + return TRUE + +/obj/effect/proc_holder/spell/pointed/manse_link/cast(list/targets, mob/user) + var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator = user + + var/mob/living/target = targets[1] + + to_chat(originator, "You begin linking [target]'s mind to yours...") + to_chat(target, "You feel your mind being pulled... connected... intertwined with the very fabric of reality...") + if(!do_after(originator, 6 SECONDS, target)) + return + if(!originator.link_mob(target)) + to_chat(originator, "You can't seem to link [target]'s mind...") + to_chat(target, "The foreign presence leaves your mind.") + return + to_chat(originator, "You connect [target]'s mind to your mansus link!") + + +/datum/action/innate/mansus_speech + name = "Mansus Link" + desc = "Send a psychic message to everyone connected to your mansus link." + button_icon_state = "link_speech" + icon_icon = 'icons/mob/actions/actions_slime.dmi' + background_icon_state = "bg_ecult" + var/mob/living/simple_animal/hostile/eldritch/raw_prophet/originator + +/datum/action/innate/mansus_speech/New(_originator) + . = ..() + originator = _originator + +/datum/action/innate/mansus_speech/Activate() + var/mob/living/living_owner = owner + if(!originator?.linked_mobs[living_owner]) + CRASH("Uh oh the mansus link got somehow activated without it being linked to a raw prophet or the mob not being in a list of mobs that should be able to do it.") + + var/message = sanitize(input("Message:", "Telepathy from the Manse") as text|null) + + if(QDELETED(living_owner)) + return + + if(!originator?.linked_mobs[living_owner]) + to_chat(living_owner, "The link seems to have been severed...") + Remove(living_owner) + return + if(message) + var/msg = "\[Mansus Link\] [living_owner]: [message]" + log_directed_talk(living_owner, originator, msg, LOG_SAY, "Mansus Link") + to_chat(originator.linked_mobs, msg) + + for(var/dead_mob in GLOB.dead_mob_list) + var/link = FOLLOW_LINK(dead_mob, living_owner) + to_chat(dead_mob, "[link] [msg]") + +/obj/effect/proc_holder/spell/pointed/trigger/blind/eldritch + range = 10 + invocation = "AK'LIS" + action_background_icon_state = "bg_ecult" + +/obj/effect/temp_visual/dir_setting/entropic + icon = 'icons/effects/160x160.dmi' + icon_state = "entropic_plume" + duration = 3 SECONDS + +/obj/effect/temp_visual/dir_setting/entropic/setDir(dir) + . = ..() + switch(dir) + if(NORTH) + pixel_x = -64 + if(SOUTH) + pixel_x = -64 + pixel_y = -128 + if(EAST) + pixel_y = -64 + if(WEST) + pixel_y = -64 + pixel_x = -128 + +/obj/effect/temp_visual/glowing_rune + icon = 'icons/effects/eldritch.dmi' + icon_state = "small_rune_1" + duration = 1 MINUTES + layer = LOW_SIGIL_LAYER + +/obj/effect/temp_visual/glowing_rune/Initialize() + . = ..() + pixel_y = rand(-6,6) + pixel_x = rand(-6,6) + icon_state = "small_rune_[rand(12)]" + update_icon() + +/obj/effect/proc_holder/spell/cone/staggered/entropic_plume + name = "Entropic Plume" + desc = "Spews forth a disorienting plume that causes enemies to strike each other, briefly blinds them(increasing with range) and poisons them(decreasing with range). Also spreads rust in the path of the plume." + school = "illusion" + invocation = "RU'KAS NU'DYTI" + invocation_type = "whisper" + clothes_req = FALSE + action_background_icon_state = "bg_ecult" + action_icon = 'icons/mob/actions/actions_ecult.dmi' + action_icon_state = "entropic_plume" + charge_max = 300 + cone_levels = 5 + respect_density = TRUE + +/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/cast(list/targets,mob/user = usr) + . = ..() + new /obj/effect/temp_visual/dir_setting/entropic(get_step(user,user.dir), user.dir) + +/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_turf_cone_effect(turf/target_turf, level) + . = ..() + target_turf.rust_heretic_act() + +/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/do_mob_cone_effect(mob/living/victim, level) + . = ..() + if(victim.anti_magic_check() || IS_HERETIC(victim) || victim.mind?.has_antag_datum(/datum/antagonist/heretic_monster)) + return + victim.apply_status_effect(STATUS_EFFECT_AMOK) + victim.apply_status_effect(STATUS_EFFECT_CLOUDSTRUCK, (level*10)) + if(iscarbon(victim)) + var/mob/living/carbon/carbon_victim = victim + carbon_victim.reagents.add_reagent(/datum/reagent/eldritch, min(1, 6-level)) + +/obj/effect/proc_holder/spell/cone/staggered/entropic_plume/calculate_cone_shape(current_level) + if(current_level == cone_levels) + return 5 + else if(current_level == cone_levels-1) + return 3 + else + return 2 diff --git a/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm new file mode 100644 index 0000000000..529128fc0a --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/eldritch_monster_antag.dm @@ -0,0 +1,43 @@ +///Tracking reasons +/datum/antagonist/heretic_monster + name = "Eldritch Horror" + roundend_category = "Heretics" + antagpanel_category = "Heretic Beast" + antag_moodlet = /datum/mood_event/heretics + job_rank = ROLE_HERETIC + antag_hud_type = ANTAG_HUD_HERETIC + antag_hud_name = "heretic_beast" + var/datum/antagonist/master + +/datum/antagonist/heretic_monster/admin_add(datum/mind/new_owner,mob/admin) + new_owner.add_antag_datum(src) + message_admins("[key_name_admin(admin)] has heresized [key_name_admin(new_owner)].") + log_admin("[key_name(admin)] has heresized [key_name(new_owner)].") + +/datum/antagonist/heretic_monster/greet() + owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/ecult_op.ogg', 100, FALSE, pressure_affected = FALSE)//subject to change + to_chat(owner, "You became an Eldritch Horror!") + +/datum/antagonist/heretic_monster/on_removal() + if(owner) + to_chat(owner, "Your master is no longer [master.owner.current.real_name]") + owner = null + return ..() + +/datum/antagonist/heretic_monster/proc/set_owner(datum/antagonist/_master) + master = _master + var/datum/objective/master_obj = new + master_obj.owner = src + master_obj.explanation_text = "Assist your master in any way you can!" + objectives += master_obj + owner.announce_objectives() + to_chat(owner, "Your master is [master.owner.current.real_name]") + return + +/datum/antagonist/heretic_monster/apply_innate_effects(mob/living/mob_override) + . = ..() + add_antag_hud(antag_hud_type, antag_hud_name, owner.current) + +/datum/antagonist/heretic_monster/remove_innate_effects(mob/living/mob_override) + . = ..() + remove_antag_hud(antag_hud_type, owner.current) diff --git a/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm new file mode 100644 index 0000000000..8efd9837a9 --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/knowledge/ash_lore.dm @@ -0,0 +1,183 @@ +/datum/eldritch_knowledge/base_ash + name = "Nightwatcher's Secret" + desc = "Inducts you into the Path of Ash. Allows you to transmute a match with an eldritch blade into an ashen blade." + gain_text = "The City guard knows their watch. If you ask them at night they may tell you about the ashy lantern." + banned_knowledge = list(/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/final/flesh_final) + next_knowledge = list(/datum/eldritch_knowledge/ashen_grasp) + required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/match) + result_atoms = list(/obj/item/melee/sickly_blade/ash) + cost = 1 + route = PATH_ASH + +/datum/eldritch_knowledge/spell/ashen_shift + name = "Ashen Shift" + gain_text = "Ash is all the same, how can one man master it all?" + desc = "A short range jaunt that will enable you to escape from danger." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash + next_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/essence,/datum/eldritch_knowledge/ashen_eyes) + route = PATH_ASH + +/datum/eldritch_knowledge/ashen_grasp + name = "Grasp of Ash" + gain_text = "Gates have opened, minds have flooded, yet I remain." + desc = "Empowers your mansus grasp to knock enemies down and throw them away." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift) + route = PATH_ASH + +/datum/eldritch_knowledge/ashen_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!iscarbon(target)) + return + + var/mob/living/carbon/C = target + var/datum/status_effect/eldritch/E = C.has_status_effect(/datum/status_effect/eldritch/rust) || C.has_status_effect(/datum/status_effect/eldritch/ash) || C.has_status_effect(/datum/status_effect/eldritch/flesh) + if(E) + . = TRUE + E.on_effect() + for(var/X in user.mind.spell_list) + if(!istype(X,/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp)) + continue + var/obj/effect/proc_holder/spell/targeted/touch/mansus_grasp/MG = X + MG.charge_counter = min(round(MG.charge_counter + MG.charge_max * 0.75),MG.charge_max) // refunds 75% of charge. + var/atom/throw_target = get_edge_target_turf(C, user.dir) + if(!C.anchored) + . = TRUE + C.throw_at(throw_target, rand(4,8), 14, user) + return + +/datum/eldritch_knowledge/ashen_eyes + name = "Ashen Eyes" + gain_text = "Piercing eyes may guide me through the mundane." + desc = "Allows you to craft thermal vision amulet by transmutating eyes with a glass shard." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/spell/ashen_shift,/datum/eldritch_knowledge/flesh_ghoul) + required_atoms = list(/obj/item/organ/eyes,/obj/item/shard) + result_atoms = list(/obj/item/clothing/neck/eldritch_amulet) + +/datum/eldritch_knowledge/ash_mark + name = "Mark of Ash" + gain_text = "Spread the famine." + desc = "Your sickly blade now applies ash mark on hit. Use your mansus grasp to proc the mark. Mark of Ash causes stamina damage, and fire loss, and spreads to a nearby carbon. Damage decreases with how many times the mark has spread." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/curse/blindness) + banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/flesh_mark) + route = PATH_ASH + +/datum/eldritch_knowledge/ash_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_status_effect(/datum/status_effect/eldritch/ash,5) + +/datum/eldritch_knowledge/curse/blindness + name = "Curse of Blindness" + gain_text = "The blind man walks through the world, unnoticed by the masses." + desc = "Curse someone with 2 minutes of complete blindness by sacrificing a pair of eyes, a screwdriver and a pool of blood, with an object that the victim has touched with their bare hands." + cost = 1 + required_atoms = list(/obj/item/organ/eyes,/obj/item/screwdriver,/obj/effect/decal/cleanable/blood) + next_knowledge = list(/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/curse/paralysis) + timer = 2 MINUTES + route = PATH_ASH + +/datum/eldritch_knowledge/curse/blindness/curse(mob/living/chosen_mob) + . = ..() + chosen_mob.become_blind(MAGIC_TRAIT) + +/datum/eldritch_knowledge/curse/blindness/uncurse(mob/living/chosen_mob) + . = ..() + chosen_mob.cure_blind(MAGIC_TRAIT) + +/datum/eldritch_knowledge/spell/flame_birth + name = "Fiery Rebirth" + gain_text = "Nightwatcher was a man of principles, and yet he arose from the chaos he vowed to protect from." + desc = "Drains nearby alive people that are engulfed in flames. It heals 10 of each damage type per person. If a person is in critical condition it finishes them off." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/targeted/fiery_rebirth + next_knowledge = list(/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/final/ash_final) + route = PATH_ASH + +/datum/eldritch_knowledge/ash_blade_upgrade + name = "Blazing Steel" + gain_text = "May the sun burn the heretics." + desc = "Your blade of choice will now add firestacks." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/spell/flame_birth) + banned_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade) + route = PATH_ASH + +/datum/eldritch_knowledge/ash_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/C = target + C.adjust_fire_stacks(1) + C.IgniteMob() + +/datum/eldritch_knowledge/curse/corrosion + name = "Curse of Corrosion" + gain_text = "Cursed land, cursed man, cursed mind." + desc = "Curse someone for 2 minutes of vomiting and major organ damage. Using a wirecutter, a spill of blood, a heart, left arm and a right arm, and an item that the victim touched with their bare hands." + cost = 1 + required_atoms = list(/obj/item/wirecutters,/obj/effect/decal/cleanable/blood,/obj/item/organ/heart,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm) + next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/spell/area_conversion) + timer = 2 MINUTES + +/datum/eldritch_knowledge/curse/corrosion/curse(mob/living/chosen_mob) + . = ..() + chosen_mob.apply_status_effect(/datum/status_effect/corrosion_curse) + +/datum/eldritch_knowledge/curse/corrosion/uncurse(mob/living/chosen_mob) + . = ..() + chosen_mob.remove_status_effect(/datum/status_effect/corrosion_curse) + +/datum/eldritch_knowledge/curse/paralysis + name = "Curse of Paralysis" + gain_text = "Corrupt their flesh, make them bleed." + desc = "Curse someone for 5 minutes of inability to walk. Using a knife, pool of blood, left leg, right leg, a hatchet and an item that the victim touched with their bare hands. " + cost = 1 + required_atoms = list(/obj/item/kitchen/knife,/obj/effect/decal/cleanable/blood,/obj/item/bodypart/l_leg,/obj/item/bodypart/r_leg,/obj/item/hatchet) + next_knowledge = list(/datum/eldritch_knowledge/curse/blindness,/datum/eldritch_knowledge/summon/raw_prophet) + timer = 5 MINUTES + +/datum/eldritch_knowledge/curse/paralysis/curse(mob/living/chosen_mob) + . = ..() + ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT) + ADD_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT) + chosen_mob.update_mobility() + +/datum/eldritch_knowledge/curse/paralysis/uncurse(mob/living/chosen_mob) + . = ..() + REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_L_LEG,MAGIC_TRAIT) + REMOVE_TRAIT(chosen_mob,TRAIT_PARALYSIS_R_LEG,MAGIC_TRAIT) + chosen_mob.update_mobility() + +/datum/eldritch_knowledge/spell/cleave + name = "Blood Cleave" + gain_text = "At first I was unfamiliar with these instruments of war, but the priest told me how to use them." + desc = "Grants a spell that will inflict wounds and bleeding upon the target, as well as in a short radius around them." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/pointed/cleave + next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume,/datum/eldritch_knowledge/spell/flame_birth) + +/datum/eldritch_knowledge/final/ash_final + name = "Ashlord's Rite" + gain_text = "The forgotten lords have spoken! The Lord of Ash has come! Fear the flame!" + desc = "Bring three corpses onto a transmutation rune, after ascending you will become immune to fire, space, temperature and other environmental hazards. You will develop resistance to all other damages. You will be granted two spells, one which can bring forth a cascade of massive fire, and another which will surround your body in precious flames for a minute." + required_atoms = list(/mob/living/carbon/human) + cost = 5 + route = PATH_ASH + var/list/trait_list = list(TRAIT_RESISTHEAT,TRAIT_NOBREATH,TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOFIRE,TRAIT_RADIMMUNE,TRAIT_GENELESS,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER,TRAIT_BOMBIMMUNE) + +/datum/eldritch_knowledge/final/ash_final/on_finished_recipe(mob/living/user, list/atoms, loc) + priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the blaze, for Ashbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') + user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/fire_cascade/big) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/fire_sworn) + var/mob/living/carbon/human/H = user + H.physiology.brute_mod *= 0.5 + H.physiology.burn_mod *= 0.5 + var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic) + ascension.ascended = TRUE + for(var/X in trait_list) + ADD_TRAIT(user,X,MAGIC_TRAIT) + return ..() diff --git a/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm new file mode 100644 index 0000000000..9684e1fa0c --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/knowledge/flesh_lore.dm @@ -0,0 +1,252 @@ +/datum/eldritch_knowledge/base_flesh + name = "Principle of Hunger" + desc = "Inducts you into the Path of Flesh. Allows you to transmute a pool of blood with your eldritch blade into a Blade of Flesh." + gain_text = "Hundred's of us starved, but I.. I found the strength in my greed." + banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_rust,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/rust_final) + next_knowledge = list(/datum/eldritch_knowledge/flesh_grasp) + required_atoms = list(/obj/item/melee/sickly_blade,/obj/effect/decal/cleanable/blood) + result_atoms = list(/obj/item/melee/sickly_blade/flesh) + cost = 1 + route = PATH_FLESH + +/datum/eldritch_knowledge/flesh_ghoul + name = "Imperfect Ritual" + desc = "Allows you to resurrect the dead as voiceless dead by sacrificing them on the transmutation rune with a poppy. Voiceless dead are mute and have 50 HP. You can only have 2 at a time." + gain_text = "I found notes... notes of a ritual, scraps, unfinished, and yet... I still did it." + cost = 1 + required_atoms = list(/mob/living/carbon/human,/obj/item/reagent_containers/food/snacks/grown/poppy) + next_knowledge = list(/datum/eldritch_knowledge/flesh_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/ashen_eyes) + route = PATH_FLESH + var/max_amt = 2 + var/current_amt = 0 + var/list/ghouls = list() + +/datum/eldritch_knowledge/flesh_ghoul/on_finished_recipe(mob/living/user,list/atoms,loc) + var/mob/living/carbon/human/humie = locate() in atoms + if(QDELETED(humie) || humie.stat != DEAD) + return + + if(length(ghouls) >= max_amt) + return + + if(HAS_TRAIT(humie,TRAIT_HUSK)) + return + + humie.grab_ghost() + + if(!humie.mind || !humie.client) + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [humie.real_name], a voiceless dead.", ROLE_HERETIC, null, ROLE_HERETIC, 50,humie) + if(!LAZYLEN(candidates)) + return + var/mob/dead/observer/C = pick(candidates) + message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(humie)]) to replace an AFK player.") + humie.ghostize(0) + humie.key = C.key + + ADD_TRAIT(humie,TRAIT_MUTE,MAGIC_TRAIT) + log_game("[key_name_admin(humie)] has become a voiceless dead, their master is [user.real_name]") + humie.revive(full_heal = TRUE, admin_revive = TRUE) + humie.setMaxHealth(75) + humie.health = 75 // Voiceless dead are much tougher than ghouls + humie.become_husk() + humie.faction |= "heretics" + + var/datum/antagonist/heretic_monster/heretic_monster = humie.mind.add_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic) + heretic_monster.set_owner(master) + atoms -= humie + RegisterSignal(humie,COMSIG_MOB_DEATH,.proc/remove_ghoul) + ghouls += humie + +/datum/eldritch_knowledge/flesh_ghoul/proc/remove_ghoul(datum/source) + var/mob/living/carbon/human/humie = source + ghouls -= humie + humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster) + UnregisterSignal(source,COMSIG_MOB_DEATH) + +/datum/eldritch_knowledge/flesh_grasp + name = "Grasp of Flesh" + gain_text = "'My newfound desire, it drove me to do great things,' The Priest said." + desc = "Empowers your Mansus Grasp to be able to create a single ghoul out of a dead player. You cannot raise the same person twice. Ghouls have only 50 HP and look like husks." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/flesh_ghoul) + var/ghoul_amt = 6 + var/list/spooky_scaries + route = PATH_FLESH + +/datum/eldritch_knowledge/flesh_grasp/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!ishuman(target) || target == user) + return + var/mob/living/carbon/human/human_target = target + var/datum/status_effect/eldritch/eldritch_effect = human_target.has_status_effect(/datum/status_effect/eldritch/rust) || human_target.has_status_effect(/datum/status_effect/eldritch/ash) || human_target.has_status_effect(/datum/status_effect/eldritch/flesh) + if(eldritch_effect) + . = TRUE + eldritch_effect.on_effect() + if(iscarbon(target)) + var/mob/living/carbon/carbon_target = target + var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts) + var/datum/wound/slash/severe/crit_wound = new + crit_wound.apply_wound(bodypart) + + if(QDELETED(human_target) || human_target.stat != DEAD) + return + + human_target.grab_ghost() + + if(!human_target.mind || !human_target.client) + to_chat(user, "There is no soul connected to this body...") + return + + if(HAS_TRAIT(human_target, TRAIT_HUSK)) + to_chat(user, "You cannot revive a dead ghoul!") + return + + if(LAZYLEN(spooky_scaries) >= ghoul_amt) + to_chat(user, "Your patron cannot support more ghouls on this plane!") + return + + LAZYADD(spooky_scaries, human_target) + log_game("[key_name_admin(human_target)] has become a ghoul, their master is [user.real_name]") + //we change it to true only after we know they passed all the checks + . = TRUE + RegisterSignal(human_target,COMSIG_MOB_DEATH,.proc/remove_ghoul) + human_target.revive(full_heal = TRUE, admin_revive = TRUE) + human_target.setMaxHealth(40) + human_target.health = 40 + human_target.become_husk() + human_target.faction |= "heretics" + var/datum/antagonist/heretic_monster/heretic_monster = human_target.mind.add_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic) + heretic_monster.set_owner(master) + return + + +/datum/eldritch_knowledge/flesh_grasp/proc/remove_ghoul(datum/source) + var/mob/living/carbon/human/humie = source + spooky_scaries -= humie + humie.mind.remove_antag_datum(/datum/antagonist/heretic_monster) + UnregisterSignal(source, COMSIG_MOB_DEATH) + +/datum/eldritch_knowledge/flesh_mark + name = "Mark of Flesh" + gain_text = "I saw them, the marked ones. The screams... the silence." + desc = "Your sickly blade now applies a mark of flesh to those cut by it. Once marked, using your Mansus Grasp upon them will cause additional bleeding from the target." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet) + banned_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/ash_mark) + route = PATH_FLESH + +/datum/eldritch_knowledge/flesh_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_status_effect(/datum/status_effect/eldritch/flesh) + +/datum/eldritch_knowledge/flesh_blade_upgrade + name = "Bleeding Steel" + gain_text = "It rained blood, that's when I understood the gravekeeper's advice." + desc = "Your blade will now cause additional bleeding to those hit by it." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/summon/stalker) + banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/rust_blade_upgrade) + route = PATH_FLESH + +/datum/eldritch_knowledge/flesh_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/carbon_target = target + var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts) + var/datum/wound/slash/severe/crit_wound = new + crit_wound.apply_wound(bodypart) + +/datum/eldritch_knowledge/summon/raw_prophet + name = "Raw Ritual" + gain_text = "The uncanny man walks alone in the valley, I was able to call his aid." + desc = "You can now summon a Raw Prophet using eyes, a left arm, right arm and a pool of blood using a transmutation circle. Raw prophets have increased seeing range, and can see through walls. They can jaunt long distances, though they are fragile." + cost = 1 + required_atoms = list(/obj/item/organ/eyes,/obj/item/bodypart/l_arm,/obj/item/bodypart/r_arm,/obj/effect/decal/cleanable/blood) + mob_to_summon = /mob/living/simple_animal/hostile/eldritch/raw_prophet + next_knowledge = list(/datum/eldritch_knowledge/flesh_blade_upgrade,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/curse/paralysis) + route = PATH_FLESH + +/datum/eldritch_knowledge/summon/stalker + name = "Lonely Ritual" + gain_text = "I was able to combine my greed and desires to summon an eldritch beast I have not seen before." + desc = "You can now summon a Stalker using a knife, a flower, a pen and a piece of paper using a transmutation circle. Stalkers possess the ability to shapeshift into various forms while assuming the vigor and powers of that form." + cost = 1 + required_atoms = list(/obj/item/kitchen/knife,/obj/item/reagent_containers/food/snacks/grown/poppy,/obj/item/pen,/obj/item/paper) + mob_to_summon = /mob/living/simple_animal/hostile/eldritch/stalker + next_knowledge = list(/datum/eldritch_knowledge/summon/ashy,/datum/eldritch_knowledge/summon/rusty,/datum/eldritch_knowledge/final/flesh_final) + route = PATH_FLESH + +/datum/eldritch_knowledge/summon/ashy + name = "Ashen Ritual" + gain_text = "I combined principle of hunger with desire of destruction. The eyeful lords have noticed me." + desc = "You can now summon an Ashen One by transmuting a pile of ash, a head and a book using a transmutation circle. They possess the ability to jaunt short distances and create a cascade of flames." + cost = 1 + required_atoms = list(/obj/effect/decal/cleanable/ash,/obj/item/bodypart/head,/obj/item/book) + mob_to_summon = /mob/living/simple_animal/hostile/eldritch/ash_spirit + next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/flame_birth) + +/datum/eldritch_knowledge/summon/rusty + name = "Rusted Ritual" + gain_text = "I combined principle of hunger with desire of corruption. The rusted hills call my name." + desc = "You can now summon a Rust Walker transmuting a vomit pool, a head, and a book using a transmutation circle. Rust Walkers possess the ability to spread rust and can fire bolts of rust to further corrode the area." + cost = 1 + required_atoms = list(/obj/effect/decal/cleanable/vomit,/obj/item/bodypart/head,/obj/item/book) + mob_to_summon = /mob/living/simple_animal/hostile/eldritch/rust_spirit + next_knowledge = list(/datum/eldritch_knowledge/summon/stalker,/datum/eldritch_knowledge/spell/entropic_plume) + +/datum/eldritch_knowledge/spell/blood_siphon + name = "Blood Siphon" + gain_text = "Our blood is all the same after all, the owl told me." + desc = "You are granted a spell that drains some of the targets health, and returns it to you. It also has a chance to transfer any wounds you possess onto the target." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/targeted/touch/blood_siphon + next_knowledge = list(/datum/eldritch_knowledge/summon/raw_prophet,/datum/eldritch_knowledge/spell/area_conversion) + +/datum/eldritch_knowledge/final/flesh_final + name = "Priest's Final Hymn" + gain_text = "Man of this world. Hear me! For the time of the lord of arms has come!" + desc = "Bring three corpses to a transmutation rune to either ascend as The Lord of the Night or summon a single Terror of the Night, however you cannot ascend more than once." + required_atoms = list(/mob/living/carbon/human) + cost = 5 + route = PATH_FLESH + +/datum/eldritch_knowledge/final/flesh_final/on_finished_recipe(mob/living/user, list/atoms, loc) + var/alert_ = alert(user,"Do you want to ascend as the lord of the night or just summon a terror of the night?","...","Yes","No") + user.SetImmobilized(10 HOURS) // no way someone will stand 10 hours in a spot, just so he can move while the alert is still showing. + switch(alert_) + if("No") + var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy(loc) + message_admins("[summoned.name] is being summoned by [user.real_name] in [loc]") + var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [summoned.real_name]", ROLE_HERETIC, null, ROLE_HERETIC, 100,summoned) + user.SetImmobilized(0) + if(LAZYLEN(candidates) == 0) + to_chat(user,"No ghost could be found...") + qdel(summoned) + return FALSE + var/mob/dead/observer/ghost_candidate = pick(candidates) + priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for vassal of arms has ascended! Terror of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') + log_game("[key_name_admin(ghost_candidate)] has taken control of ([key_name_admin(summoned)]).") + summoned.ghostize(FALSE) + summoned.key = ghost_candidate.key + summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic_monster/monster = summoned.mind.has_antag_datum(/datum/antagonist/heretic_monster) + var/datum/antagonist/heretic/master = user.mind.has_antag_datum(/datum/antagonist/heretic) + monster.set_owner(master) + master.ascended = TRUE + if("Yes") + var/mob/living/summoned = new /mob/living/simple_animal/hostile/eldritch/armsy/prime(loc,TRUE,10) + summoned.ghostize(0) + user.SetImmobilized(0) + priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the dark, for king of arms has ascended! Lord of the night has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') + log_game("[user.real_name] ascended as [summoned.real_name]") + var/mob/living/carbon/carbon_user = user + var/datum/antagonist/heretic/ascension = carbon_user.mind.has_antag_datum(/datum/antagonist/heretic) + ascension.ascended = TRUE + carbon_user.mind.transfer_to(summoned, TRUE) + carbon_user.gib() + + return ..() diff --git a/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm new file mode 100644 index 0000000000..74b3753b69 --- /dev/null +++ b/code/modules/antagonists/eldritch_cult/knowledge/rust_lore.dm @@ -0,0 +1,209 @@ +/datum/eldritch_knowledge/base_rust + name = "Blacksmith's Tale" + desc = "Inducts you into the Path of Rust. Allows you to transmute an eldritch blade with any trash item into a Blade of Rust." + gain_text = "'Let me tell you a story,' The Blacksmith said as he gazed into his rusty blade." + banned_knowledge = list(/datum/eldritch_knowledge/base_ash,/datum/eldritch_knowledge/base_flesh,/datum/eldritch_knowledge/final/ash_final,/datum/eldritch_knowledge/final/flesh_final) + next_knowledge = list(/datum/eldritch_knowledge/rust_fist) + required_atoms = list(/obj/item/melee/sickly_blade,/obj/item/trash) + result_atoms = list(/obj/item/melee/sickly_blade/rust) + cost = 1 + route = PATH_RUST + +/datum/eldritch_knowledge/rust_fist + name = "Grasp of Rust" + desc = "Empowers your Mansus Grasp to deal 500 damage to non-living matter and rust any structure it touches. Destroys already rusted structures." + gain_text = "Rust grows on the ceiling of the mansus." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/rust_regen) + var/rust_force = 500 + var/static/list/blacklisted_turfs = typecacheof(list(/turf/closed,/turf/open/space,/turf/open/lava,/turf/open/chasm,/turf/open/floor/plating/rust)) + route = PATH_RUST + +/datum/eldritch_knowledge/rust_fist/on_mansus_grasp(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/datum/status_effect/eldritch/E = H.has_status_effect(/datum/status_effect/eldritch/rust) || H.has_status_effect(/datum/status_effect/eldritch/ash) || H.has_status_effect(/datum/status_effect/eldritch/flesh) + if(E) + E.on_effect() + H.adjustOrganLoss(pick(ORGAN_SLOT_BRAIN,ORGAN_SLOT_EARS,ORGAN_SLOT_EYES,ORGAN_SLOT_LIVER,ORGAN_SLOT_LUNGS,ORGAN_SLOT_STOMACH,ORGAN_SLOT_HEART),25) + target.rust_heretic_act() + target.emp_act(EMP_HEAVY) + return TRUE + +/datum/eldritch_knowledge/spell/area_conversion + name = "Aggressive Spread" + desc = "Spreads rust to nearby turfs. Destroys already rusted walls." + gain_text = "All wise men know not to touch the bound king." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/aoe_turf/rust_conversion + next_knowledge = list(/datum/eldritch_knowledge/rust_blade_upgrade,/datum/eldritch_knowledge/curse/corrosion,/datum/eldritch_knowledge/spell/blood_siphon,/datum/eldritch_knowledge/spell/rust_wave) + route = PATH_RUST + +/datum/eldritch_knowledge/spell/rust_wave + name = "Patron's Reach" + desc = "You can now send a bolt of rust that corrupts the immediate area, and poisons the first target hit." + gain_text = "Messengers of hope fear the rustbringer." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/aimed/rust_wave + route = PATH_RUST + +/datum/eldritch_knowledge/rust_regen + name = "Leeching Walk" + desc = "Passively heals you when you are on rusted tiles." + gain_text = "'The strength was unparalleled, unnatural.' The Blacksmith was smiling." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/rust_mark,/datum/eldritch_knowledge/armor,/datum/eldritch_knowledge/essence) + route = PATH_RUST + +/datum/eldritch_knowledge/rust_regen/on_life(mob/user) + . = ..() + var/turf/user_loc_turf = get_turf(user) + if(!istype(user_loc_turf, /turf/open/floor/plating/rust) || !isliving(user)) + return + var/mob/living/living_user = user + living_user.adjustBruteLoss(-3, FALSE) + living_user.adjustFireLoss(-3, FALSE) + living_user.adjustToxLoss(-3, FALSE) + living_user.adjustOxyLoss(-1, FALSE) + living_user.adjustStaminaLoss(-6) + +/datum/eldritch_knowledge/rust_mark + name = "Mark of Rust" + desc = "Your eldritch blade now applies a rust mark. Rust marks have a chance to deal between 0 to 200 damage to 75% of enemies items. To activate the mark use your Mansus Grasp on it." + gain_text = "Lords of the depths help those in dire need at a cost." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/spell/area_conversion) + banned_knowledge = list(/datum/eldritch_knowledge/ash_mark,/datum/eldritch_knowledge/flesh_mark) + route = PATH_RUST + +/datum/eldritch_knowledge/rust_mark/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(isliving(target)) + var/mob/living/living_target = target + living_target.apply_status_effect(/datum/status_effect/eldritch/rust) + +/datum/eldritch_knowledge/rust_blade_upgrade + name = "Toxic Steel" + gain_text = "Let the blade guide you through the flesh." + desc = "Your blade of choice will now add toxin to enemies bloodstream." + cost = 2 + next_knowledge = list(/datum/eldritch_knowledge/spell/entropic_plume) + banned_knowledge = list(/datum/eldritch_knowledge/ash_blade_upgrade,/datum/eldritch_knowledge/flesh_blade_upgrade) + route = PATH_RUST + +/datum/eldritch_knowledge/rust_blade_upgrade/on_eldritch_blade(target,user,proximity_flag,click_parameters) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/carbon_target = target + carbon_target.reagents.add_reagent(/datum/reagent/eldritch, 5) + +/datum/eldritch_knowledge/spell/entropic_plume + name = "Entropic Plume" + desc = "You can now send a befuddling plume that blinds, poisons and makes enemies strike each other, while also converting the immediate area into rust." + gain_text = "Messengers of hope fear the rustbringer." + cost = 1 + spell_to_add = /obj/effect/proc_holder/spell/cone/staggered/entropic_plume + next_knowledge = list(/datum/eldritch_knowledge/final/rust_final,/datum/eldritch_knowledge/spell/cleave,/datum/eldritch_knowledge/summon/rusty) + route = PATH_RUST + +/datum/eldritch_knowledge/armor + name = "Armorer's Ritual" + desc = "You can now create eldritch armor using a built table and a gas mask on top of a transmutation rune." + gain_text = "For I am the heir to the throne of doom." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/flesh_ghoul) + required_atoms = list(/obj/structure/table,/obj/item/clothing/mask/gas) + result_atoms = list(/obj/item/clothing/suit/hooded/cultrobes/eldritch) + +/datum/eldritch_knowledge/essence + name = "Priest's Ritual" + desc = "You can now transmute a tank of water into a bottle of eldritch fluid." + gain_text = "This is an old recipe, i got it from an owl." + cost = 1 + next_knowledge = list(/datum/eldritch_knowledge/rust_regen,/datum/eldritch_knowledge/spell/ashen_shift) + required_atoms = list(/obj/structure/reagent_dispensers/watertank) + result_atoms = list(/obj/item/reagent_containers/glass/beaker/eldritch) + +/datum/eldritch_knowledge/final/rust_final + name = "Rustbringer's Oath" + desc = "Bring three corpses onto a transmutation rune. After you finish the ritual, rust will now automatically spread from the rune. Your healing on rust is also tripled, while you become more resilient overall." + gain_text = "Champion of rust. Corruptor of steel. Fear the dark for Rustbringer has come!" + cost = 5 + required_atoms = list(/mob/living/carbon/human) + route = PATH_RUST + +/datum/eldritch_knowledge/final/rust_final/on_finished_recipe(mob/living/user, list/atoms, loc) + var/mob/living/carbon/human/H = user + H.physiology.brute_mod *= 0.5 + H.physiology.burn_mod *= 0.5 + priority_announce("$^@&#*$^@(#&$(@&#^$&#^@# Fear the decay, for Rustbringer [user.real_name] has come! $^@&#*$^@(#&$(@&#^$&#^@#","#$^@&#*$^@(#&$(@&#^$&#^@#", 'sound/announcer/classic/spanomalies.ogg') + new /datum/rust_spread(loc) + var/datum/antagonist/heretic/ascension = H.mind.has_antag_datum(/datum/antagonist/heretic) + ascension.ascended = TRUE + return ..() + + +/datum/eldritch_knowledge/final/rust_final/on_life(mob/user) + . = ..() + if(!finished) + return + var/mob/living/carbon/human/human_user = user + human_user.adjustBruteLoss(-6, FALSE) + human_user.adjustFireLoss(-6, FALSE) + human_user.adjustToxLoss(-6, FALSE) + human_user.adjustOxyLoss(-6, FALSE) + human_user.adjustStaminaLoss(-20) + + +/** + * #Rust spread datum + * + * Simple datum that automatically spreads rust around it + * + * Simple implementation of automatically growing entity + */ +/datum/rust_spread + var/list/edge_turfs = list() + var/list/turfs = list() + var/static/list/blacklisted_turfs = typecacheof(list(/turf/open/indestructible,/turf/closed/indestructible,/turf/open/space,/turf/open/lava,/turf/open/chasm)) + var/spread_per_tick = 6 + + +/datum/rust_spread/New(loc) + . = ..() + var/turf/turf_loc = get_turf(loc) + turf_loc.rust_heretic_act() + turfs += turf_loc + START_PROCESSING(SSprocessing,src) + + +/datum/rust_spread/Destroy(force, ...) + STOP_PROCESSING(SSprocessing,src) + return ..() + +/datum/rust_spread/process() + compile_turfs() + var/turf/T + for(var/i in 0 to spread_per_tick) + T = pick(edge_turfs) + T.rust_heretic_act() + turfs += get_turf(T) + +/** + * Compile turfs + * + * Recreates all edge_turfs as well as normal turfs. + */ +/datum/rust_spread/proc/compile_turfs() + edge_turfs = list() + for(var/X in turfs) + if(!istype(X,/turf/closed/wall/rust) && !istype(X,/turf/closed/wall/r_wall/rust) && !istype(X,/turf/open/floor/plating/rust)) + turfs -=X + continue + for(var/turf/T in range(1,X)) + if(T in turfs) + continue + if(is_type_in_typecache(T,blacklisted_turfs)) + continue + edge_turfs += T diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm index 21ccd425a3..848fe124be 100644 --- a/code/modules/antagonists/revenant/revenant.dm +++ b/code/modules/antagonists/revenant/revenant.dm @@ -28,6 +28,7 @@ throwforce = 0 blood_volume = 0 has_field_of_vision = FALSE //we are a spoopy ghost + rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS see_in_dark = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE diff --git a/code/modules/antagonists/slaughter/slaughter.dm b/code/modules/antagonists/slaughter/slaughter.dm index 3ae9cfcfb0..d1db363b04 100644 --- a/code/modules/antagonists/slaughter/slaughter.dm +++ b/code/modules/antagonists/slaughter/slaughter.dm @@ -16,6 +16,7 @@ icon_state = "daemon" icon_living = "daemon" mob_biotypes = MOB_ORGANIC|MOB_HUMANOID + mob_size = MOB_SIZE_LARGE speed = 1 a_intent = INTENT_HARM stop_automated_movement = 1 diff --git a/code/modules/antagonists/wizard/equipment/spellbook.dm b/code/modules/antagonists/wizard/equipment/spellbook.dm index d4d117f8ee..1e98b2f753 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook.dm @@ -161,12 +161,12 @@ /datum/spellbook_entry/blind name = "Blind" - spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind + spell_type = /obj/effect/proc_holder/spell/pointed/trigger/blind cost = 1 /datum/spellbook_entry/mindswap name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + spell_type = /obj/effect/proc_holder/spell/pointed/mind_transfer category = "Mobility" /datum/spellbook_entry/forcewall @@ -246,7 +246,7 @@ /datum/spellbook_entry/barnyard name = "Barnyard Curse" - spell_type = /obj/effect/proc_holder/spell/targeted/barnyardcurse + spell_type = /obj/effect/proc_holder/spell/pointed/barnyardcurse /datum/spellbook_entry/charge name = "Charge" diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm index 7263793f7f..42954c3542 100644 --- a/code/modules/antagonists/wizard/wizard.dm +++ b/code/modules/antagonists/wizard/wizard.dm @@ -177,7 +177,7 @@ to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.") if(APPRENTICE_ROBELESS) owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/mind_transfer(null)) + owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null)) to_chat(owner, "Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned stealthy, robeless spells. You are able to cast knock and mindswap.") if(APPRENTICE_MARTIAL) owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/touch/nuclear_fist(null)) diff --git a/code/modules/arousal/genitals.dm b/code/modules/arousal/genitals.dm index 4d2e5e6fef..8f88076af9 100644 --- a/code/modules/arousal/genitals.dm +++ b/code/modules/arousal/genitals.dm @@ -232,23 +232,6 @@ /obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H) return - -//procs to handle sprite overlays being applied to humans - -/mob/living/carbon/human/equip_to_slot(obj/item/I, slot) - . = ..() - if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs. - update_genitals() - -/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE) - var/no_update = FALSE - if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it. - no_update = TRUE - . = ..() - if(!. || no_update) - return - update_genitals() - /mob/living/carbon/human/proc/update_genitals() if(QDELETED(src)) return diff --git a/code/modules/arousal/toys/dildos.dm b/code/modules/arousal/toys/dildos.dm index 5cb6d47118..3f6fa9bb45 100644 --- a/code/modules/arousal/toys/dildos.dm +++ b/code/modules/arousal/toys/dildos.dm @@ -122,6 +122,7 @@ obj/item/dildo/flared/huge name = "literal horse cock" desc = "THIS THING IS HUGE!" dildo_size = 4 + force = 10 obj/item/dildo/custom name = "customizable dildo" diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index d4ab7b5648..a8b4c93764 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -388,3 +388,9 @@ Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal") ..() +/datum/asset/spritesheet/mafia + name = "mafia" + +/datum/asset/spritesheet/mafia/register() + InsertAll("", 'icons/obj/mafia.dmi') + ..() diff --git a/code/modules/cargo/exports/gear.dm b/code/modules/cargo/exports/gear.dm index 646e1c6e47..678948128f 100644 --- a/code/modules/cargo/exports/gear.dm +++ b/code/modules/cargo/exports/gear.dm @@ -310,7 +310,7 @@ /datum/export/gear/combatgloves cost = 80 unit_name = "combat gloves" - export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/fingerless/pugilist/rapid, /obj/item/clothing/gloves/krav_maga) + export_types = list(/obj/item/clothing/gloves/tackler/combat, /obj/item/clothing/gloves/tackler/dolphin, /obj/item/clothing/gloves/krav_maga) include_subtypes = TRUE /datum/export/gear/bonegloves diff --git a/code/modules/cargo/exports/organs_robotics.dm b/code/modules/cargo/exports/organs_robotics.dm index 7f54e675ca..b65cf28949 100644 --- a/code/modules/cargo/exports/organs_robotics.dm +++ b/code/modules/cargo/exports/organs_robotics.dm @@ -2,11 +2,11 @@ /datum/export/robotics include_subtypes = FALSE - k_elasticity = 1/50 + k_elasticity = 1/200 /datum/export/implant include_subtypes = FALSE - k_elasticity = 1/50 + k_elasticity = 1/200 /datum/export/organs include_subtypes = TRUE @@ -34,8 +34,8 @@ export_types = list(/obj/item/organ/cyberimp/brain/anti_stun) /datum/export/implant/breathtube - cost = 150 - k_elasticity = 300/20 //Large before depleating + cost = 175 + k_elasticity = 1/350 //Large before depleating unit_name = "breath implant" export_types = list(/obj/item/organ/cyberimp/mouth/breathing_tube) @@ -71,35 +71,35 @@ export_types = list(/obj/item/organ/cyberimp/arm/gun/laser, /obj/item/organ/cyberimp/arm/gun/taser, /obj/item/organ/cyberimp/arm/esword, /obj/item/organ/cyberimp/arm/medibeam, /obj/item/organ/cyberimp/arm/combat, /obj/item/organ/cyberimp/arm/flash, /obj/item/organ/cyberimp/arm/baton) include_subtypes = TRUE -/datum/export/orgains/heart +/datum/export/organs/heart cost = 250 unit_name = "heart" export_types = list(/obj/item/organ/heart) exclude_types = list(/obj/item/organ/heart/cursed, /obj/item/organ/heart/cybernetic) -/datum/export/orgains/tongue +/datum/export/organs/tongue cost = 75 unit_name = "tongue" export_types = list(/obj/item/organ/tongue) -/datum/export/orgains/eyes +/datum/export/organs/eyes cost = 50 //So many things take your eyes out anyways unit_name = "eyes" export_types = list(/obj/item/organ/eyes) exclude_types = list(/obj/item/organ/eyes/robotic) -/datum/export/orgains/stomach +/datum/export/organs/stomach cost = 50 //can be replaced unit_name = "stomach" export_types = list(/obj/item/organ/stomach) -/datum/export/orgains/lungs +/datum/export/organs/lungs cost = 150 unit_name = "lungs" export_types = list(/obj/item/organ/lungs) exclude_types = list(/obj/item/organ/lungs/cybernetic, /obj/item/organ/lungs/cybernetic/upgraded) -/datum/export/orgains/liver +/datum/export/organs/liver cost = 175 unit_name = "liver" export_types = list(/obj/item/organ/liver) @@ -116,33 +116,33 @@ unit_name = "upgraded cybernetic organ" export_types = list(/obj/item/organ/lungs/cybernetic/upgraded, /obj/item/organ/liver/cybernetic/upgraded) -/datum/export/organs/tail //Shhh +/datum/export/organs/tail // yeah have fun pulling this off someone without catching a bwoink cost = 500 - unit_name = "error shipment failer" + unit_name = "organic tail" export_types = list(/obj/item/organ/tail) -/datum/export/orgains/vocal_cords +/datum/export/organs/vocal_cords cost = 500 unit_name = "vocal cords" export_types = list(/obj/item/organ/vocal_cords) //These are gotten via different races -/datum/export/robotics/lims - cost = 30 - unit_name = "robotic lim replacement" +/datum/export/robotics/limbs + cost = 60 + unit_name = "robotic limb replacement" export_types = list(/obj/item/bodypart/l_arm/robot, /obj/item/bodypart/r_arm/robot, /obj/item/bodypart/l_leg/robot, /obj/item/bodypart/r_leg/robot, /obj/item/bodypart/chest/robot, /obj/item/bodypart/head/robot) /datum/export/robotics/surpluse - cost = 40 - unit_name = "robotic lim replacement" + cost = 50 + unit_name = "robotic limb replacement" export_types = list(/obj/item/bodypart/l_arm/robot/surplus, /obj/item/bodypart/r_arm/robot/surplus, /obj/item/bodypart/l_leg/robot/surplus, /obj/item/bodypart/r_leg/robot/surplus) /datum/export/robotics/surplus_upgraded - cost = 50 - unit_name = "upgraded robotic lim replacement" + cost = 80 + unit_name = "upgraded robotic limb replacement" export_types = list(/obj/item/bodypart/l_arm/robot/surplus_upgraded, /obj/item/bodypart/r_arm/robot/surplus_upgraded, /obj/item/bodypart/l_leg/robot/surplus_upgraded, /obj/item/bodypart/r_leg/robot/surplus_upgraded) /datum/export/robotics/surgery_gear_basic - cost = 10 + cost = 50 unit_name = "surgery tool" export_types = list(/obj/item/retractor, /obj/item/hemostat, /obj/item/cautery, /obj/item/surgicaldrill, /obj/item/scalpel, /obj/item/circular_saw, /obj/item/bonesetter, /obj/item/surgical_drapes) diff --git a/code/modules/cargo/exports/parts.dm b/code/modules/cargo/exports/parts.dm index da3c0cf31d..3e52780d44 100644 --- a/code/modules/cargo/exports/parts.dm +++ b/code/modules/cargo/exports/parts.dm @@ -102,6 +102,7 @@ export_types = list(/obj/item/stock_parts/cell/high/slime/hypercharged) //Glass working stuff +// i'd just like to say how i despise the previous coder's fetish for their funny glasswork /datum/export/glasswork_dish cost = 300 diff --git a/code/modules/cargo/exports/sheets.dm b/code/modules/cargo/exports/sheets.dm index a562210164..b0676fbde2 100644 --- a/code/modules/cargo/exports/sheets.dm +++ b/code/modules/cargo/exports/sheets.dm @@ -138,11 +138,11 @@ message = "of bones" export_types = list(/obj/item/stack/sheet/bone) -/datum/export/stack/bronze +/datum/export/stack/sheet/bronze unit_name = "tiles" cost = 5 message = "of brozne" - export_types = list(/obj/item/stack/tile/bronze) + export_types = list(/obj/item/stack/sheet/bronze) /datum/export/stack/brass unit_name = "tiles" diff --git a/code/modules/cargo/exports/weapons.dm b/code/modules/cargo/exports/weapons.dm index 983348a358..dc2703c146 100644 --- a/code/modules/cargo/exports/weapons.dm +++ b/code/modules/cargo/exports/weapons.dm @@ -5,7 +5,7 @@ /datum/export/weapon/makeshift_shield cost = 30 - unit_name = "unknown shield" + unit_name = "nonstandard shield" export_types = list(/obj/item/shield/riot, /obj/item/shield/riot/roman, /obj/item/shield/riot/buckler, /obj/item/shield/makeshift) /datum/export/weapon/riot_shield @@ -37,7 +37,7 @@ /datum/export/weapon/taser cost = 200 - unit_name = "advanced taser" + unit_name = "hybrid taser" export_types = list(/obj/item/gun/energy/e_gun/advtaser) /datum/export/weapon/laser @@ -104,12 +104,12 @@ /datum/export/weapon/aeg cost = 200 //Endless power - unit_name = "advance engery gun" + unit_name = "advanced energy gun" export_types = list(/obj/item/gun/energy/e_gun/nuclear) /datum/export/weapon/deconer cost = 600 - unit_name = "deconer" + unit_name = "decloner" export_types = list(/obj/item/gun/energy/decloner) /datum/export/weapon/ntsniper @@ -123,9 +123,8 @@ export_types = list(/obj/item/gun/syringe/rapidsyringe) /datum/export/weapon/temp_gun - cost = 175 //Its just smaller + cost = 175 unit_name = "small temperature gun" - k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make export_types = list(/obj/item/gun/energy/temperature) /datum/export/weapon/flowergun @@ -139,8 +138,7 @@ export_types = list(/obj/item/gun/energy/xray) /datum/export/weapon/ioncarbine - cost = 200 - k_elasticity = 1/30 //Its just a smaller temperature gun, easy to mass make + cost = 200 unit_name = "ion carbine" export_types = list(/obj/item/gun/energy/ionrifle/carbine) @@ -194,7 +192,7 @@ export_types = list(/obj/item/firing_pin/test_range) /datum/export/weapon/techslug - cost = 25 + cost = 30 k_elasticity = 0 unit_name = "advanced shotgun shell" export_types = list(/obj/item/ammo_casing/shotgun/dragonsbreath, /obj/item/ammo_casing/shotgun/meteorslug, /obj/item/ammo_casing/shotgun/pulseslug, /obj/item/ammo_casing/shotgun/frag12, /obj/item/ammo_casing/shotgun/ion, /obj/item/ammo_casing/shotgun/laserslug) @@ -215,7 +213,7 @@ /datum/export/weapon/bow_teaching cost = 500 - unit_name = "stone tablets" + unit_name = "bowyery tablet" export_types = list(/obj/item/book/granter/crafting_recipe/bone_bow) /datum/export/weapon/quiver @@ -230,48 +228,48 @@ /datum/export/weapon/pistol cost = 120 - unit_name = "illegal firearm" + unit_name = "nonstandard sidearm" export_types = list(/obj/item/gun/ballistic/automatic/pistol) /datum/export/weapon/revolver cost = 200 - unit_name = "large handgun" + unit_name = "large-caliber revolver" export_types = list(/obj/item/gun/ballistic/revolver) exclude_types = list(/obj/item/gun/ballistic/revolver/russian, /obj/item/gun/ballistic/revolver/doublebarrel) /datum/export/weapon/rocketlauncher cost = 1000 - unit_name = "rocketlauncher" + unit_name = "PML-9 rocket-propelled grenade launcher" export_types = list(/obj/item/gun/ballistic/rocketlauncher) /datum/export/weapon/antitank cost = 300 - unit_name = "hand cannon" + unit_name = "anti-tank pistol" export_types = list(/obj/item/gun/ballistic/automatic/pistol/antitank/syndicate) /datum/export/weapon/clownstuff cost = 500 - unit_name = "clown war tech" - export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium, ) + unit_name = "clown combat equipment" + export_types = list(/obj/item/pneumatic_cannon/pie/selfcharge, /obj/item/shield/energy/bananium, /obj/item/melee/transforming/energy/sword/bananium) /datum/export/weapon/bulldog cost = 400 - unit_name = "drum loaded shotgun" + unit_name = "drum-fed compact combat shotgun" export_types = list(/obj/item/gun/ballistic/automatic/shotgun/bulldog) /datum/export/weapon/smg cost = 350 - unit_name = "automatic c-20r" + unit_name = "C-20r sub-machine gun" export_types = list(/obj/item/gun/ballistic/automatic/c20r) /datum/export/weapon/duelsaber - cost = 360 //Get it? - unit_name = "energy saber" + cost = 360 + unit_name = "double-bladed energy saber" export_types = list(/obj/item/dualsaber) /datum/export/weapon/esword cost = 130 - unit_name = "energy sword" + unit_name = "energy saber" export_types = list(/obj/item/melee/transforming/energy/sword/cx/traitor, /obj/item/melee/transforming/energy/sword/saber) /datum/export/weapon/rapier @@ -286,32 +284,32 @@ /datum/export/weapon/gloves cost = 90 - unit_name = "star struck gloves" + unit_name = "anomalous armwraps" export_types = list(/obj/item/clothing/gloves/fingerless/pugilist/rapid) /datum/export/weapon/l6 cost = 500 - unit_name = "law 6 saw" + unit_name = "Aussec Armory L6 SAW" export_types = list(/obj/item/gun/ballistic/automatic/l6_saw) /datum/export/weapon/m90 cost = 400 - unit_name = "assault class weapon" + unit_name = "M90-gl carbine" export_types = list(/obj/item/gun/ballistic/automatic/m90) /datum/export/weapon/powerglove cost = 100 - unit_name = "hydraulic glove" + unit_name = "pneumatic gauntlet" export_types = list(/obj/item/melee/powerfist) /datum/export/weapon/sniper cost = 750 - unit_name = ".50 sniper" + unit_name = "anti-materiel rifle" export_types = list(/obj/item/gun/ballistic/automatic/sniper_rifle/syndicate) /datum/export/weapon/ebow cost = 600 - unit_name = "mini crossbow" + unit_name = "compact energy crossbow" export_types = list(/obj/item/gun/energy/kinetic_accelerator/crossbow) /datum/export/weapon/m10mm @@ -333,12 +331,12 @@ /datum/export/weapon/smg_mag cost = 45 - unit_name = "smg magazine" + unit_name = "SMG/carbine magazine" export_types = list(/obj/item/ammo_box/magazine/smgm45, /obj/item/ammo_box/magazine/m556) /datum/export/weapon/l6sawammo cost = 60 - unit_name = "law 6 saw ammo box" + unit_name = "L6 SAW ammo box" export_types = list(/obj/item/ammo_box/magazine/mm195x129) include_subtypes = TRUE @@ -355,13 +353,13 @@ /datum/export/weapon/fletcher_ammo cost = 60 - unit_name = "illegal ammo magazines" + unit_name = "flechette launcher magazine" export_types = list(/obj/item/ammo_box/magazine/flechette) include_subtypes = TRUE /datum/export/weapon/dj_a_pizzabomb cost = -6000 - unit_name = "Repair Costs" + unit_name = "undeclared ordinance and subsequent repair costs" export_types = list(/obj/item/pizzabox/bomb, /obj/item/sbeacondrop/bomb) /datum/export/weapon/real_toolbox @@ -371,12 +369,12 @@ /datum/export/weapon/melee cost = 50 - unit_name = "unlisted weapon" + unit_name = "any other melee weapon" export_types = list(/obj/item/melee) include_subtypes = TRUE /datum/export/weapon/gun cost = 50 - unit_name = "unlisted weapon" + unit_name = "any other weapon" export_types = list(/obj/item/gun) include_subtypes = TRUE diff --git a/code/modules/cargo/packs/armory.dm b/code/modules/cargo/packs/armory.dm index 835457536f..9f8bb2f25f 100644 --- a/code/modules/cargo/packs/armory.dm +++ b/code/modules/cargo/packs/armory.dm @@ -158,11 +158,10 @@ /datum/supply_pack/security/armory/russian name = "Russian Surplus Crate" - desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open." + desc = "Hello Comrade, we have the most modern Russian military equipment the black market can offer, for the right price of course. Sadly we couldn't remove the lock so it requires Armory access to open." cost = 7500 contraband = TRUE contains = list(/obj/item/reagent_containers/food/snacks/rationpack, - /obj/item/ammo_box/magazine/m10mm/rifle, /obj/item/clothing/suit/armor/vest/russian, /obj/item/clothing/head/helmet/rus_helmet, /obj/item/clothing/shoes/russian, @@ -172,7 +171,10 @@ /obj/item/clothing/mask/russian_balaclava, /obj/item/clothing/head/helmet/rus_ushanka, /obj/item/clothing/suit/armor/vest/russian_coat, - /obj/item/gun/ballistic/automatic/surplus) + /obj/effect/spawner/bundle/crate/mosin, + /obj/item/storage/toolbox/ammo, + /obj/effect/spawner/bundle/crate/surplusrifle, + /obj/item/storage/toolbox/ammo/surplus) crate_name = "surplus military crate" /datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C) @@ -223,3 +225,10 @@ /obj/item/ammo_box/magazine/wt550m9/wtrubber, /obj/item/ammo_box/magazine/wt550m9/wtrubber) crate_name = "auto rifle ammo crate" + +/datum/supply_pack/security/armory/hell_single + name = "Hellgun Single-Pack" + crate_name = "hellgun crate" + desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." + cost = 1500 + contains = list(/obj/item/gun/energy/laser/hellgun) diff --git a/code/modules/cargo/packs/goodies.dm b/code/modules/cargo/packs/goodies.dm index 86a7c73a34..5d07e85bac 100644 --- a/code/modules/cargo/packs/goodies.dm +++ b/code/modules/cargo/packs/goodies.dm @@ -76,12 +76,6 @@ cost = 200 contains = list(/obj/item/toy/beach_ball) -/datum/supply_pack/goody/hell_single - name = "Hellgun Single-Pack" - desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids." - cost = 1500 - contains = list(/obj/item/gun/energy/laser/hellgun) - /datum/supply_pack/goody/medipen_twopak name = "Medipen Two-Pak" desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst." diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm index 738eb03fbf..cf9cc5e0d1 100644 --- a/code/modules/cargo/packs/security.dm +++ b/code/modules/cargo/packs/security.dm @@ -100,7 +100,7 @@ crate_name = "surplus russian clothing" crate_type = /obj/structure/closet/crate/internals -/datum/supply_pack/security/russianmosin +/datum/supply_pack/security/russian_partisan name = "Russian Partisan Gear" desc = "An old russian partisan equipment crate, comes with a full russian outfit, a loaded surplus rifle and a second magazine." contraband = TRUE @@ -112,12 +112,17 @@ /obj/item/clothing/suit/armor/bulletproof, /obj/item/clothing/head/helmet/alt, /obj/item/clothing/gloves/tackler/combat/insulated, - /obj/item/clothing/mask/gas, - /obj/item/ammo_box/magazine/m10mm/rifle, - /obj/item/gun/ballistic/automatic/surplus) + /obj/item/clothing/mask/gas) crate_name = "surplus russian gear" crate_type = /obj/structure/closet/crate/internals +/datum/supply_pack/security/russian_partisan/fill(obj/structure/closet/crate/C) + ..() + if(prob(20)) + new /obj/effect/spawner/bundle/crate/mosin(C) + else + new /obj/effect/spawner/bundle/crate/surplusrifle(C) + /datum/supply_pack/security/sechardsuit name = "Sec Hardsuit" desc = "One Sec Hardsuit with a small air tank and mask." diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 9b7e928852..e7aa447840 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -993,3 +993,6 @@ GLOBAL_LIST_EMPTY(external_rsc_urls) screen -= S qdel(S) char_render_holders = null + +/client/proc/can_have_part(part_name) + return prefs.pref_species.mutant_bodyparts[part_name] || (part_name in GLOB.unlocked_mutant_parts) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 1b6e1f9748..017a25db34 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -507,310 +507,19 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "" mutant_category = 0 - if(pref_species.mutant_bodyparts["tail_lizard"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Tail

" - - dat += "[features["tail_lizard"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["mam_tail"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Tail

" - - dat += "[features["mam_tail"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["tail_human"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Tail

" - - dat += "[features["tail_human"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["meat_type"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Meat Type

" - - dat += "[features["meat_type"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["snout"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Snout

" - - dat += "[features["snout"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["horns"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Horns

" - - dat += "[features["horns"]]" - dat += "    Change
" - - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - if(pref_species.mutant_bodyparts["frills"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Frills

" - - dat += "[features["frills"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["spines"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Spines

" - - dat += "[features["spines"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["body_markings"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Body Markings

" - - dat += "[features["body_markings"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["mam_body_markings"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Species Markings

" - - dat += "[features["mam_body_markings"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - - if(pref_species.mutant_bodyparts["mam_ears"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Ears

" - - dat += "[features["mam_ears"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["ears"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Ears

" - - dat += "[features["ears"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - if(pref_species.mutant_bodyparts["mam_snouts"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Snout

" - - dat += "[features["mam_snouts"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["legs"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Legs

" - - dat += "[features["legs"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["deco_wings"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Decorative wings

" - - dat += "[features["deco_wings"]]" - dat += "    Change
" - - if(pref_species.mutant_bodyparts["insect_wings"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Insect wings

" - - dat += "[features["insect_wings"]]" - dat += "    Change
" - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["insect_fluff"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Insect Fluff

" - - dat += "[features["insect_fluff"]]" - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["taur"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Tauric Body

" - - dat += "[features["taur"]]" - - if(pref_species.mutant_bodyparts["insect_markings"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Insect markings

" - - dat += "[features["insect_markings"]]
" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["wings"] && GLOB.r_wings_list.len >1) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Wings

" - - dat += "[features["wings"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["xenohead"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Caste Head

" - - dat += "[features["xenohead"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["xenotail"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Tail

" - - dat += "[features["xenotail"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["xenodorsal"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Dorsal Spines

" - - dat += "[features["xenodorsal"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["ipc_screen"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Screen

" - - dat += "[features["ipc_screen"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 - if(pref_species.mutant_bodyparts["ipc_antenna"]) - if(!mutant_category) - dat += APPEARANCE_CATEGORY_COLUMN - - dat += "

Antenna

" - - dat += "[features["ipc_antenna"]]" - - mutant_category++ - if(mutant_category >= MAX_MUTANT_ROWS) - dat += "" - mutant_category = 0 + for(var/mutant_part in GLOB.all_mutant_parts) + if(parent.can_have_part(mutant_part)) + if(!mutant_category) + dat += APPEARANCE_CATEGORY_COLUMN + dat += "

[GLOB.all_mutant_parts[mutant_part]]

" + dat += "[features[mutant_part]]" + var/color_type = GLOB.colored_mutant_parts[mutant_part] //if it can be coloured, show the appropriate button + if(color_type) + dat += "    Change
" + mutant_category++ + if(mutant_category >= MAX_MUTANT_ROWS) + dat += "" + mutant_category = 0 if(mutant_category) dat += "" @@ -856,7 +565,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) var/tauric_shape = FALSE if(features["cock_taur"]) var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[features["cock_shape"]] - if(P.taur_icon && pref_species.mutant_bodyparts["taur"]) + if(P.taur_icon && parent.can_have_part("taur")) var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]] if(T.taur_mode & P.accepted_taurs) tauric_shape = TRUE @@ -1808,14 +1517,14 @@ GLOBAL_LIST_EMPTY(preferences_datums) pref_species = new newtype() //let's ensure that no weird shit happens on species swapping. custom_species = null - if(!pref_species.mutant_bodyparts["body_markings"]) + if(!parent.can_have_part("body_markings")) features["body_markings"] = "None" - if(!pref_species.mutant_bodyparts["mam_body_markings"]) + if(!parent.can_have_part("mam_body_markings")) features["mam_body_markings"] = "None" - if(pref_species.mutant_bodyparts["mam_body_markings"]) + if(parent.can_have_part("mam_body_markings")) if(features["mam_body_markings"] == "None") features["mam_body_markings"] = "Plain" - if(pref_species.mutant_bodyparts["tail_lizard"]) + if(parent.can_have_part("tail_lizard")) features["tail_lizard"] = "Smooth" if(pref_species.id == "felinid") features["mam_tail"] = "Cat" @@ -1943,7 +1652,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) features["tail_human"] = "None" features["tail_lizard"] = "None" - if("meats") + if("meat_type") var/new_meat new_meat = input(user, "Choose your character's meat type:", "Character Preference") as null|anything in GLOB.meat_types if(new_meat) @@ -2048,7 +1757,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(new_deco_wings) features["deco_wings"] = new_deco_wings - if("insect_fluffs") + if("insect_fluff") var/new_insect_fluff new_insect_fluff = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.insect_fluffs_list if(new_insect_fluff) @@ -2196,7 +1905,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("cock_shape") var/new_shape var/list/hockeys = list() - if(pref_species.mutant_bodyparts["taur"]) + if(parent.can_have_part("taur")) var/datum/sprite_accessory/taur/T = GLOB.taur_list[features["taur"]] for(var/A in GLOB.cock_shapes_list) var/datum/sprite_accessory/penis/P = GLOB.cock_shapes_list[A] @@ -2805,10 +2514,10 @@ GLOBAL_LIST_EMPTY(preferences_datums) character.dna.nameless = character.nameless character.dna.custom_species = character.custom_species - if(pref_species.mutant_bodyparts["meat_type"]) + if((parent && parent.can_have_part("meat_type")) || pref_species.mutant_bodyparts["meat_type"]) character.type_of_meat = GLOB.meat_types[features["meat_type"]] - if(character.dna.species.mutant_bodyparts["legs"] && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian")) + if(((parent && parent.can_have_part("legs")) || pref_species.mutant_bodyparts["legs"]) && (character.dna.features["legs"] == "Digitigrade" || character.dna.features["legs"] == "Avian")) pref_species.species_traits |= DIGITIGRADE else pref_species.species_traits -= DIGITIGRADE diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index dffab645a4..64c5e775b0 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 33 +#define SAVEFILE_VERSION_MAX 34 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 350a577fb1..3f6d21bcd2 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -149,6 +149,30 @@ icon_state = "eyepatch" item_state = "eyepatch" +/obj/item/clothing/glasses/eyepatch/syndicate + name = "cybernetic eyepatch" + desc = "An eyepatch used to enhance one's aim with guns." + icon_state = "syndicatepatch" + item_state = "syndicatepatch" + resistance_flags = ACID_PROOF + +/obj/item/clothing/glasses/eyepatch/syndicate/equipped(mob/living/carbon/human/user, slot) + . = ..() + if(slot == SLOT_GLASSES) + user.visible_message("Circuitry from the eyepatch links itself to your brain as you put on the eyepatch.") + if(HAS_TRAIT(user, TRAIT_POOR_AIM)) + user.visible_message("You hear a fizzing noise from the circuit. That can't be good.") + ADD_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM") + ADD_TRAIT(src, TRAIT_NODROP, "SYNDICATE_EYEPATCH_NODROP") + +/obj/item/clothing/glasses/eyepatch/syndicate/dropped(mob/living/carbon/human/user) + . = ..() + REMOVE_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM") + var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES) + if(eyes) + eyes.applyOrganDamage(30) + user.visible_message("Your eye stings as the circuitry is removed from your eye!") + /obj/item/clothing/glasses/monocle name = "monocle" desc = "Such a dapper eyepiece!" diff --git a/code/modules/clothing/gloves/ring.dm b/code/modules/clothing/gloves/ring.dm index 8354f13bd2..daca843c45 100644 --- a/code/modules/clothing/gloves/ring.dm +++ b/code/modules/clothing/gloves/ring.dm @@ -21,7 +21,7 @@ desc = "An expensive ring, studded with a diamond. Cultures have used these rings in courtship for a millenia." icon_state = "ringdiamond" item_state = "dring" - + /obj/item/clothing/gloves/ring/diamond/attack_self(mob/user) user.visible_message("\The [user] gets down on one knee, presenting \the [src].","You get down on one knee, presenting \the [src].") @@ -30,3 +30,12 @@ desc = "A tiny silver ring, sized to wrap around a finger." icon_state = "ringsilver" item_state = "sring" + +/obj/item/clothing/gloves/ring/custom + name = "ring" + desc = "A ring." + gender = NEUTER + w_class = WEIGHT_CLASS_TINY + obj_flags = UNIQUE_RENAME + icon_state = "ringsilver" + item_state = "sring" diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 40797b97ed..12829ed0e7 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -214,7 +214,7 @@ name = "advanced hardsuit" desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." item_state = "ce_hardsuit" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 95, "fire" = 100, "acid" = 90, "wound" = 10) + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90, "wound" = 10) heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm index 607a8ecb55..6364a9a1c1 100644 --- a/code/modules/clothing/suits/miscellaneous.dm +++ b/code/modules/clothing/suits/miscellaneous.dm @@ -543,6 +543,7 @@ cold_protection = HEAD min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT flags_inv = HIDEHAIR|HIDEEARS + rad_flags = RAD_NO_CONTAMINATE /obj/item/clothing/suit/hooded/wintercoat/centcom name = "centcom winter coat" @@ -1062,4 +1063,4 @@ name = "Samurai outfit" desc = "An outfit used by traditional japanese warriors." icon_state = "samurai" - item_state = "samurai" \ No newline at end of file + item_state = "samurai" diff --git a/code/modules/clothing/under/accessories.dm b/code/modules/clothing/under/accessories.dm index cb173f3bde..ee7e4c48e1 100644 --- a/code/modules/clothing/under/accessories.dm +++ b/code/modules/clothing/under/accessories.dm @@ -369,3 +369,12 @@ icon_state = "plastics" armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = -40) flags_inv = HIDEACCESSORY + +//necklace +/obj/item/clothing/accessory/necklace + name = "necklace" + desc = "A necklace." + icon_state = "locket" + obj_flags = UNIQUE_RENAME + custom_materials = list(/datum/material/iron=100) + resistance_flags = FIRE_PROOF diff --git a/code/modules/emoji/emoji_parse.dm b/code/modules/emoji/emoji_parse.dm index 33d32227e1..3fd83899c9 100644 --- a/code/modules/emoji/emoji_parse.dm +++ b/code/modules/emoji/emoji_parse.dm @@ -16,11 +16,16 @@ search = findtext(text, ":", pos + length(text[pos])) if(search) emoji = lowertext(copytext(text, pos + length(text[pos]), search)) + var/isthisapath = (emoji[1] == "/") && text2path(emoji) var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat) var/tag = sheet.icon_tag("emoji-[emoji]") if(tag) parsed += "[tag]" //evil way of enforcing 16x16 pos = search + length(text[pos]) + else if(ispath(isthisapath, /atom)) //path + var/atom/thisisanatom = isthisapath + parsed += "[icon2html(initial(thisisanatom.icon), world, initial(thisisanatom.icon_state))]" + pos = search + length(text[pos]) else parsed += copytext(text, pos, search) pos = search diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 3b5ea6b20a..18b8c8e21c 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -94,7 +94,7 @@ shuffle_inplace(mobs) - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new /obj/effect/proc_holder/spell/targeted/mind_transfer + var/obj/effect/proc_holder/spell/pointed/mind_transfer/swapper = new /obj/effect/proc_holder/spell/pointed/mind_transfer while(mobs.len > 1) var/mob/living/carbon/human/H = pick(mobs) mobs -= H diff --git a/code/modules/food_and_drinks/food/snacks_pie.dm b/code/modules/food_and_drinks/food/snacks_pie.dm index 03f4640718..81805f5529 100644 --- a/code/modules/food_and_drinks/food/snacks_pie.dm +++ b/code/modules/food_and_drinks/food/snacks_pie.dm @@ -113,6 +113,27 @@ tastes = list("pie" = 1, "meat" = 1) foodtype = GRAIN | MEAT +/obj/item/reagent_containers/food/snacks/pie/burek + name = "Burek" + icon = 'icons/obj/food/piecake.dmi' + icon_state = "burek" + desc = "If you know, you know." + slice_path = /obj/item/reagent_containers/food/snacks/pie/burekslice + slices_num = 4 + bonus_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 6) + list_reagents = list(/datum/reagent/consumable/nutriment= 20, /datum/reagent/consumable/nutriment/vitamin = 6) + bitesize = 12 + tastes = list("meat" = 1, "oil" = 1) + foodtype = GRAIN | MEAT + +/obj/item/reagent_containers/food/snacks/pie/burekslice + name = "Burek Slice" + icon = 'icons/obj/food/piecake.dmi' + icon_state = "burekslice" + desc = "A slice of Burek, watch out for oil stains!" + tastes = list("meat" = 1, "oil" = 1) + foodtype = GRAIN | MEAT + /obj/item/reagent_containers/food/snacks/pie/tofupie name = "tofu-pie" diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm index 52becf81df..289c698b9a 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pies_sweets.dm @@ -130,6 +130,18 @@ result = /obj/item/reagent_containers/food/snacks/pie/dulcedebatata subcategory = CAT_PIE +/datum/crafting_recipe/food/burek + name = "Burek" + reqs = list( + /datum/reagent/consumable/blackpepper = 3, + /datum/reagent/consumable/sodiumchloride = 3, + /obj/item/reagent_containers/food/snacks/pizzabread = 2, + /obj/item/reagent_containers/food/snacks/meat/cutlet/plain = 6, + /obj/item/reagent_containers/food/snacks/butter = 1, + ) + result = /obj/item/reagent_containers/food/snacks/pie/burek + subcategory = CAT_PIE + /datum/crafting_recipe/food/meatpie name = "Meat pie" reqs = list( @@ -302,4 +314,4 @@ /obj/item/reagent_containers/food/snacks/spiderling = 1 ) result = /obj/item/reagent_containers/food/snacks/spiderlollipop - subcategory = CAT_PIE \ No newline at end of file + subcategory = CAT_PIE diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index 4e545c13ee..a0c273613f 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -3,9 +3,9 @@ desc = "An advanced device designed to manipulate plant genetic makeup." icon = 'icons/obj/hydroponics/equipment.dmi' icon_state = "dnamod" - density = TRUE circuit = /obj/item/circuitboard/machine/plantgenes - pass_flags = PASSTABLE + pass_flags = PASSTABLE | LETPASSTHROW + flags_1 = DEFAULT_RICOCHET_1 var/obj/item/seeds/seed var/obj/item/disk/plantgene/disk diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm index 621e79fb77..6525ac42d4 100644 --- a/code/modules/hydroponics/grown/cannabis.dm +++ b/code/modules/hydroponics/grown/cannabis.dm @@ -14,9 +14,7 @@ icon_dead = "cannabis-dead" // Same for the dead icon genes = list(/datum/plant_gene/trait/repeated_harvest) mutatelist = list(/obj/item/seeds/cannabis/rainbow, - /obj/item/seeds/cannabis/death, - /obj/item/seeds/cannabis/white, - /obj/item/seeds/cannabis/ultimate) + /obj/item/seeds/cannabis/death) reagents_add = list(/datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.35) // gives u the munchies @@ -27,7 +25,7 @@ species = "megacannabis" plantname = "Rainbow Weed" product = /obj/item/reagent_containers/food/snacks/grown/cannabis/rainbow - mutatelist = list() + mutatelist = list(/obj/item/seeds/cannabis/ultimate) reagents_add = list(/datum/reagent/toxin/mindbreaker = 0.15, /datum/reagent/toxin/lipolicide = 0.35) rarity = 40 @@ -38,7 +36,7 @@ species = "blackcannabis" plantname = "Deathweed" product = /obj/item/reagent_containers/food/snacks/grown/cannabis/death - mutatelist = list() + mutatelist = list(/obj/item/seeds/cannabis/white) reagents_add = list(/datum/reagent/toxin/cyanide = 0.35, /datum/reagent/drug/space_drugs = 0.15, /datum/reagent/toxin/lipolicide = 0.15) rarity = 40 diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 6ac7bbcfcb..d130d50aa5 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -33,26 +33,6 @@ filling_color = "#00FF00" juice_results = list(/datum/reagent/consumable/limejuice = 0) -// Electric Lime -/obj/item/seeds/lime/electric - name = "pack of electric lime seeds" - desc = "Electrically sour seeds." - icon_state = "seed-electriclime" - species = "electric lime" - plantname = "Electric Lime Tree" - growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' - icon_grow = "lime-grow" - icon_dead = "lime-dead" - icon_harvest = "lime-harvest" - product = /obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric - genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/cell_charge, /datum/plant_gene/trait/glow/green) - -/obj/item/reagent_containers/food/snacks/grown/citrus/lime/electric - seed = /obj/item/seeds/lime/electric - name = "electric lime" - desc = "It's so sour, you'll be shocked!" - icon_state = "electriclime" - // Orange /obj/item/seeds/orange name = "pack of orange seeds" diff --git a/code/modules/hydroponics/grown/garlic.dm b/code/modules/hydroponics/grown/garlic.dm index 4184b85008..2cc3f41860 100644 --- a/code/modules/hydroponics/grown/garlic.dm +++ b/code/modules/hydroponics/grown/garlic.dm @@ -9,6 +9,9 @@ potency = 25 growthstages = 3 growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "garlic-grow" + icon_harvest = "garlic-harvest" + icon_dead = "garlic-dead" reagents_add = list(/datum/reagent/consumable/garlic = 0.15, /datum/reagent/consumable/nutriment = 0.1) /obj/item/reagent_containers/food/snacks/grown/garlic diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm index 3b5159465c..a74850f3be 100644 --- a/code/modules/hydroponics/grown/grass_carpet.dm +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -51,6 +51,7 @@ icon_grow = "fairygrass-grow" icon_dead = "fairygrass-dead" genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/glow/blue) + mutatelist = list (/obj/item/seeds/grass/carpet) reagents_add = list(/datum/reagent/consumable/nutriment = 0.02, /datum/reagent/hydrogen = 0.05, /datum/reagent/drug/space_drugs = 0.15) /obj/item/reagent_containers/food/snacks/grown/grass/fairy @@ -99,7 +100,7 @@ species = "carpet" plantname = "Carpet" product = /obj/item/reagent_containers/food/snacks/grown/grass/carpet - mutatelist = list() + mutatelist = list(/obj/item/seeds/grass/fairy) rarity = 10 /obj/item/reagent_containers/food/snacks/grown/grass/carpet diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm index de27d1eed7..223b2c7bce 100644 --- a/code/modules/hydroponics/grown/tea_coffee.dm +++ b/code/modules/hydroponics/grown/tea_coffee.dm @@ -44,7 +44,7 @@ filling_color = "#4582B4" grind_results = list(/datum/reagent/toxin/teapowder = 0, /datum/reagent/medicine/salglu_solution = 0) -// Kitty drugs +// Catnip /obj/item/seeds/tea/catnip name = "pack of catnip seeds" icon_state = "seed-catnip" @@ -52,6 +52,9 @@ species = "catnip" plantname = "Catnip Plant" growthstages = 3 + icon_grow = "catnip-grow" + icon_harvest = "catnip-harvest" + icon_dead = "tea-dead" product = /obj/item/reagent_containers/food/snacks/grown/tea/catnip reagents_add = list(/datum/reagent/pax/catnip = 0.1, /datum/reagent/consumable/nutriment/vitamin = 0.06, /datum/reagent/toxin/teapowder = 0.3) rarity = 50 diff --git a/code/modules/mafia/_defines.dm b/code/modules/mafia/_defines.dm new file mode 100644 index 0000000000..194851beed --- /dev/null +++ b/code/modules/mafia/_defines.dm @@ -0,0 +1,65 @@ +///how many people can play mafia without issues (running out of spawns, procs not expecting more than this amount of people, etc) +#define MAFIA_MAX_PLAYER_COUNT 12 + +#define MAFIA_TEAM_TOWN "town" +#define MAFIA_TEAM_MAFIA "mafia" +#define MAFIA_TEAM_SOLO "solo" + +//types of town roles for random setup gen +/// assistants it's just assistants filling up the rest of the roles +#define TOWN_OVERFLOW "overflow" +/// roles that learn info about others in the game (chaplain, detective, psych) +#define TOWN_INVEST "invest" +/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts) +#define TOWN_PROTECT "protect" +/// roles that don't fit into anything else (hop) +#define TOWN_MISC "misc" + +//other types (mafia team, neutrals) +/// normal vote kill changelings +#define MAFIA_REGULAR "regular" +/// every other changeling role that has extra abilities +#define MAFIA_SPECIAL "special" +/// role that wins solo that nobody likes +#define NEUTRAL_KILL "kill" +/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia +#define NEUTRAL_DISRUPT "disrupt" + +#define MAFIA_PHASE_SETUP 1 +#define MAFIA_PHASE_DAY 2 +#define MAFIA_PHASE_VOTING 3 +#define MAFIA_PHASE_JUDGEMENT 4 +#define MAFIA_PHASE_NIGHT 5 +#define MAFIA_PHASE_VICTORY_LAP 6 + +#define MAFIA_ALIVE 1 +#define MAFIA_DEAD 2 + +#define COMSIG_MAFIA_ON_KILL "mafia_onkill" +#define MAFIA_PREVENT_KILL 1 + +#define COMSIG_MAFIA_CAN_PERFORM_ACTION "mafia_can_perform_action" +#define MAFIA_PREVENT_ACTION 1 + +//in order of events + game end + +/// when the shutters fall, before the 45 second wait and night event resolution +#define COMSIG_MAFIA_SUNDOWN "sundown" +/// after the 45 second wait, for actions that must go first +#define COMSIG_MAFIA_NIGHT_START "night_start" +/// most night actions now resolve +#define COMSIG_MAFIA_NIGHT_ACTION_PHASE "night_actions" +/// now killing happens from the roles that do that. the reason this is post action phase is to ensure doctors can protect and lawyers can block +#define COMSIG_MAFIA_NIGHT_KILL_PHASE "night_kill" +/// now undoing states like protection, actions that must happen last, etc. right before shutters raise and the day begins +#define COMSIG_MAFIA_NIGHT_END "night_end" + +/// signal sent to roles when the game is confirmed ending +#define COMSIG_MAFIA_GAME_END "game_end" + +/// list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in +GLOBAL_LIST_EMPTY(mafia_signup) +/// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game. +GLOBAL_LIST_EMPTY(mafia_bad_signup) +/// the current global mafia game running. +GLOBAL_VAR(mafia_game) diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm new file mode 100644 index 0000000000..cd8c382f30 --- /dev/null +++ b/code/modules/mafia/controller.dm @@ -0,0 +1,971 @@ + + +/** + * The mafia controller handles the mafia minigame in progress. + * It is first created when the first ghost signs up to play. + */ +/datum/mafia_controller + ///list of observers that should get game updates. + var/list/spectators = list() + ///all roles in the game, dead or alive. check their game status if you only want living or dead. + var/list/all_roles = list() + ///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play + var/list/player_role_lookup = list() + ///what part of the game you're playing in. day phases, night phases, judgement phases, etc. + var/phase = MAFIA_PHASE_SETUP + ///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc. + var/turn = 0 + ///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over + var/list/custom_setup = list() + ///first day has no voting, and thus is shorter + var/first_day_phase_period = 20 SECONDS + ///talk with others about the last night + var/day_phase_period = 1 MINUTES + ///vote someone to get put on trial + var/voting_phase_period = 30 SECONDS + ///defend yourself! don't get lynched! sometimes skipped if nobody votes. + var/judgement_phase_period = 30 SECONDS + ///guilty or innocent, we want a bit of time for players to process the outcome of the vote + var/judgement_lynch_period = 5 SECONDS + ///mafia talk at night and pick someone to kill, some town roles use their actions, etc etc. + var/night_phase_period = 45 SECONDS + ///like the lynch period, players need to see what the other players in the game's roles were + var/victory_lap_period = 20 SECONDS + + ///template picked when the game starts. used for the name and desc reading + var/datum/map_template/mafia/current_map + ///map generation tool that deletes the current map after the game finishes + var/datum/mapGenerator/massdelete/map_deleter + + ///Readable list of roles in current game, sent to the tgui panel for roles list > list("Psychologist x1", "Clown x2") + var/list/current_setup_text + + ///starting outfit for all mafia players. it's just a grey jumpsuit. + var/player_outfit = /datum/outfit/mafia + + ///spawn points for players, each one has a house + var/list/landmarks = list() + ///town center for when people get put on trial + var/town_center_landmark + + ///group voting on one person, like putting people to trial or choosing who to kill as mafia + var/list/votes = list() + ///and these (judgement_innocent_votes, judgement_abstain_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent, and "eh, i don't really care" lists. whichever has more inno or guilty wins! + var/list/judgement_abstain_votes = list() + var/list/judgement_innocent_votes = list() + var/list/judgement_guilty_votes = list() + ///current role on trial for the judgement phase, will die if guilty is greater than innocent + var/datum/mafia_role/on_trial + + ///current timer for phase + var/next_phase_timer + + ///used for debugging in testing (doesn't put people out of the game, some other shit i forgot, who knows just don't set this in live) honestly kinda deprecated + var/debug = FALSE + + ///Max player count + var/max_player = MAFIA_MAX_PLAYER_COUNT + ///Required player count + var/required_player = 5 + ///Prioritizes clients to have cool antag roles + var/low_pop_mode = FALSE + +/datum/mafia_controller/New() + . = ..() + GLOB.mafia_game = src + map_deleter = new + +/datum/mafia_controller/Destroy(force, ...) + . = ..() + GLOB.mafia_game = null + end_game() + qdel(map_deleter) + +/** + * Triggers at beginning of the game when there is a confirmed list of valid, ready players. + * Creates a 100% ready game that has NOT started (no players in bodies) + * Followed by start game + * + * Does the following: + * * Picks map, and loads it + * * Grabs landmarks if it is the first time it's loading + * * Sets up the role list + * * Puts players in each role randomly + * Arguments: + * * setup_list: list of all the datum setups (fancy list of roles) that would work for the game + * * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles + */ +/datum/mafia_controller/proc/prepare_game(setup_list, ready_players) + + var/list/possible_maps = subtypesof(/datum/map_template/mafia) + var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list) + + current_map = pick(possible_maps) + current_map = new current_map + + if(!spawn_area) + CRASH("No spawn area detected for Mafia!") + var/list/bounds = current_map.load(spawn_area) + if(!bounds) + CRASH("Loading mafia map failed!") + map_deleter.defineRegion(spawn_area, locate(spawn_area.x + 23,spawn_area.y + 23,spawn_area.z), replace = TRUE) //so we're ready to mass delete when round ends + + if(!landmarks.len)//we grab town center when we grab landmarks, if there is none (the first game signed up for let's grab them post load) + for(var/obj/effect/landmark/mafia/possible_spawn in GLOB.landmarks_list) + if(istype(possible_spawn, /obj/effect/landmark/mafia/town_center)) + town_center_landmark = possible_spawn + else + landmarks += possible_spawn + + current_setup_text = list() + + var/list/boring_roles = list() + var/list/not_boring_roles = list() + + for(var/rtype in setup_list) + for(var/i in 1 to setup_list[rtype]) + var/datum/mafia_role/role = new rtype(src) + all_roles += role + if(role.role_type == TOWN_PROTECT || role.role_type == TOWN_INVEST || role.role_type == MAFIA_SPECIAL || role.role_type == MAFIA_REGULAR) + not_boring_roles += role + else + boring_roles += role + var/datum/mafia_role/rp = rtype + current_setup_text += "[initial(rp.name)] x[setup_list[rtype]]" + + var/list/spawnpoints = landmarks.Copy() + + if(length(ready_players) < 7 || low_pop_mode) + //do normal assign + for(var/datum/mafia_role/role in not_boring_roles) + role.assigned_landmark = pick_n_take(spawnpoints) + role.player_key = pick_n_take(ready_players) + //shame! + for(var/datum/mafia_role/role in boring_roles) + role.assigned_landmark = pick_n_take(spawnpoints) + role.player_key = pick_n_take(ready_players) + + else //go run the normal one + for(var/datum/mafia_role/role in all_roles) + role.assigned_landmark = pick_n_take(spawnpoints) + if(!debug) + role.player_key = pick_n_take(ready_players) + else + role.player_key = pop(ready_players) + +/datum/mafia_controller/proc/send_message(msg,team) + for(var/datum/mafia_role/R in all_roles) + if(team && R.team != team) + continue + to_chat(R.body,msg) + var/team_suffix = team ? "([uppertext(team)] CHAT)" : "" + for(var/M in GLOB.dead_mob_list) + var/mob/spectator = M + if(spectator.ckey in spectators) //was in current game, or spectatin' (won't send to living) + var/link = FOLLOW_LINK(M, town_center_landmark) + to_chat(M, "[link] MAFIA: [msg] [team_suffix]") + +/** + * The game by this point is now all set up, and so we can put people in their bodies and start the first phase. + * + * Does the following: + * * Creates bodies for all of the roles with the first proc + * * Starts the first day manually (so no timer) with the second proc + */ +/datum/mafia_controller/proc/start_game() + create_bodies() + start_day() + +/** + * How every day starts. + * + * What players do in this phase: + * * If day one, just a small starting period to see who is in the game and check role, leading to the night phase. + * * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase. + */ +/datum/mafia_controller/proc/start_day() + turn += 1 + phase = MAFIA_PHASE_DAY + if(!check_victory()) + if(turn == 1) + send_message("The selected map is [current_map.name]!
[current_map.description]
") + send_message("Day [turn] started! There is no voting on the first day. Say hello to everybody!") + next_phase_timer = addtimer(CALLBACK(src,.proc/check_trial, FALSE),first_day_phase_period,TIMER_STOPPABLE) //no voting period = no votes = instant night + else + send_message("Day [turn] started! Voting will start in 1 minute.") + next_phase_timer = addtimer(CALLBACK(src,.proc/start_voting_phase),day_phase_period,TIMER_STOPPABLE) + + SStgui.update_uis(src) + +/** + * Players have finished the discussion period, and now must put up someone to the chopping block. + * + * What players do in this phase: + * * Vote on which player to put up for lynching, leading to the judgement phase. + * * If no votes are case, the judgement phase is skipped, leading to the night phase. + */ +/datum/mafia_controller/proc/start_voting_phase() + phase = MAFIA_PHASE_VOTING + next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose! + send_message("Voting started! Vote for who you want to see on trial today.") + SStgui.update_uis(src) + +/** + * Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty. + * + * What players do in this phase: + * * Vote innocent or guilty, if they are not on trial. + * * Defend themselves and wait for judgement, if they are. + * * Leads to the lynch phase. + * Arguments: + * * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day. + */ +/datum/mafia_controller/proc/check_trial(verbose = TRUE) + var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE) + // var/loser_votes = get_vote_count(loser,"Day") + if(loser) + // if(loser_votes > 12) + // loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body) + send_message("[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!") + //refresh the lists + judgement_abstain_votes = list() + judgement_innocent_votes = list() + judgement_guilty_votes = list() + for(var/i in all_roles) + var/datum/mafia_role/abstainee = i + if(abstainee.game_status == MAFIA_ALIVE && abstainee != loser) + judgement_abstain_votes += abstainee + on_trial = loser + on_trial.body.forceMove(get_turf(town_center_landmark)) + phase = MAFIA_PHASE_JUDGEMENT + next_phase_timer = addtimer(CALLBACK(src, .proc/lynch),judgement_phase_period,TIMER_STOPPABLE) + reset_votes("Day") + else + if(verbose) + send_message("Not enough people have voted to put someone on trial, nobody will be lynched today.") + if(!check_victory()) + lockdown() + SStgui.update_uis(src) + +/** + * Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home. + * + * What players do in this phase: + * * r/watchpeopledie + * * If the accused is killed, their true role is revealed to the rest of the players. + */ +/datum/mafia_controller/proc/lynch() + for(var/i in judgement_innocent_votes) + var/datum/mafia_role/role = i + send_message("[role.body.real_name] voted innocent.") + for(var/ii in judgement_abstain_votes) + var/datum/mafia_role/role = ii + send_message("[role.body.real_name] abstained.") + for(var/iii in judgement_guilty_votes) + var/datum/mafia_role/role = iii + send_message("[role.body.real_name] voted guilty.") + if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch + send_message("Guilty wins majority, [on_trial.body.real_name] has been lynched.") + on_trial.kill(src, lynch = TRUE) + addtimer(CALLBACK(src, .proc/send_home, on_trial),judgement_lynch_period) + else + send_message("Innocent wins majority, [on_trial.body.real_name] has been spared.") + on_trial.body.forceMove(get_turf(on_trial.assigned_landmark)) + on_trial = null + //day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else + next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this + +/** + * Teenie helper proc to move players back to their home. + * Used in the above, but also used in the debug button "send all players home" + * Arguments: + * * role: mafia role that is getting sent back to the game. + */ +/datum/mafia_controller/proc/send_home(datum/mafia_role/role) + role.body.forceMove(get_turf(role.assigned_landmark)) + +/** + * Checks to see if a faction (or solo antagonist) has won. + * + * Calculates in this order: + * * counts up town, mafia, and solo + * * solos can count as town members for the purposes of mafia winning + * * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams + * * checks if solos won from above, then if town, then if mafia + * * starts the end of the game if a faction won + * * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory + */ +/datum/mafia_controller/proc/check_victory() + //needed for achievements + var/list/total_town = list() + var/list/total_mafia = list() + + var/alive_town = 0 + var/alive_mafia = 0 + var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes + var/list/total_victors = list() //if this list gets filled with anyone, they win. list because side antags can with with people + var/blocked_victory = FALSE //if a solo antagonist is stopping the town or mafia from finishing the game. + + ///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE + + for(var/datum/mafia_role/R in all_roles) + switch(R.team) + if(MAFIA_TEAM_MAFIA) + total_mafia += R + if(R.game_status == MAFIA_ALIVE) + alive_mafia += R.vote_power + if(MAFIA_TEAM_TOWN) + total_town += R + if(R.game_status == MAFIA_ALIVE) + alive_town += R.vote_power + if(MAFIA_TEAM_SOLO) + if(R.game_status == MAFIA_ALIVE) + if(R.solo_counts_as_town) + alive_town += R.vote_power + solos_to_ask += R + + ///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN + + for(var/datum/mafia_role/solo in solos_to_ask) + if(solo.check_total_victory(alive_town, alive_mafia)) + total_victors += solo + if(solo.block_team_victory(alive_town, alive_mafia)) + blocked_victory = TRUE + + //solo victories! + var/solo_end = FALSE + for(var/datum/mafia_role/winner in total_victors) + send_message("!! [uppertext(winner.name)] VICTORY !!") + // var/client/winner_client = GLOB.directory[winner.player_key] + // winner_client?.give_award(winner.winner_award, winner.body) + solo_end = TRUE + if(solo_end) + start_the_end() + return TRUE + if(blocked_victory) + return FALSE + if(alive_mafia == 0) + // for(var/datum/mafia_role/townie in total_town) + // var/client/townie_client = GLOB.directory[townie.player_key] + // townie_client?.give_award(townie.winner_award, townie.body) + start_the_end("!! TOWN VICTORY !!") + return TRUE + else if(alive_mafia >= alive_town) //guess could change if town nightkill is added + start_the_end("!! MAFIA VICTORY !!") + // for(var/datum/mafia_role/changeling in total_mafia) + // var/client/changeling_client = GLOB.directory[changeling.player_key] + // changeling_client?.give_award(changeling.winner_award, changeling.body) + return TRUE + +/** + * The end of the game is in two procs, because we want a bit of time for players to see eachothers roles. + * Because of how check_victory works, the game is halted in other places by this point. + * + * What players do in this phase: + * * See everyone's role postgame + * * See who won the game + * Arguments: + * * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory + */ +/datum/mafia_controller/proc/start_the_end(message) + SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END) + if(message) + send_message(message) + for(var/datum/mafia_role/R in all_roles) + R.reveal_role(src) + phase = MAFIA_PHASE_VICTORY_LAP + next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE) + +/** + * Cleans up the game, resetting variables back to the beginning and removing the map with the generator. + */ +/datum/mafia_controller/proc/end_game() + map_deleter.generate() //remove the map, it will be loaded at the start of the next one + QDEL_LIST(all_roles) + current_setup_text = null + custom_setup = list() + turn = 0 + votes = list() + //map gen does not deal with landmarks + QDEL_LIST(landmarks) + QDEL_NULL(town_center_landmark) + phase = MAFIA_PHASE_SETUP + +/** + * After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc. + */ +/datum/mafia_controller/proc/lockdown() + toggle_night_curtains(close=TRUE) + start_night() + +/** + * Shuts poddoors attached to mafia. + * Arguments: + * * close: boolean, the state you want the curtains in. + */ +/datum/mafia_controller/proc/toggle_night_curtains(close) + for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these + if(D.id != "mafia") //so as to not trigger shutters on station, lol + continue + if(close) + INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/close) + else + INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open) + +/** + * The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried. + * + * What players do in this phase: + * * Mafia are told to begin voting on who to kill + * * Powers that are picked during the day announce themselves right now + */ +/datum/mafia_controller/proc/start_night() + phase = MAFIA_PHASE_NIGHT + send_message("Night [turn] started! Lockdown will end in 45 seconds.") + SEND_SIGNAL(src,COMSIG_MAFIA_SUNDOWN) + next_phase_timer = addtimer(CALLBACK(src, .proc/resolve_night),night_phase_period,TIMER_STOPPABLE) + SStgui.update_uis(src) + +/** + * The end of the night, and a series of signals for the order of events on a night. + * + * Order of events, and what they mean: + * * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first + * * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities + * * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking) + * * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities + * * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last + * * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE + */ +/datum/mafia_controller/proc/resolve_night() + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START) + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE) + //resolve mafia kill, todo unsnowflake this + var/datum/mafia_role/R = get_vote_winner("Mafia") + if(R) + var/datum/mafia_role/killer = get_random_voter("Mafia") + if(SEND_SIGNAL(killer,COMSIG_MAFIA_CAN_PERFORM_ACTION,src,"mafia killing",R) & MAFIA_PREVENT_ACTION) + send_message("[killer.body.real_name] was unable to attack [R.body.real_name] tonight!",MAFIA_TEAM_MAFIA) + else + send_message("[killer.body.real_name] has attacked [R.body.real_name]!",MAFIA_TEAM_MAFIA) + R.kill(src) + reset_votes("Mafia") + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_KILL_PHASE) + SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_END) + toggle_night_curtains(close=FALSE) + start_day() + SStgui.update_uis(src) + +/** + * Proc that goes off when players vote for something with their mafia panel. + * + * If teams, it hides the tally overlay and only sends the vote messages to the team that is voting + * Arguments: + * * voter: the mafia role that is trying to vote for... + * * target: the mafia role that is getting voted for + * * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?) + * * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night) + */ +/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams) + if(!votes[vote_type]) + votes[vote_type] = list() + var/old_vote = votes[vote_type][voter] + if(old_vote && old_vote == target) + votes[vote_type] -= voter + else + votes[vote_type][voter] = target + if(old_vote && old_vote == target) + send_message("[voter.body.real_name] retracts their vote for [target.body.real_name]!", team = teams) + else + send_message("[voter.body.real_name] voted for [target.body.real_name]!",team = teams) + if(!teams) + target.body.update_icon() //Update the vote display if it's a public vote + var/datum/mafia_role/old = old_vote + if(old) + old.body.update_icon() + +/** + * Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched + */ +/datum/mafia_controller/proc/reset_votes(vote_type) + var/list/bodies_to_update = list() + for(var/vote in votes[vote_type]) + var/datum/mafia_role/R = votes[vote_type][vote] + bodies_to_update += R.body + votes[vote_type] = list() + for(var/mob/M in bodies_to_update) + M.update_icon() + +/** + * Returns how many people voted for the role, in whatever vote (day vote, night kill vote) + * Arguments: + * * role: the mafia role the proc tries to get the amount of votes for + * * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role) + */ +/datum/mafia_controller/proc/get_vote_count(role,vote_type) + . = 0 + for(var/v in votes[vote_type]) + var/datum/mafia_role/votee = v + if(votes[vote_type][votee] == role) + . += votee.vote_power + +/** + * Returns whichever role got the most votes, in whatever vote (day vote, night kill vote) + * returns null if no votes + * Arguments: + * * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) + */ +/datum/mafia_controller/proc/get_vote_winner(vote_type) + var/list/tally = list() + for(var/votee in votes[vote_type]) + if(!tally[votes[vote_type][votee]]) + tally[votes[vote_type][votee]] = 1 + else + tally[votes[vote_type][votee]] += 1 + sortTim(tally,/proc/cmp_numeric_dsc,associative=TRUE) + return length(tally) ? tally[1] : null + +/** + * Returns a random person who voted for whatever vote (day vote, night kill vote) + * Arguments: + * * vote_type: vote type (getting a random day voter, or mafia night voter) + */ +/datum/mafia_controller/proc/get_random_voter(vote_type) + if(length(votes[vote_type])) + return pick(votes[vote_type]) + +/** + * Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them + * Arguments: + * * source: the body of the role getting the overlays + * * overlay_list: signal var passing the overlay list of the mob + */ +/datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list) + if(phase != MAFIA_PHASE_VOTING) + return + var/v = get_vote_count(player_role_lookup[source],"Day") + var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v > 12 ? "over_12" : v]") + overlay_list += MA + +/** + * Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key + * + * Notably: + * * Toggles godmode so the mafia players cannot kill themselves + * * Adds signals for voting overlays, see display_votes proc + * * gives mafia panel + * * sends the greeting text (goals, role name, etc) + */ +/datum/mafia_controller/proc/create_bodies() + for(var/datum/mafia_role/role in all_roles) + var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark)) + H.equipOutfit(player_outfit) + H.status_flags |= GODMODE + RegisterSignal(H,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/display_votes) + var/datum/action/innate/mafia_panel/mafia_panel = new(null,src) + mafia_panel.Grant(H) + var/client/player_client = GLOB.directory[role.player_key] + if(player_client) + player_client.prefs.copy_to(H) + if(H.dna.species.outfit_important_for_life) //plasmamen + H.set_species(/datum/species/human) + role.body = H + player_role_lookup[H] = role + H.key = role.player_key + role.greet() + +/datum/mafia_controller/ui_data(mob/user) + . = ..() + switch(phase) + if(MAFIA_PHASE_DAY,MAFIA_PHASE_VOTING,MAFIA_PHASE_JUDGEMENT) + .["phase"] = "Day [turn]" + if(MAFIA_PHASE_NIGHT) + .["phase"] = "Night [turn]" + else + .["phase"] = "No Game" + if(user.client?.holder) + .["admin_controls"] = TRUE //show admin buttons to start/setup/stop + if(phase == MAFIA_PHASE_JUDGEMENT) + .["judgement_phase"] = TRUE //show judgement section + else + .["judgement_phase"] = FALSE + var/datum/mafia_role/user_role = player_role_lookup[user] + if(user_role) + .["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes, "hud_icon" = user_role.hud_icon, "revealed_icon" = user_role.revealed_icon) + var/actions = list() + for(var/action in user_role.actions) + if(user_role.validate_action_target(src,action,null)) + actions += action + .["actions"] = actions + .["role_theme"] = user_role.special_theme + else + var/list/lobby_data = list() + for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup) + var/list/lobby_member = list() + lobby_member["name"] = key + lobby_member["status"] = "Ready" + if(key in GLOB.mafia_bad_signup) + lobby_member["status"] = "Disconnected" + lobby_member["spectating"] = "Ghost" + if(key in spectators) + lobby_member["spectating"] = "Spectator" + lobby_data += list(lobby_member) + .["lobbydata"] = lobby_data + var/list/player_data = list() + for(var/datum/mafia_role/R in all_roles) + var/list/player_info = list() + var/list/actions = list() + if(user_role) //not observer + for(var/action in user_role.targeted_actions) + if(user_role.validate_action_target(src,action,R)) + actions += action + //Awful snowflake, could use generalizing + if(phase == MAFIA_PHASE_VOTING) + player_info["votes"] = get_vote_count(R,"Day") + if(R.game_status == MAFIA_ALIVE && R != user_role) + actions += "Vote" + if(phase == MAFIA_PHASE_NIGHT && user_role.team == MAFIA_TEAM_MAFIA && R.game_status == MAFIA_ALIVE && R.team != MAFIA_TEAM_MAFIA) + actions += "Kill Vote" + player_info["name"] = R.body.real_name + player_info["ref"] = REF(R) + player_info["actions"] = actions + player_info["alive"] = R.game_status == MAFIA_ALIVE + player_data += list(player_info) + .["players"] = player_data + .["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0 + + //Not sure on this, should this info be visible + .["all_roles"] = current_setup_text + +/datum/mafia_controller/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/mafia), + ) + +/datum/mafia_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/datum/mafia_role/user_role = player_role_lookup[usr] + //Admin actions + if(usr.client?.holder) + switch(action) + if("new_game") + end_game() + basic_setup() + if("nuke") + end_game() + qdel(src) + if("next_phase") + var/datum/timedevent/timer = SStimer.timer_id_dict[next_phase_timer] + if(!timer.spent) + var/datum/callback/tc = timer.callBack + deltimer(next_phase_timer) + tc.InvokeAsync() + return TRUE + if("players_home") + var/list/failed = list() + for(var/datum/mafia_role/player in all_roles) + if(!player.body) + failed += player + continue + player.body.forceMove(get_turf(player.assigned_landmark)) + if(failed.len) + to_chat(usr, "List of players who no longer had a body (if you see this, the game is runtiming anyway so just hit \"New Game\" to end it)") + for(var/i in failed) + var/datum/mafia_role/fail = i + to_chat(usr, fail.player_key) + if("debug_setup") + var/list/debug_setup = list() + var/list/rolelist_dict = list() + var/done = FALSE + for(var/p in typesof(/datum/mafia_role)) + var/datum/mafia_role/path = p + rolelist_dict[initial(path.name) + " ([uppertext(initial(path.team))])"] = path + rolelist_dict = list("CANCEL", "FINISH") + rolelist_dict + while(!done) + to_chat(usr, "You have a total player count of [assoc_value_sum(debug_setup)] in this setup.") + var/chosen_role_name = input(usr,"Select a role!","Custom Setup Creation",rolelist_dict[1]) as null|anything in rolelist_dict + if(chosen_role_name == "CANCEL") + return + if(chosen_role_name == "FINISH") + break + var/found_path = rolelist_dict[chosen_role_name] + var/role_count = input(usr,"How many? Zero to cancel.","Custom Setup Creation",0) as null|num + if(role_count > 0) + debug_setup[found_path] = role_count + custom_setup = debug_setup + if("cancel_setup") + custom_setup = list() + switch(action) //both living and dead + if("mf_lookup") + var/role_lookup = params["atype"] + var/datum/mafia_role/helper + for(var/datum/mafia_role/role in all_roles) + if(role_lookup == role.name) + helper = role + break + helper.show_help(usr) + if(!user_role)//just the dead + var/client/C = ui.user.client + switch(action) + if("mf_signup") + if(!SSticker.HasRoundStarted()) + to_chat(usr, "Wait for the round to start.") + return + if(GLOB.mafia_signup[C.ckey]) + GLOB.mafia_signup -= C.ckey + to_chat(usr, "You unregister from Mafia.") + return + else + GLOB.mafia_signup[C.ckey] = C + to_chat(usr, "You sign up for Mafia.") + if(phase == MAFIA_PHASE_SETUP) + check_signups() + try_autostart() + if("mf_spectate") + if(C.ckey in spectators) + to_chat(usr, "You will no longer get messages from the game.") + spectators -= C.ckey + else + to_chat(usr, "You will now get messages from the game.") + spectators += C.ckey + if(user_role.game_status == MAFIA_DEAD) + return + //User actions (just living) + switch(action) + if("mf_action") + if(!user_role.actions.Find(params["atype"])) + return + user_role.handle_action(src,params["atype"],null) + return TRUE //vals for self-ui update + if("mf_targ_action") + var/datum/mafia_role/target = locate(params["target"]) in all_roles + if(!istype(target)) + return + switch(params["atype"]) + if("Vote") + if(phase != MAFIA_PHASE_VOTING) + return + vote_for(user_role,target,vote_type="Day") + if("Kill Vote") + if(phase != MAFIA_PHASE_NIGHT || user_role.team != MAFIA_TEAM_MAFIA) + return + vote_for(user_role,target,"Mafia", MAFIA_TEAM_MAFIA) + to_chat(user_role.body,"You will vote for [target.body.real_name] for tonights killing.") + else + if(!user_role.targeted_actions.Find(params["atype"])) + return + if(!user_role.validate_action_target(src,params["atype"],target)) + return + user_role.handle_action(src,params["atype"],target) + return TRUE + if(user_role != on_trial) + switch(action) + if("vote_abstain") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes)) + return + to_chat(user_role.body,"You have decided to abstain.") + judgement_innocent_votes -= user_role + judgement_guilty_votes -= user_role + judgement_abstain_votes += user_role + if("vote_innocent") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes)) + return + to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!") + judgement_abstain_votes -= user_role//no fakers, and... + judgement_guilty_votes -= user_role//no radical centrism + judgement_innocent_votes += user_role + if("vote_guilty") + if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes)) + return + to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!") + judgement_abstain_votes -= user_role//no fakers, and... + judgement_innocent_votes -= user_role//no radical centrism + judgement_guilty_votes += user_role + +/datum/mafia_controller/ui_state(mob/user) + return GLOB.always_state + +/datum/mafia_controller/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, null) + if(!ui) + ui = new(user, src, "MafiaPanel") + ui.set_autoupdate(FALSE) + ui.open() + +/proc/assoc_value_sum(list/L) + . = 0 + for(var/key in L) + . += L[key] + +/** + * Returns a semirandom setup, with... + * Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town. + * Mafia, 2 normal mafia and one special. + * Neutral, two disruption roles, sometimes one is a killing. + * + * See _defines.dm in the mafia folder for a rundown on what these groups of roles include. + */ +/datum/mafia_controller/proc/generate_random_setup() + var/invests_left = 2 + var/protects_left = 1 + var/miscs_left = prob(35) + var/mafiareg_left = 2 + var/mafiaspe_left = 1 + var/killing_role = prob(50) + var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow + var/overflow_left = max_player - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors) + + var/list/random_setup = list() + for(var/i in 1 to max_player) //should match the number of roles to add + if(overflow_left) + add_setup_role(random_setup, TOWN_OVERFLOW) + overflow_left-- + else if(invests_left) + add_setup_role(random_setup, TOWN_INVEST) + invests_left-- + else if(protects_left) + add_setup_role(random_setup, TOWN_PROTECT) + protects_left-- + else if(miscs_left) + add_setup_role(random_setup, TOWN_MISC) + miscs_left-- + else if(mafiareg_left) + add_setup_role(random_setup, MAFIA_REGULAR) + mafiareg_left-- + else if(mafiaspe_left) + add_setup_role(random_setup, MAFIA_SPECIAL) + mafiaspe_left-- + else if(killing_role) + add_setup_role(random_setup, NEUTRAL_KILL) + killing_role-- + else + add_setup_role(random_setup, NEUTRAL_DISRUPT) + return random_setup + +/** + * Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one + */ +/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type) + var/list/role_type_paths = list() + for(var/path in typesof(/datum/mafia_role)) + var/datum/mafia_role/instance = path + if(initial(instance.role_type) == wanted_role_type) + role_type_paths += instance + + var/mafia_path = pick(role_type_paths) + var/datum/mafia_role/mafia_path_type = mafia_path + var/found_role + for(var/searched_path in setup_list) + var/datum/mafia_role/searched_path_type = searched_path + if(initial(mafia_path_type.name) == initial(searched_path_type.name)) + found_role = searched_path + break + if(found_role) + setup_list[found_role] += 1 + return + setup_list[mafia_path] = 1 + +/** + * Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START. + * + * Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game. + * Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list. + * If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real. + */ +/datum/mafia_controller/proc/basic_setup() + var/req_players + var/list/setup = custom_setup + if(!setup.len) + req_players = required_player //max_player + else + req_players = assoc_value_sum(setup) + + //final list for all the players who will be in this game + var/list/filtered_keys = list() + //cuts invalid players from signups (disconnected/not a ghost) + var/list/possible_keys = list() + for(var/key in GLOB.mafia_signup) + if(GLOB.directory[key]) + var/client/C = GLOB.directory[key] + if(isobserver(C.mob)) + possible_keys += key + continue + GLOB.mafia_signup -= key //not valid to play when we checked so remove them from signups + + //if there were not enough players, don't start. we already trimmed the list to now hold only valid signups + if(length(possible_keys) < req_players) + return + else //hacky implementation of max players + req_players = clamp(length(possible_keys), 1, max_player) + + //if there were too many players, still start but only make filtered keys as big as it needs to be (cut excess) + //also removes people who do get into final player list from the signup so they have to sign up again when game ends + for(var/i in 1 to req_players) + var/chosen_key = pick_n_take(possible_keys) + filtered_keys += chosen_key + GLOB.mafia_signup -= chosen_key + //small message about not getting into this game for clarity on why they didn't get in + for(var/unpicked in possible_keys) + var/client/unpicked_client = GLOB.directory[unpicked] + to_chat(unpicked_client, "Sorry, the starting mafia game has too many players and you were not picked.") + to_chat(unpicked_client, "You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.") + + if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start. + setup = generate_random_setup() + prepare_game(setup,filtered_keys) + start_game() + +/** + * Called when someone signs up, and sees if there are enough people in the signup list to begin. + * + * Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup) + */ +/datum/mafia_controller/proc/try_autostart() + if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME)) + return + if(GLOB.mafia_signup.len >= max_player || GLOB.mafia_signup.len >= required_player|| custom_setup.len)//enough people to try and make something (or debug mode) + basic_setup() + +/** + * Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts. + * + * If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that. + */ +/datum/mafia_controller/proc/check_signups() + for(var/bad_key in GLOB.mafia_bad_signup) + if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client + GLOB.mafia_bad_signup -= bad_key + GLOB.mafia_signup += bad_key + for(var/key in GLOB.mafia_signup) + var/client/C = GLOB.directory[key] + if(!C)//vice versa but in a variable we use later + GLOB.mafia_signup -= key + GLOB.mafia_bad_signup += key + if(!isobserver(C.mob)) + //they are back to playing the game, remove them from the signups + GLOB.mafia_signup -= key + +/datum/action/innate/mafia_panel + name = "Mafia Panel" + desc = "Use this to play." + icon_icon = 'icons/obj/mafia.dmi' + button_icon_state = "board" + var/datum/mafia_controller/parent + +/datum/action/innate/mafia_panel/New(Target,mf) + . = ..() + parent = mf + +/datum/action/innate/mafia_panel/Activate() + parent.ui_interact(owner) + +/** + * Creates the global datum for playing mafia games, destroys the last if that's required and returns the new. + */ +/proc/create_mafia_game() + if(GLOB.mafia_game) + QDEL_NULL(GLOB.mafia_game) + var/datum/mafia_controller/MF = new() + return MF diff --git a/code/modules/mafia/map_pieces.dm b/code/modules/mafia/map_pieces.dm new file mode 100644 index 0000000000..3339c596b4 --- /dev/null +++ b/code/modules/mafia/map_pieces.dm @@ -0,0 +1,79 @@ +/obj/effect/landmark/mafia_game_area //locations where mafia will be loaded by the datum + name = "Mafia Area Spawn" + var/game_id = "mafia" + +/obj/effect/landmark/mafia + name = "Mafia Player Spawn" + var/game_id = "mafia" + +/obj/effect/landmark/mafia/town_center + name = "Mafia Town Center" + +//for ghosts/admins +/obj/mafia_game_board + name = "Mafia Game Board" + icon = 'icons/obj/mafia.dmi' + icon_state = "board" + anchored = TRUE + var/game_id = "mafia" + var/datum/mafia_controller/MF + +/obj/mafia_game_board/attack_ghost(mob/user) + . = ..() + if(!MF) + MF = GLOB.mafia_game + if(!MF) + MF = create_mafia_game() + MF.ui_interact(user) + +/area/mafia + name = "Mafia Minigame" + icon_state = "mafia" + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + requires_power = FALSE + has_gravity = STANDARD_GRAVITY + flags_1 = NONE + // block_suicide = TRUE + +/datum/map_template/mafia + var/description = "" + +/datum/map_template/mafia/summerball + name = "Summerball 2020" + description = "The original, the OG. The 2020 Summer ball was where mafia came from, with this map." + mappath = "_maps/map_files/Mafia/mafia_ball.dmm" + +/datum/map_template/mafia/syndicate + name = "Syndicate Megastation" + description = "Yes, it's a very confusing day at the Megastation. Will the syndicate conflict resolution operatives succeed?" + mappath = "_maps/map_files/Mafia/mafia_syndie.dmm" + +/datum/map_template/mafia/lavaland + name = "Lavaland Excursion" + description = "The station has no idea what's going down on lavaland right now, we got changelings... traitors, and worst of all... lawyers roleblocking you every night." + mappath = "_maps/map_files/Mafia/mafia_lavaland.dmm" + +/datum/map_template/mafia/ufo + name = "Alien Mothership" + description = "The haunted ghost UFO tour has gone south and now it's up to our fine townies and scare seekers to kill the actual real alien changelings..." + mappath = "_maps/map_files/Mafia/mafia_ayylmao.dmm" + +/datum/map_template/mafia/spider_clan + name = "Spider Clan Kidnapping" + description = "New and improved spider clan kidnappings are a lot less boring and have a lot more lynching. Damn westaboos!" + mappath = "_maps/map_files/Mafia/mafia_spiderclan.dmm" + +/datum/map_template/mafia/snowy + name = "Snowdin" + description = "Based off of the icey moon map of the same name, the guy who reworked it pretty much did it for nothing since away missions are disabled but at least he'll get this...?" + mappath = "_maps/map_files/Mafia/mafia_snow.dmm" + +/datum/map_template/mafia/gothic + name = "Vampire's Castle" + description = "Vampires and changelings clash to find out who's the superior bloodsucking monster in this creepy castle map." + mappath = "_maps/map_files/Mafia/mafia_gothic.dmm" + +/datum/map_template/mafia/reebe + name = "Reebe" + description = "Trouble in Reebe station! Copypaste guranteed by ClockCo™" + mappath = "_maps/map_files/Mafia/mafia_reebe.dmm" diff --git a/code/modules/mafia/outfits.dm b/code/modules/mafia/outfits.dm new file mode 100644 index 0000000000..bbc72bd120 --- /dev/null +++ b/code/modules/mafia/outfits.dm @@ -0,0 +1,108 @@ + +//what people wear unrevealed + +/datum/outfit/mafia + name = "Mafia Game Outfit" + uniform = /obj/item/clothing/under/color/grey + shoes = /obj/item/clothing/shoes/sneakers/black + +//town + +/datum/outfit/mafia/assistant + name = "Mafia Assistant" + + uniform = /obj/item/clothing/under/color/rainbow + +/datum/outfit/mafia/detective + name = "Mafia Detective" + + uniform = /obj/item/clothing/under/rank/security/detective + // neck = /obj/item/clothing/neck/tie/detective + shoes = /obj/item/clothing/shoes/sneakers/brown + suit = /obj/item/clothing/suit/det_suit + gloves = /obj/item/clothing/gloves/color/black + head = /obj/item/clothing/head/fedora/det_hat + mask = /obj/item/clothing/mask/cigarette + +/datum/outfit/mafia/psychologist + name = "Mafia Psychologist" + + uniform = /obj/item/clothing/under/suit/black + shoes = /obj/item/clothing/shoes/laceup + +/datum/outfit/mafia/md + name = "Mafia Medical Doctor" + + uniform = /obj/item/clothing/under/rank/medical/doctor + shoes = /obj/item/clothing/shoes/sneakers/white + suit = /obj/item/clothing/suit/toggle/labcoat + +/datum/outfit/mafia/chaplain + name = "Mafia Chaplain" + + uniform = /obj/item/clothing/under/rank/civilian/chaplain + +/datum/outfit/mafia/lawyer + name = "Mafia Lawyer" + + uniform = /obj/item/clothing/under/rank/civilian/lawyer/bluesuit + suit = /obj/item/clothing/suit/toggle/lawyer + shoes = /obj/item/clothing/shoes/laceup + +/datum/outfit/mafia/hop + name = "Mafia Head of Personnel" + + uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel + suit = /obj/item/clothing/suit/armor/vest/alt + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/hopcap + glasses = /obj/item/clothing/glasses/sunglasses + +//mafia + +/datum/outfit/mafia/changeling + name = "Mafia Changeling" + + head = /obj/item/clothing/head/helmet/changeling + suit = /obj/item/clothing/suit/armor/changeling + +//solo + +/datum/outfit/mafia/fugitive + name = "Mafia Fugitive" + + uniform = /obj/item/clothing/under/rank/prisoner + shoes = /obj/item/clothing/shoes/sneakers/orange + +/datum/outfit/mafia/obsessed + name = "Mafia Obsessed" + uniform = /obj/item/clothing/under/misc/overalls + shoes = /obj/item/clothing/shoes/sneakers/white + gloves = /obj/item/clothing/gloves/color/latex + mask = /obj/item/clothing/mask/surgical + suit = /obj/item/clothing/suit/apron + +/datum/outfit/mafia/obsessed/post_equip(mob/living/carbon/human/H) + for(var/obj/item/carried_item in H.get_equipped_items(TRUE)) + carried_item.add_mob_blood(H)//Oh yes, there will be blood... + H.regenerate_icons() + +/datum/outfit/mafia/clown + name = "Mafia Clown" + + uniform = /obj/item/clothing/under/rank/civilian/clown + shoes = /obj/item/clothing/shoes/clown_shoes + mask = /obj/item/clothing/mask/gas/clown_hat + +/datum/outfit/mafia/traitor + name = "Mafia Traitor" + + mask = /obj/item/clothing/mask/gas/syndicate + uniform = /obj/item/clothing/under/syndicate/tacticool + shoes = /obj/item/clothing/shoes/jackboots + +/datum/outfit/mafia/nightmare + name = "Mafia Nightmare" + + uniform = null + shoes = null diff --git a/code/modules/mafia/roles.dm b/code/modules/mafia/roles.dm new file mode 100644 index 0000000000..2461a93976 --- /dev/null +++ b/code/modules/mafia/roles.dm @@ -0,0 +1,705 @@ +/datum/mafia_role + var/name = "Assistant" + var/desc = "You are a crewmember without any special abilities." + var/win_condition = "kill all mafia and solo killing roles." + var/team = MAFIA_TEAM_TOWN + ///how the random setup chooses which roles get put in + var/role_type = TOWN_OVERFLOW + + var/player_key + var/mob/living/carbon/human/body + var/obj/effect/landmark/mafia/assigned_landmark + + ///how many votes submitted when you vote. + var/vote_power = 1 + var/detect_immune = FALSE + var/revealed = FALSE + var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment + //action = uses + var/list/actions = list() + var/list/targeted_actions = list() + //what the role gets when it wins a game + // var/winner_award = /datum/award/achievement/mafia/assistant + + //so mafia have to also kill them to have a majority + var/solo_counts_as_town = FALSE //(don't set this for town) + var/game_status = MAFIA_ALIVE + + ///icon state in the mafia dmi of the hud of the role, used in the mafia ui + var/hud_icon = "hudassistant" + ///icon state in the mafia dmi of the hud of the role, used in the mafia ui + var/revealed_icon = "assistant" + ///set this to something cool for antagonists and their window will look different + var/special_theme + + var/list/role_notes = list() + + +/datum/mafia_role/New(datum/mafia_controller/game) + . = ..() + +/datum/mafia_role/proc/kill(datum/mafia_controller/game,lynch=FALSE) + if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,lynch) & MAFIA_PREVENT_KILL) + return FALSE + game_status = MAFIA_DEAD + body.death() + if(lynch) + reveal_role(game, verbose = TRUE) + if(!(player_key in game.spectators)) //people who played will want to see the end of the game more often than not + game.spectators += player_key + return TRUE + +/datum/mafia_role/Destroy(force, ...) + QDEL_NULL(body) + . = ..() + +/datum/mafia_role/proc/greet() + SEND_SOUND(body, 'sound/ambience/ambifailure.ogg') + to_chat(body,"You are the [name].") + to_chat(body,"[desc]") + switch(team) + if(MAFIA_TEAM_MAFIA) + to_chat(body,"You and your co-conspirators win if you outnumber crewmembers.") + if(MAFIA_TEAM_TOWN) + to_chat(body,"You are a crewmember. Find out and lynch the changelings!") + if(MAFIA_TEAM_SOLO) + to_chat(body,"You are not aligned to town or mafia. Accomplish your own objectives!") + to_chat(body, "Be sure to read the wiki page to learn more, if you have no idea what's going on.") + +/datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE) + if(revealed) + return + if(verbose) + game.send_message("It is revealed that the true role of [body] [game_status == MAFIA_ALIVE ? "is" : "was"] [name]!") + var/list/oldoutfit = body.get_equipped_items() + for(var/thing in oldoutfit) + qdel(thing) + special_reveal_equip(game) + body.equipOutfit(revealed_outfit) + revealed = TRUE + +/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game) + return + +/datum/mafia_role/proc/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + return + +/datum/mafia_role/proc/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,action,target) & MAFIA_PREVENT_ACTION) + return FALSE + return TRUE + +/datum/mafia_role/proc/add_note(note) + role_notes += note + +/datum/mafia_role/proc/check_total_victory(alive_town, alive_mafia) //solo antags can win... solo. + return FALSE + +/datum/mafia_role/proc/block_team_victory(alive_town, alive_mafia) //solo antags can also block team wins. + return FALSE + +/datum/mafia_role/proc/show_help(clueless) + var/list/result = list() + var/team_desc = "" + var/team_span = "" + var/the = TRUE + switch(team) + if(MAFIA_TEAM_TOWN) + team_desc = "Town" + team_span = "nicegreen" + if(MAFIA_TEAM_MAFIA) + team_desc = "Mafia" + team_span = "red" + if(MAFIA_TEAM_SOLO) + team_desc = "Nobody" + team_span = "comradio" + the = FALSE + result += "The [name] is aligned with [the ? "the " : ""][team_desc]" + result += "\"[desc]\"" + result += "[name] wins when they [win_condition]" + to_chat(clueless, result.Join("
")) + +/datum/mafia_role/detective + name = "Detective" + desc = "You can investigate a single person each night to learn their team." + revealed_outfit = /datum/outfit/mafia/detective + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/detective + + hud_icon = "huddetective" + revealed_icon = "detective" + + targeted_actions = list("Investigate") + + var/datum/mafia_role/current_investigation + +/datum/mafia_role/detective/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate) + +/datum/mafia_role/detective/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/detective/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + if(!target || target.game_status != MAFIA_ALIVE) + to_chat(body,"You can only investigate alive people.") + return + to_chat(body,"You will investigate [target.body.real_name] tonight.") + current_investigation = target + +/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game) + var/datum/mafia_role/target = current_investigation + if(target) + if(target.detect_immune) + to_chat(body,"Your investigations reveal that [target.body.real_name] is a true member of the station.") + add_note("N[game.turn] - [target.body.real_name] - Town") + else + var/team_text + var/fluff + switch(target.team) + if(MAFIA_TEAM_TOWN) + team_text = "Town" + fluff = "a true member of the station." + if(MAFIA_TEAM_MAFIA) + team_text = "Mafia" + fluff = "an unfeeling, hideous changeling!" + if(MAFIA_TEAM_SOLO) + team_text = "Solo" + fluff = "a rogue, with their own objectives..." + to_chat(body,"Your investigations reveal that [target.body.real_name] is [fluff]") + add_note("N[game.turn] - [target.body.real_name] - [team_text]") + current_investigation = null + +/datum/mafia_role/psychologist + name = "Psychologist" + desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!" + revealed_outfit = /datum/outfit/mafia/psychologist + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/psychologist + + hud_icon = "hudpsychologist" + revealed_icon = "psychologist" + + targeted_actions = list("Reveal") + var/datum/mafia_role/current_target + var/can_use = TRUE + +/datum/mafia_role/psychologist/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal) + +/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src) + return FALSE + +/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + to_chat(body,"You will reveal [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer. + current_target = null + if(current_target) + add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity") + to_chat(body,"You have revealed the true nature of the [current_target]!") + current_target.reveal_role(game, verbose = TRUE) + current_target = null + can_use = FALSE + +/datum/mafia_role/chaplain + name = "Chaplain" + desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles." + revealed_outfit = /datum/outfit/mafia/chaplain + role_type = TOWN_INVEST + hud_icon = "hudchaplain" + revealed_icon = "chaplain" + // winner_award = /datum/award/achievement/mafia/chaplain + + targeted_actions = list("Pray") + var/current_target + +/datum/mafia_role/chaplain/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune) + +/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed + +/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + to_chat(body,"You will commune with the spirit of [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game) + var/datum/mafia_role/target = current_target + if(target) + to_chat(body,"You invoke spirit of [target.body.real_name] and learn their role was [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + current_target = null + +/datum/mafia_role/md + name = "Medical Doctor" + desc = "You can protect a single person each night from killing." + revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + role_type = TOWN_PROTECT + hud_icon = "hudmedicaldoctor" + revealed_icon = "medicaldoctor" + // winner_award = /datum/award/achievement/mafia/md + + targeted_actions = list("Protect") + var/datum/mafia_role/current_protected + +/datum/mafia_role/md/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/protect) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/end_protection) + +/datum/mafia_role/md/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + if(target.name == "Head of Personnel" && target.revealed) + return FALSE + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + if(!target || target.game_status != MAFIA_ALIVE) + to_chat(body,"You can only protect alive people.") + return + to_chat(body,"You will protect [target.body.real_name] tonight.") + current_protected = target + +/datum/mafia_role/md/proc/protect(datum/mafia_controller/game) + if(current_protected) + RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill) + add_note("N[game.turn] - Protected [current_protected.body.real_name]") + +/datum/mafia_role/md/proc/prevent_kill(datum/source) + to_chat(body,"The person you protected tonight was attacked!") + to_chat(current_protected.body,"You were attacked last night, but someone nursed you back to life!") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game) + if(current_protected) + UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL) + current_protected = null + +/datum/mafia_role/lawyer + name = "Lawyer" + desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions." + revealed_outfit = /datum/outfit/mafia/lawyer + role_type = TOWN_PROTECT + hud_icon = "hudlawyer" + revealed_icon = "lawyer" + // winner_award = /datum/award/achievement/mafia/lawyer + + targeted_actions = list("Advise") + var/datum/mafia_role/current_target + +/datum/mafia_role/lawyer/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock_text) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_START,.proc/try_to_roleblock) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release) + +/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer. + current_target = null + if(current_target) + to_chat(current_target.body,"YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.") + add_note("N[game.turn] - [current_target.body.real_name] - Blocked") + +/datum/mafia_role/lawyer/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return FALSE + if(game.phase == MAFIA_PHASE_NIGHT) + return FALSE + if(target.game_status != MAFIA_ALIVE) + return FALSE + +/datum/mafia_role/lawyer/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(target == current_target) + current_target = null + to_chat(body,"You have decided against blocking anyone tonight.") + else + current_target = target + to_chat(body,"You will block [target.body.real_name] tonight.") + +/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game) + if(current_target) + RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action) + +/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game) + . = ..() + if(current_target) + UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION) + current_target = null + +/datum/mafia_role/lawyer/proc/prevent_action(datum/source) + if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge + return MAFIA_PREVENT_ACTION + +/datum/mafia_role/hop + name = "Head of Personnel" + desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!" + revealed_outfit = /datum/outfit/mafia/hop + role_type = TOWN_MISC + hud_icon = "hudheadofpersonnel" + revealed_icon = "headofpersonnel" + // winner_award = /datum/award/achievement/mafia/hop + + targeted_actions = list("Reveal") + +/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed) + return FALSE + +/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + reveal_role(game, TRUE) + vote_power = 2 + +///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win + +/datum/mafia_role/mafia + name = "Changeling" + desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings." + team = MAFIA_TEAM_MAFIA + role_type = MAFIA_REGULAR + hud_icon = "hudchangeling" + revealed_icon = "changeling" + // winner_award = /datum/award/achievement/mafia/changeling + + revealed_outfit = /datum/outfit/mafia/changeling + special_theme = "syndicate" + win_condition = "become majority over the town and no solo killing role can stop them." + +/datum/mafia_role/mafia/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text) + +/datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source) + to_chat(body,"Vote for who to kill tonight. The killer will be chosen randomly from voters.") + +//better detective for mafia +/datum/mafia_role/mafia/thoughtfeeder + name = "Thoughtfeeder" + desc = "You're a changeling variant that feeds on the memories of others. Use ':j' talk prefix to talk to your fellow lings, and visit people at night to learn their role." + role_type = MAFIA_SPECIAL + hud_icon = "hudthoughtfeeder" + revealed_icon = "thoughtfeeder" + // winner_award = /datum/award/achievement/mafia/thoughtfeeder + + targeted_actions = list("Learn Role") + var/datum/mafia_role/current_investigation + +/datum/mafia_role/mafia/thoughtfeeder/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate) + +/datum/mafia_role/mafia/thoughtfeeder/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + to_chat(body,"You will feast on the memories of [target.body.real_name] tonight.") + current_investigation = target + +/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game) + var/datum/mafia_role/target = current_investigation + current_investigation = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION) + to_chat(body,"You were unable to investigate [target.body.real_name].") + add_note("N[game.turn] - [target.body.real_name] - Unable to investigate") + return + if(target) + if(target.detect_immune) + to_chat(body,"[target.body.real_name]'s memories reveal that they are the Assistant.") + add_note("N[game.turn] - [target.body.real_name] - Assistant") + else + to_chat(body,"[target.body.real_name]'s memories reveal that they are the [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + + +///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone. + +/datum/mafia_role/traitor + name = "Traitor" + desc = "You're a solo traitor. You are immune to night kills, can kill every night and you win by outnumbering everyone else." + win_condition = "kill everyone." + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + // winner_award = /datum/award/achievement/mafia/traitor + + targeted_actions = list("Night Kill") + revealed_outfit = /datum/outfit/mafia/traitor + + hud_icon = "hudtraitor" + revealed_icon = "traitor" + special_theme = "neutral" + + var/datum/mafia_role/current_victim + +/datum/mafia_role/traitor/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/nightkill_immunity) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/try_to_kill) + +/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead + return alive_town + alive_mafia <= 1 + +/datum/mafia_role/traitor/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead + return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) + +/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch) + if(game.phase == MAFIA_PHASE_NIGHT && !lynch) + to_chat(body,"You were attacked, but they'll have to try harder than that to put you down.") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/traitor/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return FALSE + if(game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target == src) + return FALSE + +/datum/mafia_role/traitor/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + current_victim = target + to_chat(body,"You will attempt to kill [target.body.real_name] tonight.") + +/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source) + var/datum/mafia_role/target = current_victim + current_victim = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION) + return + if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE) + if(!target.kill(source)) + to_chat(body,"Your attempt at killing [target.body] was prevented!") + +/datum/mafia_role/nightmare + name = "Nightmare" + desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win." + win_condition = "kill everyone." + revealed_outfit = /datum/outfit/mafia/nightmare + detect_immune = TRUE + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + special_theme = "neutral" + hud_icon = "hudnightmare" + revealed_icon = "nightmare" + // winner_award = /datum/award/achievement/mafia/nightmare + + targeted_actions = list("Flicker", "Hunt") + var/list/flickering = list() + var/datum/mafia_role/flicker_target + +/datum/mafia_role/nightmare/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt) + +/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead + return alive_town + alive_mafia <= 1 + +/datum/mafia_role/nightmare/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead + return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) + +/datum/mafia_role/nightmare/special_reveal_equip() + body.underwear = "Nude" + body.undershirt = "Nude" + body.socks = "Nude" + body.set_species(/datum/species/shadow) + body.update_body() + +/datum/mafia_role/nightmare/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE) + return FALSE + if(action == "Flicker") + return target != src && !(target in flickering) + return target == src + +/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(target == flicker_target) + to_chat(body,"You will do nothing tonight.") + flicker_target = null + flicker_target = target + if(action == "Flicker") + to_chat(body,"You will attempt to flicker [target.body.real_name]'s room tonight.") + else + to_chat(body,"You will hunt everyone in a flickering room down tonight.") + +/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source) + if(game_status != MAFIA_ALIVE || !flicker_target) + return + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION) + to_chat(flicker_target.body, "Your actions were prevented!") + return + var/datum/mafia_role/target = flicker_target + flicker_target = null + if(target != src) //flicker instead of hunt + to_chat(target.body, "The lights begin to flicker and dim. You're in danger.") + flickering += target + return + for(var/r in flickering) + var/datum/mafia_role/role = r + if(role && role.game_status == MAFIA_ALIVE) + to_chat(role.body, "A shadowy monster appears out of the darkness!") + role.kill(source) + flickering -= role + +//just helps read better +#define FUGITIVE_NOT_PRESERVING 0//will not become night immune tonight +#define FUGITIVE_WILL_PRESERVE 1 //will become night immune tonight + +/datum/mafia_role/fugitive + name = "Fugitive" + desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone." + win_condition = "survive to the end of the game, with anyone" + solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town + revealed_outfit = /datum/outfit/mafia/fugitive + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudfugitive" + revealed_icon = "fugitive" + // winner_award = /datum/award/achievement/mafia/fugitive + + actions = list("Self Preservation") + var/charges = 2 + var/protection_status = FUGITIVE_NOT_PRESERVING + + +/datum/mafia_role/fugitive/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/night_start) + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/night_end) + RegisterSignal(game,COMSIG_MAFIA_GAME_END,.proc/survived) + +/datum/mafia_role/fugitive/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!charges) + to_chat(body,"You're out of supplies and cannot protect yourself anymore.") + return + if(game.phase == MAFIA_PHASE_NIGHT) + to_chat(body,"You don't have time to prepare, night has already arrived.") + return + if(protection_status == FUGITIVE_WILL_PRESERVE) + to_chat(body,"You decide to not prepare tonight.") + else + to_chat(body,"You decide to prepare for a horrible night.") + protection_status = !protection_status + +/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game) + if(protection_status == FUGITIVE_WILL_PRESERVE) + to_chat(body,"Your preparations are complete. Nothing could kill you tonight!") + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death) + +/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game) + if(protection_status == FUGITIVE_WILL_PRESERVE) + charges-- + UnregisterSignal(src,COMSIG_MAFIA_ON_KILL) + to_chat(body,"You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power.") + protection_status = FUGITIVE_NOT_PRESERVING + +/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game) + to_chat(body,"You were attacked! Luckily, you were ready for this!") + return MAFIA_PREVENT_KILL + +/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game) + if(game_status == MAFIA_ALIVE) + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + game.send_message("!! FUGITIVE VICTORY !!") + +#undef FUGITIVE_NOT_PRESERVING +#undef FUGITIVE_WILL_PRESERVE + +/datum/mafia_role/obsessed + name = "Obsessed" + desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!" + win_condition = "lynch their obsession." + revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudobsessed" + revealed_icon = "obsessed" + + // winner_award = /datum/award/achievement/mafia/obsessed + + revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! + var/datum/mafia_role/obsession + var/lynched_target = FALSE + +/datum/mafia_role/obsessed/New(datum/mafia_controller/game) //note: obsession is always a townie + . = ..() + RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession) + +/datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game) + var/list/all_roles_shuffle = shuffle(game.all_roles) + for(var/role in all_roles_shuffle) + var/datum/mafia_role/possible = role + if(possible.team == MAFIA_TEAM_TOWN && possible.game_status != MAFIA_DEAD) + obsession = possible + break + if(!obsession) + obsession = pick(all_roles_shuffle) //okay no town just pick anyone here + //if you still don't have an obsession you're playing a single player game like i can't help your dumb ass + to_chat(body, "Your obsession is [obsession.body.real_name]! Get them lynched to win!") + add_note("N[game.turn] - I vowed to watch my obsession, [obsession.body.real_name], hang!") //it'll always be N1 but whatever + RegisterSignal(obsession,COMSIG_MAFIA_ON_KILL,.proc/check_victory) + UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN) + +/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch) + UnregisterSignal(source,COMSIG_MAFIA_ON_KILL) + if(game_status == MAFIA_DEAD) + return + if(lynch) + game.send_message("!! OBSESSED VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + reveal_role(game, FALSE) + else + to_chat(body, "You have failed your objective to lynch [obsession.body]!") + +/datum/mafia_role/clown + name = "Clown" + desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!" + win_condition = "get themselves lynched!" + revealed_outfit = /datum/outfit/mafia/clown + solo_counts_as_town = TRUE + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT + special_theme = "neutral" + hud_icon = "hudclown" + revealed_icon = "clown" + // winner_award = /datum/award/achievement/mafia/clown + +/datum/mafia_role/clown/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank) + +/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch) + if(lynch) + var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes) + game.send_message("[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.") + game.send_message("!! CLOWN VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) + victim.kill(game,FALSE) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index a09d890f69..a6f456ce6f 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -183,7 +183,6 @@ attack_verb = list("stabbed", "diced", "sliced", "cleaved", "chopped", "lacerated", "cut", "jabbed", "punctured") icon_state = "crusher-glaive" item_state = "crusher0-glaive" - item_flags = ITEM_CAN_PARRY block_parry_data = /datum/block_parry_data/crusherglaive //ideas: altclick that lets you pummel people with the handguard/handle? //parrying functionality? @@ -217,6 +216,16 @@ if(owner.Adjacent(attacker) && (!attacker.anchored || ismegafauna(attacker))) // free backstab, if you perfect parry attacker.dir = get_dir(owner,attacker) +/// triggered on wield of two handed item +/obj/item/kinetic_crusher/glaive/on_wield(obj/item/source, mob/user) + wielded = TRUE + item_flags |= (ITEM_CAN_PARRY) + +/// triggered on unwield of two handed item +/obj/item/kinetic_crusher/glaive/on_unwield(obj/item/source, mob/user) + wielded = FALSE + item_flags &= ~(ITEM_CAN_PARRY) + /obj/item/kinetic_crusher/glaive/update_icon_state() item_state = "crusher[wielded]-glaive" // this is not icon_state and not supported by 2hcomponent diff --git a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm index 60e8ed1007..2c0db56ce4 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/snouts.dm @@ -184,6 +184,12 @@ extra = TRUE extra_color_src = MUTCOLORS3 +/datum/sprite_accessory/mam_snouts/skulldog + name = "Skulldog" + icon_state = "skulldog" + extra = TRUE + extra_color_src = MATRIXED + /datum/sprite_accessory/mam_snouts/lcanid name = "Mammal, Long" icon_state = "lcanid" diff --git a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm index 73233f3e09..b8282931d2 100644 --- a/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm +++ b/code/modules/mob/dead/new_player/sprite_accessories/undershirt.dm @@ -348,6 +348,12 @@ name = "Cowboy Shirt Shortsleeved Red" icon_state = "cowboyshirt_reds" +/datum/sprite_accessory/underwear/top/bra_binder + name = "Bra (binder)" + icon_state = "bra_binder" + has_color = TRUE - - +/datum/sprite_accessory/underwear/top/bra_binder_strapless + name = "Bra (binder, strapless)" + icon_state = "bra_binder_strapless" + has_color = TRUE diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index bb39639ec1..a0df1ee938 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -905,6 +905,22 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp else to_chat(usr, "Can't become a pAI candidate while not dead!") +/mob/dead/observer/verb/mafia_game_signup() + set category = "Ghost" + set name = "Signup for Mafia" + set desc = "Sign up for a game of Mafia to pass the time while dead." + mafia_signup() +/mob/dead/observer/proc/mafia_signup() + if(!client) + return + if(!isobserver(src)) + to_chat(usr, "You must be a ghost to join mafia!") + return + var/datum/mafia_controller/game = GLOB.mafia_game //this needs to change if you want multiple mafia games up at once. + if(!game) + game = create_mafia_game("mafia") + game.ui_interact(usr) + /mob/dead/observer/CtrlShiftClick(mob/user) if(isobserver(user) && check_rights(R_SPAWN)) change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index caebb9cf10..9ac270bf34 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -382,10 +382,20 @@ return 0 -//Outdated but still in use apparently. This should at least be a human proc. -//Daily reminder to murder this - Remie. +/** + * Used to return a list of equipped items on a mob; does not include held items (use get_all_gear) + * + * Argument(s): + * * Optional - include_pockets (TRUE/FALSE), whether or not to include the pockets and suit storage in the returned list + */ + /mob/living/proc/get_equipped_items(include_pockets = FALSE) - return + var/list/items = list() + for(var/obj/item/I in contents) + if(I.item_flags & IN_INVENTORY) + items += I + items -= held_items + return items /mob/living/proc/unequip_everything() var/list/items = list() @@ -476,5 +486,19 @@ hand_bodyparts[i] = BP ..() //Don't redraw hands until we have organs for them + +//GetAllContents that is reasonable and not stupid +/mob/living/carbon/proc/get_all_gear() + var/list/processing_list = get_equipped_items(include_pockets = TRUE) + held_items + listclearnulls(processing_list) // handles empty hands + var/i = 0 + while(i < length(processing_list) ) + var/atom/A = processing_list[++i] + if(SEND_SIGNAL(A, COMSIG_CONTAINS_STORAGE)) + var/list/item_stuff = list() + SEND_SIGNAL(A, COMSIG_TRY_STORAGE_RETURN_INVENTORY, item_stuff) + processing_list += item_stuff + return processing_list + /mob/canReachInto(atom/user, atom/target, list/next, view_only, obj/item/tool) return ..() && (user == src) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index de4b7a659f..18501b4ca2 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -299,10 +299,10 @@ /mob/living/carbon/human/ex_act(severity, target, origin) - if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src)) + if(TRAIT_BOMBIMMUNE in dna.species.species_traits) return ..() - if (!severity) + if (!severity || QDELETED(src)) return var/brute_loss = 0 var/burn_loss = 0 diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index a7593ca0cb..523369d10a 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,31 +1,17 @@ /mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, clothing_check = FALSE, list/return_warning) return dna.species.can_equip(I, slot, disable_warning, src, bypass_equip_delay_self, clothing_check, return_warning) +/** + * Used to return a list of equipped items on a human mob; does not include held items (use get_all_gear) + * + * Argument(s): + * * Optional - include_pockets (TRUE/FALSE), whether or not to include the pockets and suit storage in the returned list + */ + /mob/living/carbon/human/get_equipped_items(include_pockets = FALSE) var/list/items = ..() - if(belt) - items += belt - if(ears) - items += ears - if(glasses) - items += glasses - if(gloves) - items += gloves - if(shoes) - items += shoes - if(wear_id) - items += wear_id - if(wear_suit) - items += wear_suit - if(w_uniform) - items += w_uniform - if(include_pockets) - if(l_store) - items += l_store - if(r_store) - items += r_store - if(s_store) - items += s_store + if(!include_pockets) + items -= list(l_store, r_store, s_store) return items // Return the item currently in the slot ID @@ -169,7 +155,7 @@ //Item is handled and in slot, valid to call callback, for this proc should always be true if(!not_handled) I.equipped(src, slot) - + update_genitals() return not_handled //For future deeper overrides /mob/living/carbon/human/equipped_speed_mods() @@ -257,6 +243,7 @@ s_store = null if(!QDELETED(src)) update_inv_s_store() + update_genitals() /mob/living/carbon/human/wear_mask_update(obj/item/clothing/C, toggle_off = 1) if((C.flags_inv & (HIDEHAIR|HIDEFACIALHAIR)) || (initial(C.flags_inv) & (HIDEHAIR|HIDEFACIALHAIR))) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 54c3b314b9..df049f672d 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -124,6 +124,9 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) limbs_id = id ..() + //update our mutant bodyparts to include unlocked ones + mutant_bodyparts += GLOB.unlocked_mutant_parts + /proc/generate_selectable_species(clear = FALSE) if(clear) GLOB.roundstart_races = list() diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm index 27338f598f..cda6faded0 100644 --- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm +++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm @@ -14,7 +14,7 @@ brutemod = 1.25 //They're weak to punches attack_type = BURN //burn bish damage_overlay_type = "" //We are too cool for regular damage overlays - species_traits = list(MUTCOLORS, NO_UNDERWEAR, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too + species_traits = list(MUTCOLORS, HAIR, HAS_FLESH, HAS_BONE) // i mean i guess they have blood so they can have wounds too species_language_holder = /datum/language_holder/ethereal inherent_traits = list(TRAIT_NOHUNGER) sexes = FALSE diff --git a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm index d768788e87..a55952d62f 100644 --- a/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/lizardpeople.dm @@ -52,6 +52,7 @@ species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE) inherent_traits = list(TRAIT_CHUNKYFINGERS) mutantlungs = /obj/item/organ/lungs/ashwalker + mutanteyes = /obj/item/organ/eyes/night_vision burnmod = 0.9 brutemod = 0.9 species_language_holder = /datum/language_holder/lizard/ash diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 51d81bd4d3..c504e5ccd3 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -5,8 +5,8 @@ blacklisted = 0 sexes = 0 meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/skeleton - species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,HAS_BONE) - inherent_traits = list(TRAIT_NOBREATH,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NOHUNGER,TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_FAKEDEATH, TRAIT_CALCIUM_HEALER) + species_traits = list(NOBLOOD,NOGENITALS,NOAROUSAL,HAS_BONE,NOTRANSSTING) + inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_CALCIUM_HEALER) inherent_biotypes = MOB_UNDEAD|MOB_HUMANOID mutanttongue = /obj/item/organ/tongue/bone damage_overlay_type = ""//let's not show bloody wounds or burns over bones. @@ -19,7 +19,7 @@ /datum/species/skeleton/New() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season! - inherent_traits |= list(TRAIT_RESISTHEAT,TRAIT_RESISTCOLD) + inherent_traits |= list(TRAIT_RESISTHEAT, TRAIT_NOBREATH, TRAIT_PIERCEIMMUNE, TRAIT_FAKEDEATH, TRAIT_RESISTCOLD, TRAIT_RADIMMUNE) brutemod = 1 burnmod = 1 ..() diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index cc837a490c..50801e1c0d 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -117,18 +117,6 @@ if(!QDELETED(src)) update_inv_legcuffed() -/mob/living/carbon/get_equipped_items(include_pockets = FALSE) - var/list/items = list() - if(back) - items += back - if(head) - items += head - if(wear_mask) - items += wear_mask - if(wear_neck) - items += wear_neck - return items - //handle stuff to update when a mob equips/unequips a mask. /mob/living/proc/wear_mask_update(obj/item/clothing/C, toggle_off = 1) update_inv_wear_mask() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0f55d5c408..2a461b5921 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -57,7 +57,7 @@ /mob/living/Bump(atom/A) if(..()) //we are thrown onto something return - if (buckled || now_pushing) + if(buckled || now_pushing) return if(ismob(A)) var/mob/M = A diff --git a/code/modules/mob/living/living_active_block.dm b/code/modules/mob/living/living_active_block.dm index 729e7d3d7a..2493509e16 100644 --- a/code/modules/mob/living/living_active_block.dm +++ b/code/modules/mob/living/living_active_block.dm @@ -13,7 +13,7 @@ DelayNextAction(data.block_end_click_cd_add) return TRUE -/mob/living/proc/ACTIVE_BLOCK_START(obj/item/I) +/mob/living/proc/active_block_start(obj/item/I) if(combat_flags & (COMBAT_FLAG_ACTIVE_BLOCK_STARTING | COMBAT_FLAG_ACTIVE_BLOCKING)) return FALSE if(!(I in held_items)) @@ -109,7 +109,7 @@ animate(src, pixel_x = get_standard_pixel_x_offset(), pixel_y = get_standard_pixel_y_offset(), time = 2.5, FALSE, SINE_EASING | EASE_IN, ANIMATION_END_NOW) return combat_flags &= ~(COMBAT_FLAG_ACTIVE_BLOCK_STARTING) - ACTIVE_BLOCK_START(I) + active_block_start(I) /** * Gets the first item we can that can block, but if that fails, default to active held item.COMSIG_ENABLE_COMBAT_MODE @@ -180,6 +180,12 @@ /// Apply the stamina damage to our user, notice how damage argument is stamina_amount. /obj/item/proc/active_block_do_stamina_damage(mob/living/owner, atom/object, stamina_amount, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) + if(istype(object, /obj/item/projectile)) + var/obj/item/projectile/P = object + if(P.stamina) + var/blocked = active_block_calculate_final_damage(owner, object, P.stamina, attack_text, attack_type, armour_penetration, attacker, def_zone, final_block_chance, block_return) + var/stam = active_block_stamina_cost(owner, object, blocked, attack_text, ATTACK_TYPE_PROJECTILE, armour_penetration, attacker, def_zone, final_block_chance, block_return) + stamina_amount += stam var/datum/block_parry_data/data = get_block_parry_data() if(iscarbon(owner)) var/mob/living/carbon/C = owner diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 7c4125fbc6..6b58988c31 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -361,7 +361,8 @@ "Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmed"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamed"), "Eyebot" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "eyebotmed"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymed"), + "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakemedbox") ) var/list/L = list("Medihound" = "medihound", "Medihound Dark" = "medihounddark", "Vale" = "valemed") for(var/a in L) @@ -422,6 +423,13 @@ moduleselect_icon = "medihound" moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi' dogborg = TRUE + if("Drake") + cyborg_base_icon = "drakemed" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakemedsleeper" + moduleselect_icon = "medihound" + moduleselect_alternate_icon = 'modular_citadel/icons/ui/screen_cyborg.dmi' + dogborg = TRUE else return FALSE return ..() @@ -478,7 +486,8 @@ "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "caneng"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinaeng"), "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidereng"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyeng"), + "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeengbox") ) var/list/L = list("Pup Dozer" = "pupdozer", "Vale" = "valeeng") for(var/a in L) @@ -536,6 +545,11 @@ cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi' sleeper_overlay = "alinasleeper" dogborg = TRUE + if("Drake") + cyborg_base_icon = "drakeeng" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakesecsleeper" + dogborg = TRUE else return FALSE return ..() @@ -574,7 +588,8 @@ "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "cansec"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinasec"), "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "spidersec"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavysec"), + "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakesecbox") ) var/list/L = list("K9" = "k9", "Vale" = "valesec", "K9 Dark" = "k9dark") for(var/a in L) @@ -630,6 +645,11 @@ sleeper_overlay = "valesecsleeper" cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi' dogborg = TRUE + if("Drake") + cyborg_base_icon = "drakesec" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakesecsleeper" + dogborg = TRUE else return FALSE return ..() @@ -673,7 +693,8 @@ var/static/list/peace_icons = sortList(list( "Default" = image(icon = 'icons/mob/robots.dmi', icon_state = "peace"), "Borgi" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "borgi"), - "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider") + "Spider" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "whitespider"), + "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakepeacebox") )) var/peace_borg_icon = show_radial_menu(R, R , peace_icons, custom_check = CALLBACK(src, .proc/check_menu, R), radius = 42, require_near = TRUE) switch(peace_borg_icon) @@ -689,6 +710,11 @@ hat_offset = INFINITY cyborg_icon_override = 'modular_citadel/icons/mob/robots.dmi' has_snowflake_deadsprite = TRUE + if("Drake") + cyborg_base_icon = "drakepeace" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakepeacesleeper" + dogborg = TRUE else return FALSE return ..() @@ -827,7 +853,8 @@ "(Janitor) Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinajan"), "(Janitor) Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekjan"), "(Janitor) Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canjan"), - "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan") + "(Janitor) Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavyjan"), + "(Janitor) Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakejanitbox") ) var/list/L = list("(Service) DarkK9" = "k50", "(Service) Vale" = "valeserv", "(Service) ValeDark" = "valeservdark", "(Janitor) Scrubpuppy" = "scrubpup") @@ -899,6 +926,11 @@ cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi' sleeper_overlay = "jsleeper" dogborg = TRUE + if("(Janitor) Drake") + cyborg_base_icon = "drakejanit" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakesecsleeper" + dogborg = TRUE else return FALSE return ..() @@ -944,7 +976,8 @@ "Sleek" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "sleekmin"), "Marina" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "marinamin"), "Can" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "canmin"), - "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin") + "Heavy" = image(icon = 'modular_citadel/icons/mob/robots.dmi', icon_state = "heavymin"), + "Drake" = image(icon = 'icons/mob/cyborg/drakemech.dmi', icon_state = "drakeminebox") ) var/list/L = list("Blade" = "blade", "Vale" = "valemine") for(var/a in L) @@ -988,6 +1021,11 @@ cyborg_icon_override = 'modular_citadel/icons/mob/widerobot.dmi' sleeper_overlay = "valeminesleeper" dogborg = TRUE + if("Drake") + cyborg_base_icon = "drakemine" + cyborg_icon_override = 'icons/mob/cyborg/drakemech.dmi' + sleeper_overlay = "drakeminesleeper" + dogborg = TRUE else return FALSE return ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 7a9610fb53..c6aee397e4 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -430,3 +430,6 @@ /mob/living/silicon/handle_high_gravity(gravity) return + +/mob/living/silicon/rust_heretic_act() + adjustBruteLoss(500) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index fe6792b5c2..a1772d9281 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -1058,3 +1058,6 @@ Pass a positive integer as an argument to override a bot's default speed. if(I) I.icon_state = null path.Cut(1, 2) + +/mob/living/silicon/rust_heretic_act() + adjustBruteLoss(500) diff --git a/code/modules/mob/living/simple_animal/eldritch_demons.dm b/code/modules/mob/living/simple_animal/eldritch_demons.dm new file mode 100644 index 0000000000..dbf62be16d --- /dev/null +++ b/code/modules/mob/living/simple_animal/eldritch_demons.dm @@ -0,0 +1,386 @@ +/mob/living/simple_animal/hostile/eldritch + name = "Demon" + real_name = "Demon" + desc = "" + gender = NEUTER + mob_biotypes = NONE + speak_emote = list("screams") + response_help_continuous = "thinks better of touching" + response_help_simple = "think better of touching" + response_disarm_continuous = "flails at" + response_disarm_simple = "flail at" + response_harm_continuous = "reaps" + response_harm_simple = "tears" + speak_chance = 1 + icon = 'icons/mob/eldritch_mobs.dmi' + speed = 0 + a_intent = INTENT_HARM + stop_automated_movement = 1 + AIStatus = AI_OFF + attack_sound = 'sound/weapons/punch1.ogg' + see_in_dark = 7 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = INFINITY + healable = 0 + movement_type = GROUND + pressure_resistance = 100 + del_on_death = TRUE + deathmessage = "implodes into itself" + faction = list("heretics") + simple_mob_flags = SILENCE_RANGED_MESSAGE + ///Innate spells that are supposed to be added when a beast is created + var/list/spells_to_add + +/mob/living/simple_animal/hostile/eldritch/Initialize() + . = ..() + add_spells() + +/** + * Add_spells + * + * Goes through spells_to_add and adds each spell to the mind. + */ +/mob/living/simple_animal/hostile/eldritch/proc/add_spells() + for(var/spell in spells_to_add) + AddSpell(new spell()) + +/mob/living/simple_animal/hostile/eldritch/raw_prophet + name = "Raw Prophet" + real_name = "Raw Prophet" + desc = "Abomination made from severed limbs." + icon_state = "raw_prophet" + status_flags = CANPUSH + icon_living = "raw_prophet" + melee_damage_lower = 5 + melee_damage_upper = 10 + maxHealth = 50 + health = 50 + sight = SEE_MOBS|SEE_OBJS|SEE_TURFS + spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash/long,/obj/effect/proc_holder/spell/pointed/manse_link,/obj/effect/proc_holder/spell/targeted/telepathy/eldritch,/obj/effect/proc_holder/spell/pointed/trigger/blind/eldritch) + + var/list/linked_mobs = list() + +/mob/living/simple_animal/hostile/eldritch/raw_prophet/Initialize() + . = ..() + link_mob(src) + +/mob/living/simple_animal/hostile/eldritch/raw_prophet/Login() + . = ..() + client.change_view(10) + +/mob/living/simple_animal/hostile/eldritch/raw_prophet/proc/link_mob(mob/living/mob_linked) + if(QDELETED(mob_linked) || mob_linked.stat == DEAD) + return FALSE + if(HAS_TRAIT(mob_linked, TRAIT_MINDSHIELD)) //mindshield implant, no dice + return FALSE + if(mob_linked.anti_magic_check(FALSE, FALSE, TRUE, 0)) + return FALSE + if(linked_mobs[mob_linked]) + return FALSE + + to_chat(mob_linked, "You feel something new enter your sphere of mind, you hear whispers of people far away, screeches of horror and a humming of welcome to [src]'s Mansus Link.") + var/datum/action/innate/mansus_speech/action = new(src) + linked_mobs[mob_linked] = action + action.Grant(mob_linked) + RegisterSignal(mob_linked, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING) , .proc/unlink_mob) + return TRUE + +/mob/living/simple_animal/hostile/eldritch/raw_prophet/proc/unlink_mob(mob/living/mob_linked) + if(!linked_mobs[mob_linked]) + return + UnregisterSignal(mob_linked, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING)) + var/datum/action/innate/mansus_speech/action = linked_mobs[mob_linked] + action.Remove(mob_linked) + qdel(action) + to_chat(mob_linked, "Your mind shatters as the [src]'s Mansus Link leaves your mind.") + mob_linked.emote("Scream") + //micro stun + mob_linked.AdjustParalyzed(0.5 SECONDS) + linked_mobs -= mob_linked + +/mob/living/simple_animal/hostile/eldritch/raw_prophet/death(gibbed) + for(var/linked_mob in linked_mobs) + unlink_mob(linked_mob) + return ..() + +/mob/living/simple_animal/hostile/eldritch/armsy + name = "Terror of the Night" + real_name = "Armsy" + desc = "Abomination made from severed limbs." + icon_state = "armsy_start" + icon_living = "armsy_start" + maxHealth = 200 + health = 200 + obj_damage = 80 + melee_damage_lower = 10 + melee_damage_upper = 15 + move_resist = MOVE_FORCE_OVERPOWERING+1 + movement_type = GROUND + environment_smash = ENVIRONMENT_SMASH_RWALLS + sight = SEE_MOBS + spells_to_add = list(/obj/effect/proc_holder/spell/targeted/worm_contract) + ranged = TRUE + ///Previous segment in the chain + var/mob/living/simple_animal/hostile/eldritch/armsy/back + ///Next segment in the chain + var/mob/living/simple_animal/hostile/eldritch/armsy/front + ///Your old location + var/oldloc + ///Allow / disallow pulling + var/allow_pulling = FALSE + ///How many arms do we have to eat to expand? + var/stacks_to_grow = 2 + ///Currently eaten arms + var/current_stacks = 0 + +//I tried Initalize but it didnt work, like at all. This proc just wouldnt fire if it was Initalize instead of New +/mob/living/simple_animal/hostile/eldritch/armsy/Initialize(mapload,spawn_more = TRUE,len = 6) + . = ..() + if(len < 3) + stack_trace("Eldritch Armsy created with invalid len ([len]). Reverting to 3.") + len = 3 //code breaks below 3, let's just not allow it. + oldloc = loc + RegisterSignal(src,COMSIG_MOVABLE_MOVED,.proc/update_chain_links) + if(!spawn_more) + return + allow_pulling = TRUE + ///next link + var/mob/living/simple_animal/hostile/eldritch/armsy/next + ///previous link + var/mob/living/simple_animal/hostile/eldritch/armsy/prev + ///current link + var/mob/living/simple_animal/hostile/eldritch/armsy/current + for(var/i in 0 to len) + prev = current + //i tried using switch, but byond is really fucky and it didnt work as intended. Im sorry + if(i == 0) + current = new type(drop_location(),FALSE) + current.icon_state = "armsy_mid" + current.icon_living = "armsy_mid" + current.front = src + current.AIStatus = AI_OFF + back = current + else if(i < len) + current = new type(drop_location(),FALSE) + prev.back = current + prev.icon_state = "armsy_mid" + prev.icon_living = "armsy_mid" + prev.front = next + prev.AIStatus = AI_OFF + else + prev.icon_state = "armsy_end" + prev.icon_living = "armsy_end" + prev.front = next + prev.AIStatus = AI_OFF + next = prev + +//we are literally a vessel of otherworldly destruction, we bring our own gravity unto this plane +/mob/living/simple_animal/hostile/eldritch/armsy/has_gravity(turf/T) + return TRUE + + +/mob/living/simple_animal/hostile/eldritch/armsy/can_be_pulled() + return FALSE + +///Updates chain links to force move onto a single tile +/mob/living/simple_animal/hostile/eldritch/armsy/proc/contract_next_chain_into_single_tile() + if(back) + back.forceMove(loc) + back.contract_next_chain_into_single_tile() + return + +///Updates the next mob in the chain to move to our last location, fixed the worm if somehow broken. +/mob/living/simple_animal/hostile/eldritch/armsy/proc/update_chain_links() + gib_trail() + if(back && back.loc != oldloc) + back.Move(oldloc) + // self fixing properties if somehow broken + if(front && loc != front.oldloc) + forceMove(front.oldloc) + oldloc = loc + +/mob/living/simple_animal/hostile/eldritch/armsy/proc/gib_trail() + if(front) // head makes gibs + return + var/chosen_decal = pick(typesof(/obj/effect/decal/cleanable/blood/tracks)) + var/obj/effect/decal/cleanable/blood/gibs/decal = new chosen_decal(drop_location()) + decal.setDir(dir) + +/mob/living/simple_animal/hostile/eldritch/armsy/Destroy() + if(front) + front.icon_state = "armsy_end" + front.icon_living = "armsy_end" + front.back = null + if(back) + QDEL_NULL(back) // chain destruction baby + return ..() + +/mob/living/simple_animal/hostile/eldritch/armsy/BiologicalLife(seconds, times_fired) + adjustBruteLoss(-2) + +/mob/living/simple_animal/hostile/eldritch/armsy/proc/heal() + if(health == maxHealth) + if(back) + back.heal() + return + else + current_stacks++ + if(current_stacks >= stacks_to_grow) + var/mob/living/simple_animal/hostile/eldritch/armsy/prev = new type(drop_location(),spawn_more = FALSE) + icon_state = "armsy_mid" + icon_living = "armsy_mid" + back = prev + prev.icon_state = "armsy_end" + prev.icon_living = "armsy_end" + prev.front = src + prev.AIStatus = AI_OFF + current_stacks = 0 + + adjustBruteLoss(-maxHealth * 0.5, FALSE) + adjustFireLoss(-maxHealth * 0.5 ,FALSE) + + +/mob/living/simple_animal/hostile/eldritch/armsy/Shoot(atom/targeted_atom) + target = targeted_atom + AttackingTarget() + + +/mob/living/simple_animal/hostile/eldritch/armsy/AttackingTarget() + if(istype(target,/obj/item/bodypart/r_arm) || istype(target,/obj/item/bodypart/l_arm)) + qdel(target) + heal() + return + if(target == back || target == front) + return + if(back) + back.target = target + back.AttackingTarget() + if(!Adjacent(target)) + return + do_attack_animation(target) + //have fun + //if(istype(target,/turf/closed/wall)) + //var/turf/closed/wall = target + //wall.ScrapeAway() + + + if(iscarbon(target)) + var/mob/living/carbon/C = target + if(HAS_TRAIT(C, TRAIT_NODISMEMBER)) + return + var/list/parts = list() + for(var/X in C.bodyparts) + var/obj/item/bodypart/bodypart = X + if(bodypart.body_part != HEAD && bodypart.body_part != CHEST) + if(bodypart.dismemberable) + parts += bodypart + if(length(parts) && prob(10)) + var/obj/item/bodypart/bodypart = pick(parts) + bodypart.dismember() + + return ..() + +/mob/living/simple_animal/hostile/eldritch/armsy/prime + name = "Lord of the Night" + real_name = "Master of Decay" + maxHealth = 400 + health = 400 + melee_damage_lower = 20 + melee_damage_upper = 25 + +/mob/living/simple_animal/hostile/eldritch/armsy/prime/Initialize(mapload,spawn_more = TRUE,len = 9) + . = ..() + var/matrix/matrix_transformation = matrix() + matrix_transformation.Scale(1.4,1.4) + transform = matrix_transformation + +/mob/living/simple_animal/hostile/eldritch/armsy/primeproc/heal() + if(health == maxHealth) + if(back) + back.heal() + return + else + current_stacks++ + if(current_stacks >= stacks_to_grow) + var/mob/living/simple_animal/hostile/eldritch/armsy/prev = new type(drop_location(),spawn_more = FALSE) + icon_state = "armsy_mid" + icon_living = "armsy_mid" + back = prev + prev.icon_state = "armsy_end" + prev.icon_living = "armsy_end" + prev.front = src + prev.AIStatus = AI_OFF + current_stacks = 0 + var/matrix/matrix_transformation = matrix() + matrix_transformation.Scale(1.4,1.4) + transform = matrix_transformation + + adjustBruteLoss(-maxHealth * 0.5, FALSE) + adjustFireLoss(-maxHealth * 0.5 ,FALSE) + + +/mob/living/simple_animal/hostile/eldritch/rust_spirit + name = "Rust Walker" + real_name = "Rusty" + desc = "Incomprehensible abomination actively seeping life out of it's surrounding." + icon_state = "rust_walker_s" + status_flags = CANPUSH + icon_living = "rust_walker_s" + maxHealth = 75 + health = 75 + melee_damage_lower = 15 + melee_damage_upper = 20 + sight = SEE_TURFS + spells_to_add = list(/obj/effect/proc_holder/spell/aoe_turf/rust_conversion/small,/obj/effect/proc_holder/spell/aimed/rust_wave/short) + +/mob/living/simple_animal/hostile/eldritch/rust_spirit/setDir(newdir, ismousemovement) + . = ..() + if(newdir == NORTH) + icon_state = "rust_walker_n" + else if(newdir == SOUTH) + icon_state = "rust_walker_s" + update_icon() + +/mob/living/simple_animal/hostile/eldritch/rust_spirit/Moved() + . = ..() + playsound(src, 'sound/effects/footstep/rustystep1.ogg', 100, TRUE) + +/mob/living/simple_animal/hostile/eldritch/rust_spirit/Life() + if(stat == DEAD) + return ..() + var/turf/T = get_turf(src) + if(istype(T,/turf/open/floor/plating/rust)) + adjustBruteLoss(-3, FALSE) + adjustFireLoss(-3, FALSE) + return ..() + +/mob/living/simple_animal/hostile/eldritch/ash_spirit + name = "Ash Man" + real_name = "Ashy" + desc = "Incomprehensible abomination actively seeping life out of it's surrounding." + icon_state = "ash_walker" + status_flags = CANPUSH + icon_living = "ash_walker" + maxHealth = 75 + health = 75 + melee_damage_lower = 15 + melee_damage_upper = 20 + sight = SEE_TURFS + spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash,/obj/effect/proc_holder/spell/pointed/cleave/long,/obj/effect/proc_holder/spell/aoe_turf/fire_cascade) + +/mob/living/simple_animal/hostile/eldritch/stalker + name = "Flesh Stalker" + real_name = "Flesh Stalker" + desc = "Abomination made from severed limbs." + icon_state = "stalker" + status_flags = CANPUSH + icon_living = "stalker" + maxHealth = 150 + health = 150 + melee_damage_lower = 15 + melee_damage_upper = 20 + sight = SEE_MOBS + spells_to_add = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift/ash,/obj/effect/proc_holder/spell/targeted/shapeshift/eldritch,/obj/effect/proc_holder/spell/targeted/emplosion/eldritch) diff --git a/code/modules/mob/living/simple_animal/friendly/possum.dm b/code/modules/mob/living/simple_animal/friendly/possum.dm new file mode 100644 index 0000000000..71fdbd1465 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/possum.dm @@ -0,0 +1,37 @@ +/mob/living/simple_animal/opossum + name = "opossum" + desc = "It's an opossum, a small scavenging marsupial." + icon_state = "possum" + icon_living = "possum" + icon_dead = "possum_dead" + speak = list("Hiss!","HISS!","Hissss?") + speak_emote = list("hisses") + emote_hear = list("hisses.") + emote_see = list("runs in a circle.", "shakes.") + speak_chance = 1 + turns_per_move = 3 + blood_volume = 250 + see_in_dark = 5 + maxHealth = 15 + health = 15 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 1) + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently pushes aside" + response_disarm_simple = "gently push aside" + response_harm_continuous = "stamps on" + response_harm_simple = "stamp" + density = FALSE + ventcrawler = VENTCRAWLER_ALWAYS + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_TINY + mob_biotypes = MOB_ORGANIC|MOB_BEAST + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/opossum/poppy + name = "Poppy the Safety Possum" + desc = "Safety first!" + icon_state = "poppypossum" + icon_living = "poppypossum" + icon_dead = "poppypossum_dead" + butcher_results = list(/obj/item/clothing/head/hardhat = 1) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 706bcc46f5..be0338a60e 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -146,6 +146,8 @@ var/bare_wound_bonus = 0 //If the attacks from this are sharp var/sharpness = SHARP_NONE + //Generic flags + var/simple_mob_flags = NONE /mob/living/simple_animal/Initialize() . = ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index d454f85d1c..358eff7a47 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -649,8 +649,6 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0) continue if(overrides.len && (A in overrides)) continue - if(A.IsObscured()) - continue statpanel(listed_turf.name, null, A) if(mind) add_spells_to_statpanel(mind.spell_list) diff --git a/code/modules/newscaster/ghostread.dm b/code/modules/newscaster/ghostread.dm index 77cb1a03c8..ff51f5268c 100644 --- a/code/modules/newscaster/ghostread.dm +++ b/code/modules/newscaster/ghostread.dm @@ -3,7 +3,7 @@ set desc = "Open a list of available news channels" set category = "Ghost" - var/datum/browser/B = new(src, "ghost_news_list", "Chanenl List", 450, 600) + var/datum/browser/B = new(src, "ghost_news_list", "Channel List", 450, 600) B.set_content(render_news_channel_list()) B.open() diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 997ada6b21..91b8a6719b 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -147,7 +147,7 @@ log_game("[user] [key_name(user)] has renamed [O] to [input]") if(penchoice == "Change description") - var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 100) + var/input = stripped_input(user,"Describe \the [O.name] here", ,"", 2048) if(QDELETED(O) || !user.canUseTopic(O, BE_CLOSE)) return O.desc = input diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 8559b8ba45..fd8f900552 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -2,34 +2,54 @@ //Please do not bother them with bugs from this port, however, as it has been modified quite a bit. //Modifications include removing the world-ending full supermatter variation, and leaving only the shard. +//Zap constants, speeds up targeting + +#define BIKE (COIL + 1) +#define COIL (ROD + 1) +#define ROD (LIVING + 1) +#define LIVING (MACHINERY + 1) +#define MACHINERY (OBJECT + 1) +#define OBJECT (LOWEST + 1) +#define LOWEST (1) + #define PLASMA_HEAT_PENALTY 15 // Higher == Bigger heat and waste penalty from having the crystal surrounded by this gas. Negative numbers reduce penalty. #define OXYGEN_HEAT_PENALTY 1 -#define CO2_HEAT_PENALTY 0.1 #define PLUOXIUM_HEAT_PENALTY -1 #define TRITIUM_HEAT_PENALTY 10 +#define CO2_HEAT_PENALTY 0.1 #define NITROGEN_HEAT_PENALTY -1.5 #define BZ_HEAT_PENALTY 5 +#define H2O_HEAT_PENALTY 8 +//#define FREON_HEAT_PENALTY -10 //very good heat absorbtion and less plasma and o2 generation +//#define HYDROGEN_HEAT_PENALTY 10 // similar heat penalty as tritium (dangerous) + +//All of these get divided by 10-bzcomp * 5 before having 1 added and being multiplied with power to determine rads +//Keep the negative values here above -10 and we won't get negative rads #define OXYGEN_TRANSMIT_MODIFIER 1.5 //Higher == Bigger bonus to power generation. #define PLASMA_TRANSMIT_MODIFIER 4 #define BZ_TRANSMIT_MODIFIER -2 +#define TRITIUM_TRANSMIT_MODIFIER 30 //We divide by 10, so this works out to 3 +#define PLUOXIUM_TRANSMIT_MODIFIER -5 //Should halve the power output +#define H2O_TRANSMIT_MODIFIER 2 +//#define HYDROGEN_TRANSMIT_MODIFIER 25 //increase the radiation emission, but less than the trit (2.5) -#define TRITIUM_RADIOACTIVITY_MODIFIER 3 //Higher == Crystal spews out more radiation -#define BZ_RADIOACTIVITY_MODIFIER 5 -#define PLUOXIUM_RADIOACTIVITY_MODIFIER -2 +#define BZ_RADIOACTIVITY_MODIFIER 5 //Improves the effect of transmit modifiers #define N2O_HEAT_RESISTANCE 6 //Higher == Gas makes the crystal more resistant against heat damage. #define PLUOXIUM_HEAT_RESISTANCE 3 +//#define HYDROGEN_HEAT_RESISTANCE 2 // just a bit of heat resistance to spice it up #define POWERLOSS_INHIBITION_GAS_THRESHOLD 0.20 //Higher == Higher percentage of inhibitor gas needed before the charge inertia chain reaction effect starts. #define POWERLOSS_INHIBITION_MOLE_THRESHOLD 20 //Higher == More moles of the gas are needed before the charge inertia chain reaction effect starts. //Scales powerloss inhibition down until this amount of moles is reached #define POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD 500 //bonus powerloss inhibition boost if this amount of moles is reached -#define MOLE_PENALTY_THRESHOLD 1800 //Higher == Shard can absorb more moles before triggering the high mole penalties. +#define MOLE_PENALTY_THRESHOLD 1800 //Above this value we can get lord singulo and independent mol damage, below it we can heal damage #define MOLE_HEAT_PENALTY 350 //Heat damage scales around this. Too hot setups with this amount of moles do regular damage, anything above and below is scaled -#define POWER_PENALTY_THRESHOLD 5000 //Higher == Engine can generate more power before triggering the high power penalties. -#define SEVERE_POWER_PENALTY_THRESHOLD 7000 //Same as above, but causes more dangerous effects -#define CRITICAL_POWER_PENALTY_THRESHOLD 12000 //Even more dangerous effects, threshold for tesla delamination +//Along with damage_penalty_point, makes flux anomalies. +#define POWER_PENALTY_THRESHOLD 5000 //The cutoff on power properly doing damage, pulling shit around, and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity +#define SEVERE_POWER_PENALTY_THRESHOLD 7000 //+1 bolt of electricity, allows for gravitational anomalies, and higher chances of pyro anomalies +#define CRITICAL_POWER_PENALTY_THRESHOLD 9000 //+1 bolt of electricity. #define HEAT_PENALTY_THRESHOLD 40 //Higher == Crystal safe operational temperature is higher. #define DAMAGE_HARDCAP 0.002 #define DAMAGE_INCREASE_MULTIPLIER 0.25 @@ -66,6 +86,13 @@ #define SUPERMATTER_COUNTDOWN_TIME 30 SECONDS +///to prevent accent sounds from layering +#define SUPERMATTER_ACCENT_SOUND_MIN_COOLDOWN 2 SECONDS + +#define DEFAULT_ZAP_ICON_STATE "sm_arc" +#define SLIGHTLY_CHARGED_ZAP_ICON_STATE "sm_arc_supercharged" +#define OVER_9000_ZAP_ICON_STATE "sm_arc_dbz_referance" //Witty I know + GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal @@ -76,89 +103,183 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) density = TRUE anchored = TRUE flags_1 = PREVENT_CONTENTS_EXPLOSION_1 - var/uid = 1 - var/static/gl_uid = 1 light_range = 4 resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - critical_machine = TRUE + ///The id of our supermatter + var/uid = 1 + ///The amount of supermatters that have been created this round + var/static/gl_uid = 1 + ///Tracks the bolt color we are using + var/zap_icon = DEFAULT_ZAP_ICON_STATE + ///The portion of the gasmix we're on that we should remove var/gasefficency = 0.15 - + ///Used for changing icon states for diff base sprites var/base_icon_state = "darkmatter" + ///Are we exploding? var/final_countdown = FALSE + ///The amount of damage we have currently var/damage = 0 + ///The damage we had before this cycle. Used to limit the damage we can take each cycle, and for safe_alert var/damage_archived = 0 + ///Our "Shit is no longer fucked" message. We send it when damage is less then damage_archived var/safe_alert = "Crystalline hyperstructure returning to safe operating parameters." + ///The point at which we should start sending messeges about the damage to the engi channels. var/warning_point = 50 + ///The alert we send when we've reached warning_point var/warning_alert = "Danger! Crystal hyperstructure integrity faltering!" - var/damage_penalty_point = 550 + ///The point at which we start sending messages to the common channel var/emergency_point = 700 + ///The alert we send when we've reached emergency_point var/emergency_alert = "CRYSTAL DELAMINATION IMMINENT." + ///The point at which we delam var/explosion_point = 900 + ///When we pass this amount of damage we start shooting bolts + var/damage_penalty_point = 550 - var/emergency_issued = FALSE - + ///A scaling value that affects the severity of explosions. var/explosion_power = 35 - var/temp_factor = 30 - - var/lastwarning = 0 // Time in 1/10th of seconds since the last sent warning + ///Time in 1/10th of seconds since the last sent warning + var/lastwarning = 0 + ///Refered to as eer on the moniter. This value effects gas output, heat, damage, and radiation. var/power = 0 - - var/n2comp = 0 // raw composition of each gas in the chamber, ranges from 0 to 1 - - var/plasmacomp = 0 - var/o2comp = 0 - var/co2comp = 0 - var/n2ocomp = 0 - var/pluoxiumcomp = 0 - var/tritiumcomp = 0 - var/bzcomp = 0 - - var/pluoxiumbonus = 0 - + ///Determines the rate of positve change in gas comp values + var/gas_change_rate = 0.05 + ///The list of gases we will be interacting with in process_atoms() + var/list/gases_we_care_about = list( + /datum/gas/oxygen, + /datum/gas/water_vapor, + /datum/gas/plasma, + /datum/gas/carbon_dioxide, + /datum/gas/nitrous_oxide, + /datum/gas/nitrogen, + /datum/gas/pluoxium, + /datum/gas/tritium, + /datum/gas/bz, +// /datum/gas/freon, +// /datum/gas/hydrogen, + ) + ///The list of gases mapped against their current comp. We use this to calculate different values the supermatter uses, like power or heat resistance. It doesn't perfectly match the air around the sm, instead moving up at a rate determined by gas_change_rate per call. Ranges from 0 to 1 + var/list/gas_comp = list( + /datum/gas/oxygen = 0, + /datum/gas/water_vapor = 0, + /datum/gas/plasma = 0, + /datum/gas/carbon_dioxide = 0, + /datum/gas/nitrous_oxide = 0, + /datum/gas/nitrogen = 0, + /datum/gas/pluoxium = 0, + /datum/gas/tritium = 0, + /datum/gas/bz = 0, +// /datum/gas/freon = 0, +// /datum/gas/hydrogen = 0, + ) + ///The list of gases mapped against their transmit values. We use it to determine the effect different gases have on radiation + var/list/gas_trans = list( + /datum/gas/oxygen = OXYGEN_TRANSMIT_MODIFIER, + /datum/gas/water_vapor = H2O_TRANSMIT_MODIFIER, + /datum/gas/plasma = PLASMA_TRANSMIT_MODIFIER, + /datum/gas/pluoxium = PLUOXIUM_TRANSMIT_MODIFIER, + /datum/gas/tritium = TRITIUM_TRANSMIT_MODIFIER, + /datum/gas/bz = BZ_TRANSMIT_MODIFIER, +// /datum/gas/hydrogen = HYDROGEN_TRANSMIT_MODIFIER, + ) + ///The list of gases mapped against their heat penaltys. We use it to determin molar and heat output + var/list/gas_heat = list( + /datum/gas/oxygen = OXYGEN_HEAT_PENALTY, + /datum/gas/water_vapor = H2O_HEAT_PENALTY, + /datum/gas/plasma = PLASMA_HEAT_PENALTY, + /datum/gas/carbon_dioxide = CO2_HEAT_PENALTY, + /datum/gas/nitrogen = NITROGEN_HEAT_PENALTY, + /datum/gas/pluoxium = PLUOXIUM_HEAT_PENALTY, + /datum/gas/tritium = TRITIUM_HEAT_PENALTY, + /datum/gas/bz = BZ_HEAT_PENALTY, +// /datum/gas/freon = FREON_HEAT_PENALTY, +// /datum/gas/hydrogen = HYDROGEN_HEAT_PENALTY, + ) + ///The list of gases mapped against their heat resistance. We use it to moderate heat damage. + var/list/gas_resist = list( + /datum/gas/nitrous_oxide = N2O_HEAT_RESISTANCE, + /datum/gas/pluoxium = PLUOXIUM_HEAT_RESISTANCE, +// /datum/gas/hydrogen = HYDROGEN_HEAT_RESISTANCE, + ) + ///The list of gases mapped against their powermix ratio + var/list/gas_powermix = list( + /datum/gas/oxygen = 1, + /datum/gas/water_vapor = 1, + /datum/gas/plasma = 1, + /datum/gas/carbon_dioxide = 1, + /datum/gas/nitrogen = -1, + /datum/gas/pluoxium = -1, + /datum/gas/tritium = 1, + /datum/gas/bz = 1, +// /datum/gas/freon = -1, +// /datum/gas/hydrogen = 1, + ) + ///The last air sample's total molar count, will always be above or equal to 0 var/combined_gas = 0 + ///Affects the power gain the sm experiances from heat var/gasmix_power_ratio = 0 + ///Affects the amount of o2 and plasma the sm outputs, along with the heat it makes. var/dynamic_heat_modifier = 1 + ///Affects the amount of damage and minimum point at which the sm takes heat damage var/dynamic_heat_resistance = 1 + ///Uses powerloss_dynamic_scaling and combined_gas to lessen the effects of our powerloss functions var/powerloss_inhibitor = 1 + ///Based on co2 percentage, slowly moves between 0 and 1. We use it to calc the powerloss_inhibitor var/powerloss_dynamic_scaling= 0 + ///Affects the amount of radiation the sm makes. We multiply this with power to find the rads. var/power_transmission_bonus = 0 + ///Used to increase or lessen the amount of damage the sm takes from heat based on molar counts. var/mole_heat_penalty = 0 - - + ///Takes the energy throwing things into the sm generates and slowly turns it into actual power var/matter_power = 0 + ///The cutoff for a bolt jumping, grows with heat, lowers with higher mol count, + var/zap_cutoff = 1500 + ///How much the bullets damage should be multiplied by when it is added to the internal variables + var/bullet_energy = 2 + ///How much hallucination should we produce per unit of power? + var/hallucination_power = 0.1 - //Temporary values so that we can optimize this - //How much the bullets damage should be multiplied by when it is added to the internal variables - var/config_bullet_energy = 2 - //How much of the power is left after processing is finished? -// var/config_power_reduction_per_tick = 0.5 - //How much hallucination should it produce per unit of power? - var/config_hallucination_power = 0.1 - + ///Our internal radio var/obj/item/radio/radio + ///The key our internal radio uses var/radio_key = /obj/item/encryptionkey/headset_eng + ///The engineering channel var/engineering_channel = "Engineering" + ///The common channel var/common_channel = null - //for logging + ///Boolean used for logging if we've been powered var/has_been_powered = FALSE + ///Boolean used for logging if we've passed the emergency point var/has_reached_emergency = FALSE - // For making hugbox supermatter - var/takes_damage = TRUE - var/produces_gas = TRUE + ///An effect we show to admins and ghosts the percentage of delam we're at var/obj/effect/countdown/supermatter/countdown + ///Used along with a global var to track if we can give out the sm sliver stealing objective var/is_main_engine = FALSE - + ///Our soundloop var/datum/looping_sound/supermatter/soundloop - + ///Can it be moved? var/moveable = FALSE + ///cooldown tracker for accent sounds + var/last_accent_sound = 0 + + //For making hugbox supermatters + ///Disables all methods of taking damage + var/takes_damage = TRUE + ///Disables the production of gas, and pretty much any handling of it we do. + var/produces_gas = TRUE + ///Disables power changes + var/power_changes = TRUE + ///Disables the sm's proccessing totally. + var/processes = TRUE + /obj/machinery/power/supermatter_crystal/Initialize() . = ..() uid = gl_uid++ @@ -174,6 +295,9 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) if(is_main_engine) GLOB.main_supermatter_engine = src + AddElement(/datum/element/bsa_blocker) + RegisterSignal(src, COMSIG_ATOM_BSA_BEAM, .proc/call_explode) + soundloop = new(list(src), TRUE) /obj/machinery/power/supermatter_crystal/Destroy() @@ -184,12 +308,11 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) QDEL_NULL(countdown) if(is_main_engine && GLOB.main_supermatter_engine == src) GLOB.main_supermatter_engine = null - QDEL_NULL(soundloop) return ..() /obj/machinery/power/supermatter_crystal/examine(mob/user) . = ..() - if (iscarbon(user)) + if (istype(user, /mob/living/carbon)) var/mob/living/carbon/C = user if (!istype(C.glasses, /obj/item/clothing/glasses/meson) && (get_dist(user, src) < HALLUCINATION_RANGE(power))) . += "You get headaches just from looking at it." @@ -202,16 +325,17 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) if(!air) return SUPERMATTER_ERROR - if(get_integrity() < SUPERMATTER_DELAM_PERCENT) + var/integrity = get_integrity() + if(integrity < SUPERMATTER_DELAM_PERCENT) return SUPERMATTER_DELAMINATING - if(get_integrity() < SUPERMATTER_EMERGENCY_PERCENT) + if(integrity < SUPERMATTER_EMERGENCY_PERCENT) return SUPERMATTER_EMERGENCY - if(get_integrity() < SUPERMATTER_DANGER_PERCENT) + if(integrity < SUPERMATTER_DANGER_PERCENT) return SUPERMATTER_DANGER - if((get_integrity() < SUPERMATTER_WARNING_PERCENT) || (air.return_temperature() > CRITICAL_TEMPERATURE)) + if((integrity < SUPERMATTER_WARNING_PERCENT) || (air.return_temperature() > CRITICAL_TEMPERATURE)) return SUPERMATTER_WARNING if(air.return_temperature() > (CRITICAL_TEMPERATURE * 0.8)) @@ -238,23 +362,26 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) integrity = integrity < 0 ? 0 : integrity return integrity +/obj/machinery/power/supermatter_crystal/update_overlays() + . = ..() + if(final_countdown) + . += "casuality_field" + /obj/machinery/power/supermatter_crystal/proc/countdown() set waitfor = FALSE if(final_countdown) // We're already doing it go away return final_countdown = TRUE - - var/image/causality_field = image(icon, null, "causality_field") - add_overlay(causality_field, TRUE) + update_icon() var/speaking = "[emergency_alert] The supermatter has reached critical integrity failure. Emergency causality destabilization field has been activated." radio.talk_into(src, speaking, common_channel, language = get_selected_language()) for(var/i in SUPERMATTER_COUNTDOWN_TIME to 0 step -10) if(damage < explosion_point) // Cutting it a bit close there engineers radio.talk_into(src, "[safe_alert] Failsafe has been disengaged.", common_channel) - cut_overlay(causality_field, TRUE) final_countdown = FALSE + update_icon() return else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individualy sleep(10) @@ -287,48 +414,65 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "delam", /datum/mood_event/delam) if(combined_gas > MOLE_PENALTY_THRESHOLD) investigate_log("has collapsed into a singularity.", INVESTIGATE_SUPERMATTER) - if(T) + if(T) //If something fucks up we blow anyhow. This fix is 4 years old and none ever said why it's here. help. var/obj/singularity/S = new(T) S.energy = 800 S.consume(src) - else - investigate_log("has exploded.", INVESTIGATE_SUPERMATTER) - explosion(get_turf(T), explosion_power * max(gasmix_power_ratio, 0.205) * 0.5 , explosion_power * max(gasmix_power_ratio, 0.205) + 2, explosion_power * max(gasmix_power_ratio, 0.205) + 4 , explosion_power * max(gasmix_power_ratio, 0.205) + 6, 1, 1) - if(power > POWER_PENALTY_THRESHOLD) - investigate_log("has spawned additional energy balls.", INVESTIGATE_SUPERMATTER) + return //No boom for me sir + else if(power > POWER_PENALTY_THRESHOLD) + investigate_log("has spawned additional energy balls.", INVESTIGATE_SUPERMATTER) + if(T) var/obj/singularity/energy_ball/E = new(T) E.energy = power - qdel(src) + investigate_log("has exploded.", INVESTIGATE_SUPERMATTER) + //Dear mappers, balance the sm max explosion radius to 17.5, 37, 39, 41 + explosion(get_turf(T), explosion_power * max(gasmix_power_ratio, 0.205) * 0.5 , explosion_power * max(gasmix_power_ratio, 0.205) + 2, explosion_power * max(gasmix_power_ratio, 0.205) + 4 , explosion_power * max(gasmix_power_ratio, 0.205) + 6, 1, 1) + qdel(src) -/obj/machinery/power/supermatter_crystal/proc/consume_turf(turf/T) - var/oldtype = T.type - var/turf/newT = T.ScrapeAway() - if(newT.type == oldtype) - return - playsound(T, 'sound/effects/supermatter.ogg', 50, 1) - T.visible_message("[T] smacks into [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") - CALCULATE_ADJACENT_TURFS(T) + +//this is here to eat arguments +/obj/machinery/power/supermatter_crystal/proc/call_explode() + explode() /obj/machinery/power/supermatter_crystal/process_atmos() + if(!processes) //Just fuck me up bro + return var/turf/T = loc - if(isnull(T)) // We have a null turf...something is wrong, stop processing this entity. + if(isnull(T))// We have a null turf...something is wrong, stop processing this entity. return PROCESS_KILL - if(!istype(T)) //We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now. + if(!istype(T))//We are in a crate or somewhere that isn't turf, if we return to turf resume processing but for now. return //Yeah just stop. - if(istype(T, /turf/closed)) - consume_turf(T) + if(isclosedturf(T)) + var/turf/did_it_melt = T.Melt() + if(!isclosedturf(did_it_melt)) //In case some joker finds way to place these on indestructible walls + visible_message("[src] melts through [T]!") + return + + //We vary volume by power, and handle OH FUCK FUSION IN COOLING LOOP noises. if(power) - soundloop.volume = min(40, (round(power/100)/50)+1) // 5 +1 volume per 20 power. 2500 power is max + soundloop.volume = clamp((50 + (power / 50)), 50, 100) + if(damage >= 300) + soundloop.mid_sounds = list('sound/machines/sm/loops/delamming.ogg' = 1) + else + soundloop.mid_sounds = list('sound/machines/sm/loops/calm.ogg' = 1) + + //We play delam/neutral sounds at a rate determined by power and damage + if(last_accent_sound < world.time && prob(20)) + var/aggression = min(((damage / 800) * (power / 2500)), 1.0) * 100 + if(damage >= 300) + playsound(src, "smdelam", max(50, aggression), FALSE, 10) + else + playsound(src, "smcalm", max(50, aggression), FALSE, 10) + var/next_sound = round((100 - aggression) * 5) + last_accent_sound = world.time + max(SUPERMATTER_ACCENT_SOUND_MIN_COOLDOWN, next_sound) //Ok, get the air from the turf var/datum/gas_mixture/env = T.return_air() var/datum/gas_mixture/removed - if(produces_gas) //Remove gas from surrounding area removed = env.remove(gasefficency * env.total_moles()) @@ -336,141 +480,240 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) // Pass all the gas related code an empty gas container removed = new() damage_archived = damage + + /******** + EXPERIMENTAL, HUGBOXY AS HELL CITADEL CHANGES: Even in a vaccum, update gas composition and modifiers. + This means that the SM will usually have a very small explosion if it ends up being breached to space, + and CO2 tesla delaminations basically require multiple grounding rods to stabilize it long enough to not have it vent. + *********/ + if(!removed || !removed.total_moles() || isspaceturf(T)) //we're in space or there is no gas to process if(takes_damage) damage += max((power / 1000) * DAMAGE_INCREASE_MULTIPLIER, 0.1) // always does at least some damage + combined_gas = max(0, combined_gas - 0.5) // Slowly wear off. + for(var/gasID in gases_we_care_about) + gas_comp[gasID] = max(0, gas_comp[gasID] - 0.05) //slowly ramp down else if(takes_damage) //causing damage + //Due to DAMAGE_INCREASE_MULTIPLIER, we only deal one 4th of the damage the statements otherwise would cause + + //((((some value between 0.5 and 1 * temp - ((273.15 + 40) * some values between 1 and 10)) * some number between 0.25 and knock your socks off / 150) * 0.25 + //Heat and mols account for each other, a lot of hot mols are more damaging then a few + //Mols start to have a positive effect on damage after 350 damage = max(damage + (max(clamp(removed.total_moles() / 200, 0.5, 1) * removed.return_temperature() - ((T0C + HEAT_PENALTY_THRESHOLD)*dynamic_heat_resistance), 0) * mole_heat_penalty / 150 ) * DAMAGE_INCREASE_MULTIPLIER, 0) + //Power only starts affecting damage when it is above 5000 damage = max(damage + (max(power - POWER_PENALTY_THRESHOLD, 0)/500) * DAMAGE_INCREASE_MULTIPLIER, 0) + //Molar count only starts affecting damage when it is above 1800 damage = max(damage + (max(combined_gas - MOLE_PENALTY_THRESHOLD, 0)/80) * DAMAGE_INCREASE_MULTIPLIER, 0) + //There might be a way to integrate healing and hurting via heat //healing damage if(combined_gas < MOLE_PENALTY_THRESHOLD) - damage = max(damage + (min(removed.return_temperature() - (T0C + HEAT_PENALTY_THRESHOLD), 0) / 150 ), 0) + //Only has a net positive effect when the temp is below 313.15, heals up to 2 damage. Psycologists increase this temp min by up to 45 + damage = max(damage + (min(removed.return_temperature() - (T0C + HEAT_PENALTY_THRESHOLD), 0) / 150), 0) - //capping damage - damage = min(damage_archived + (DAMAGE_HARDCAP * explosion_point),damage) - if(damage > damage_archived && prob(10)) - playsound(get_turf(src), 'sound/effects/empulse.ogg', 50, 1) + //caps damage rate - //calculating gas related values - combined_gas = max(removed.total_moles(), 0) + //Takes the lower number between archived damage + (1.8) and damage + //This means we can only deal 1.8 damage per function call + damage = min(damage_archived + (DAMAGE_HARDCAP * explosion_point), damage) - plasmacomp = max(removed.get_moles(/datum/gas/plasma)/combined_gas, 0) - o2comp = max(removed.get_moles(/datum/gas/oxygen)/combined_gas, 0) - co2comp = max(removed.get_moles(/datum/gas/carbon_dioxide)/combined_gas, 0) - tritiumcomp = max(removed.get_moles(/datum/gas/tritium)/combined_gas, 0) - bzcomp = max(removed.get_moles(/datum/gas/bz)/combined_gas, 0) + //calculating gas related values + //Wanna know a secret? See that max() to zero? it's used for error checking. If we get a mol count in the negative, we'll get a divide by zero error + combined_gas = max(removed.total_moles(), 0) - pluoxiumcomp = max(removed.get_moles(/datum/gas/pluoxium)/combined_gas, 0) - n2ocomp = max(removed.get_moles(/datum/gas/nitrous_oxide)/combined_gas, 0) - n2comp = max(removed.get_moles(/datum/gas/nitrogen)/combined_gas, 0) + //This is more error prevention, according to all known laws of atmos, gas_mix.remove() should never make negative mol values. + //But this is tg - if(pluoxiumcomp >= 0.15) - pluoxiumbonus = 1 //makes pluoxium only work at 15%+ - else - pluoxiumbonus = 0 + //Lets get the proportions of the gasses in the mix and then slowly move our comp to that value + //Can cause an overestimation of mol count, should stabalize things though. + //Prevents huge bursts of gas/heat when a large amount of something is introduced + //They range between 0 and 1 + for(var/gasID in gases_we_care_about) + gas_comp[gasID] += clamp(max(removed.get_moles(gasID)/combined_gas, 0) - gas_comp[gasID], -1, gas_change_rate) - gasmix_power_ratio = min(max(plasmacomp + o2comp + co2comp + tritiumcomp + bzcomp - pluoxiumcomp - n2comp, 0), 1) + var/list/heat_mod = gases_we_care_about.Copy() + var/list/transit_mod = gases_we_care_about.Copy() + var/list/resistance_mod = gases_we_care_about.Copy() - dynamic_heat_modifier = max((plasmacomp * PLASMA_HEAT_PENALTY) + (o2comp * OXYGEN_HEAT_PENALTY) + (co2comp * CO2_HEAT_PENALTY) + (tritiumcomp * TRITIUM_HEAT_PENALTY) + ((pluoxiumcomp * PLUOXIUM_HEAT_PENALTY) * pluoxiumbonus) + (n2comp * NITROGEN_HEAT_PENALTY) + (bzcomp * BZ_HEAT_PENALTY), 0.5) - dynamic_heat_resistance = max((n2ocomp * N2O_HEAT_RESISTANCE) + ((pluoxiumcomp * PLUOXIUM_HEAT_RESISTANCE) * pluoxiumbonus), 1) + //We're concerned about pluoxium being too easy to abuse at low percents, so we make sure there's a substantial amount. + var/pluoxiumbonus = (gas_comp[/datum/gas/pluoxium] >= 0.15) //makes pluoxium only work at 15%+ + var/h2obonus = 1 - (gas_comp[/datum/gas/water_vapor] * 0.25)//At max this value should be 0.75 +// var/freonbonus = (gas_comp[/datum/gas/freon] <= 0.03) //Let's just yeet power output if this shit is high - power_transmission_bonus = max((plasmacomp * PLASMA_TRANSMIT_MODIFIER) + (o2comp * OXYGEN_TRANSMIT_MODIFIER) + (bzcomp * BZ_TRANSMIT_MODIFIER), 0) + heat_mod[/datum/gas/pluoxium] = pluoxiumbonus + transit_mod[/datum/gas/pluoxium] = pluoxiumbonus + resistance_mod[/datum/gas/pluoxium] = pluoxiumbonus - //more moles of gases are harder to heat than fewer, so let's scale heat damage around them - mole_heat_penalty = max(combined_gas / MOLE_HEAT_PENALTY, 0.25) + //No less then zero, and no greater then one, we use this to do explosions and heat to power transfer + //Be very careful with modifing this var by large amounts, and for the love of god do not push it past 1 + gasmix_power_ratio = 0 + for(var/gasID in gas_powermix) + gasmix_power_ratio += gas_comp[gasID] * gas_powermix[gasID] + gasmix_power_ratio = clamp(gasmix_power_ratio, 0, 1) - if (combined_gas > POWERLOSS_INHIBITION_MOLE_THRESHOLD && co2comp > POWERLOSS_INHIBITION_GAS_THRESHOLD) - powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling + clamp(co2comp - powerloss_dynamic_scaling, -0.02, 0.02), 0, 1) - else - powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling - 0.05,0, 1) - powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD,1 ,1.5)),0 ,1) + //Minimum value of -10, maximum value of 23. Effects plasma and o2 output and the output heat + dynamic_heat_modifier = 0 + for(var/gasID in gas_heat) + dynamic_heat_modifier += gas_comp[gasID] * gas_heat[gasID] * (isnull(heat_mod[gasID]) ? 1 : heat_mod[gasID]) + dynamic_heat_modifier *= h2obonus + dynamic_heat_modifier = max(dynamic_heat_modifier, 0.5) - if(matter_power) - var/removed_matter = max(matter_power/MATTER_POWER_CONVERSION, 40) - power = max(power + removed_matter, 0) - matter_power = max(matter_power - removed_matter, 0) + //Value between 1 and 10. Effects the damage heat does to the crystal + dynamic_heat_resistance = 0 + for(var/gasID in gas_resist) + dynamic_heat_resistance += gas_comp[gasID] * gas_resist[gasID] * (isnull(resistance_mod[gasID]) ? 1 : resistance_mod[gasID]) + dynamic_heat_resistance = max(dynamic_heat_resistance, 1) - var/temp_factor = 50 + //Value between -5 and 30, used to determine radiation output as it concerns things like collectors. + power_transmission_bonus = 0 + for(var/gasID in gas_trans) + power_transmission_bonus += gas_comp[gasID] * gas_trans[gasID] * (isnull(transit_mod[gasID]) ? 1 : transit_mod[gasID]) + power_transmission_bonus *= h2obonus - if(gasmix_power_ratio > 0.8) - // with a perfect gas mix, make the power less based on heat - icon_state = "[base_icon_state]_glow" - else - // in normal mode, base the produced energy around the heat - temp_factor = 30 - icon_state = base_icon_state + //more moles of gases are harder to heat than fewer, so let's scale heat damage around them + mole_heat_penalty = max(combined_gas / MOLE_HEAT_PENALTY, 0.25) - power = max( (removed.return_temperature() * temp_factor / T0C) * gasmix_power_ratio + power, 0) //Total laser power plus an overload + //Ramps up or down in increments of 0.02 up to the proportion of co2 + //Given infinite time, powerloss_dynamic_scaling = co2comp + //Some value between 0 and 1 + if (combined_gas > POWERLOSS_INHIBITION_MOLE_THRESHOLD && gas_comp[/datum/gas/carbon_dioxide] > POWERLOSS_INHIBITION_GAS_THRESHOLD) //If there are more then 20 mols, and more then 20% co2 + powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling + clamp(gas_comp[/datum/gas/carbon_dioxide] - powerloss_dynamic_scaling, -0.02, 0.02), 0, 1) + else + powerloss_dynamic_scaling = clamp(powerloss_dynamic_scaling - 0.05, 0, 1) + //Ranges from 0 to 1(1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500))) + //We take the mol count, and scale it to be our inhibitor + powerloss_inhibitor = clamp(1-(powerloss_dynamic_scaling * clamp(combined_gas/POWERLOSS_INHIBITION_MOLE_BOOST_THRESHOLD, 1, 1.5)), 0, 1) - if(prob(50)) - radiation_pulse(src, power * (1 + (tritiumcomp * TRITIUM_RADIOACTIVITY_MODIFIER) + ((pluoxiumcomp * PLUOXIUM_RADIOACTIVITY_MODIFIER) * pluoxiumbonus) * (power_transmission_bonus/(10-(bzcomp * BZ_RADIOACTIVITY_MODIFIER))))) // Rad Modifiers BZ(500%), Tritium(300%), and Pluoxium(-200%) - if(bzcomp >= 0.4 && prob(30 * bzcomp)) - fire_nuclear_particle() // Start to emit radballs at a maximum of 30% chance per tick + //Releases stored power into the general pool + //We get this by consuming shit or being scalpeled + if(matter_power && power_changes) + //We base our removed power off one 10th of the matter_power. + var/removed_matter = max(matter_power/MATTER_POWER_CONVERSION, 40) + //Adds at least 40 power + power = max(power + removed_matter, 0) + //Removes at least 40 matter power + matter_power = max(matter_power - removed_matter, 0) - var/device_energy = power * REACTION_POWER_MODIFIER + var/temp_factor = 50 + if(gasmix_power_ratio > 0.8) + //with a perfect gas mix, make the power more based on heat + icon_state = "[base_icon_state]_glow" + else + //in normal mode, power is less effected by heat + temp_factor = 30 + icon_state = base_icon_state - //To figure out how much temperature to add each tick, consider that at one atmosphere's worth - //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature - //that the device energy is around 2140. At that stage, we don't want too much heat to be put out - //Since the core is effectively "cold" + //if there is more pluox and n2 then anything else, we receive no power increase from heat + if(power_changes) + power = max((removed.return_temperature() * temp_factor / T0C) * gasmix_power_ratio + power, 0) - //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock - //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall. - removed.set_temperature(removed.return_temperature() + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER)) + if(prob(50)) + //(1 + (tritRad + pluoxDampen * bzDampen * o2Rad * plasmaRad / (10 - bzrads))) * freonbonus + radiation_pulse(src, power * max(0, (1 + (power_transmission_bonus/(10-(gas_comp[/datum/gas/bz] * BZ_RADIOACTIVITY_MODIFIER)))) * 1))//freonbonus))// RadModBZ(500%) + if(gas_comp[/datum/gas/bz] >= 0.4 && prob(30 * gas_comp[/datum/gas/bz])) + src.fire_nuclear_particle() // Start to emit radballs at a maximum of 30% chance per tick - removed.set_temperature(max(0, min(removed.return_temperature(), 2500 * dynamic_heat_modifier))) + //Power * 0.55 * a value between 1 and 0.8 + var/device_energy = power * REACTION_POWER_MODIFIER - //Calculate how much gas to release - removed.adjust_moles(/datum/gas/plasma, max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0)) + //To figure out how much temperature to add each tick, consider that at one atmosphere's worth + //of pure oxygen, with all four lasers firing at standard energy and no N2 present, at room temperature + //that the device energy is around 2140. At that stage, we don't want too much heat to be put out + //Since the core is effectively "cold" - removed.adjust_moles(/datum/gas/oxygen, max(((device_energy + removed.return_temperature() * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0)) + //Also keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock + //is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall. + //Power * 0.55 * (some value between 1.5 and 23) / 5 + removed.set_temperature(removed.return_temperature() + ((device_energy * dynamic_heat_modifier) / THERMAL_RELEASE_MODIFIER)) + //We can only emit so much heat, that being 57500 + removed.set_temperature(max(0, min(removed.return_temperature(), 2500 * dynamic_heat_modifier))) - if(produces_gas) - env.merge(removed) - air_update_turf() + //Calculate how much gas to release + //Varies based on power and gas content + removed.adjust_moles(/datum/gas/plasma, max((device_energy * dynamic_heat_modifier) / PLASMA_RELEASE_MODIFIER, 0)) + //Varies based on power, gas content, and heat + removed.adjust_moles(/datum/gas/oxygen, max(((device_energy + removed.return_temperature() * dynamic_heat_modifier) - T0C) / OXYGEN_RELEASE_MODIFIER, 0)) - for(var/mob/living/carbon/human/l in fov_viewers(HALLUCINATION_RANGE(power), src)) // If they can see it without mesons on. Bad on them. + if(produces_gas) + env.merge(removed) + air_update_turf() + + /********* + END CITADEL CHANGES + *********/ + + //Makes em go mad and accumulate rads. + for(var/mob/living/carbon/human/l in fov_viewers(src, HALLUCINATION_RANGE(power))) // If they can see it without mesons on. Bad on them. if(!istype(l.glasses, /obj/item/clothing/glasses/meson)) var/D = sqrt(1 / max(1, get_dist(l, src))) - l.hallucination += power * config_hallucination_power * D - l.hallucination = clamp(0, 200, l.hallucination) - + l.hallucination += power * hallucination_power * D + l.hallucination = clamp(l.hallucination, 0, 200) for(var/mob/living/l in range(src, round((power / 100) ** 0.25))) var/rads = (power / 10) * sqrt( 1 / max(get_dist(l, src),1) ) l.rad_act(rads) - power -= ((power/500)**3) * powerloss_inhibitor + //Transitions between one function and another, one we use for the fast inital startup, the other is used to prevent errors with fusion temperatures. + //Use of the second function improves the power gain imparted by using co2 + if(power_changes) + power = max(power - min(((power/500)**3) * powerloss_inhibitor, power * 0.83 * powerloss_inhibitor),0) + //After this point power is lowered + //This wraps around to the begining of the function + //Handle high power zaps/anomaly generation + if(power > POWER_PENALTY_THRESHOLD || damage > damage_penalty_point) //If the power is above 5000 or if the damage is above 550 + var/range = 4 + zap_cutoff = 1500 + if(removed && removed.return_pressure() > 0 && removed.return_temperature() > 0) + //You may be able to freeze the zapstate of the engine with good planning, we'll see + zap_cutoff = clamp(3000 - (power * (removed.total_moles()) / 10) / removed.return_temperature(), 350, 3000)//If the core is cold, it's easier to jump, ditto if there are a lot of mols + //We should always be able to zap our way out of the default enclosure + //See supermatter_zap() for more details + range = clamp(power / removed.return_pressure() * 10, 2, 7) + var/flags = ZAP_SUPERMATTER_FLAGS + var/zap_count = 0 + //Deal with power zaps + switch(power) + if(POWER_PENALTY_THRESHOLD to SEVERE_POWER_PENALTY_THRESHOLD) + zap_icon = DEFAULT_ZAP_ICON_STATE + zap_count = 2 + if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD) + zap_icon = SLIGHTLY_CHARGED_ZAP_ICON_STATE + //Uncaps the zap damage, it's maxed by the input power + //Objects take damage now + flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE) + zap_count = 3 + if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY) + zap_icon = OVER_9000_ZAP_ICON_STATE + //It'll stun more now, and damage will hit harder, gloves are no garentee. + //Machines go boom + flags |= (ZAP_MOB_STUN | ZAP_MACHINE_EXPLOSIVE | ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE) + zap_count = 4 + //Now we deal with damage shit + if (damage > damage_penalty_point && prob(20)) + zap_count += 1 - if(power > POWER_PENALTY_THRESHOLD || damage > damage_penalty_point) + if(zap_count >= 1) + playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10) + for(var/i in 1 to zap_count) + supermatter_zap(src, range, clamp(power*2, 4000, 20000), flags) - if(power > POWER_PENALTY_THRESHOLD) - playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, 1, extrarange = 10) - supermatter_zap(src, 5, min(power*2, 20000)) - supermatter_zap(src, 5, min(power*2, 20000)) - if(power > SEVERE_POWER_PENALTY_THRESHOLD) - supermatter_zap(src, 5, min(power*2, 20000)) - if(power > CRITICAL_POWER_PENALTY_THRESHOLD) - supermatter_zap(src, 5, min(power*2, 20000)) - else if (damage > damage_penalty_point && prob(20)) - playsound(src.loc, 'sound/weapons/emitter2.ogg', 100, 1, extrarange = 10) - supermatter_zap(src, 5, clamp(power*2, 4000, 20000)) - - if(prob(15) && power > POWER_PENALTY_THRESHOLD) - supermatter_pull(src, power/750) if(prob(5)) supermatter_anomaly_gen(src, FLUX_ANOMALY, rand(5, 10)) if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(5) || prob(1)) supermatter_anomaly_gen(src, GRAVITATIONAL_ANOMALY, rand(5, 10)) - if(power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2) || prob(0.3) && power > POWER_PENALTY_THRESHOLD) + if((power > SEVERE_POWER_PENALTY_THRESHOLD && prob(2)) || (prob(0.3) && power > POWER_PENALTY_THRESHOLD)) supermatter_anomaly_gen(src, PYRO_ANOMALY, rand(5, 10)) + + if(prob(15)) + supermatter_pull(loc, min(power/850, 3))//850, 1700, 2550 + + //Tells the engi team to get their butt in gear if(damage > warning_point) // while the core is still damaged and it's still worth noting its status if((REALTIMEOFDAY - lastwarning) / 10 >= WARNING_DELAY) alarm() + //Oh shit it's bad, time to freak out if(damage > emergency_point) radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel) lastwarning = REALTIMEOFDAY @@ -493,7 +736,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) if(combined_gas > MOLE_PENALTY_THRESHOLD) radio.talk_into(src, "Warning: Critical coolant mass reached.", engineering_channel) - + //Boom (Mind blown) if(damage > explosion_point) countdown() @@ -503,16 +746,17 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) var/turf/L = loc if(!istype(L)) return FALSE - if(!istype(Proj.firer, /obj/machinery/power/emitter)) + if(!istype(Proj.firer, /obj/machinery/power/emitter) && power_changes) investigate_log("has been hit by [Proj] fired by [key_name(Proj.firer)]", INVESTIGATE_SUPERMATTER) if(Proj.flag != "bullet") - power += Proj.damage * config_bullet_energy - if(!has_been_powered) - investigate_log("has been powered for the first time.", INVESTIGATE_SUPERMATTER) - message_admins("[src] has been powered for the first time [ADMIN_JMP(src)].") - has_been_powered = TRUE + if(power_changes) //This needs to be here I swear + power += Proj.damage * bullet_energy + if(!has_been_powered) + investigate_log("has been powered for the first time.", INVESTIGATE_SUPERMATTER) + message_admins("[src] has been powered for the first time [ADMIN_JMP(src)].") + has_been_powered = TRUE else if(takes_damage) - matter_power += Proj.damage * config_bullet_energy + matter_power += Proj.damage * bullet_energy return BULLET_ACT_HIT /obj/machinery/power/supermatter_crystal/singularity_act() @@ -529,15 +773,15 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/blob_act(obj/structure/blob/B) if(B && !isspaceturf(loc)) //does nothing in space - playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1) + playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE) damage += B.obj_integrity * 0.5 //take damage equal to 50% of remaining blob health before it tried to eat us if(B.obj_integrity > 100) B.visible_message("\The [B] strikes at \the [src] and flinches away!",\ - "You hear a loud crack as you are washed with a wave of heat.") + "You hear a loud crack as you are washed with a wave of heat.") B.take_damage(100, BURN) else B.visible_message("\The [B] strikes at \the [src] and rapidly flashes to ash.",\ - "You hear a loud crack as you are washed with a wave of heat.") + "You hear a loud crack as you are washed with a wave of heat.") Consume(B) /obj/machinery/power/supermatter_crystal/attack_tk(mob/user) @@ -546,11 +790,10 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) log_game("[key_name(C)] has been disintegrated by a telekenetic grab on a supermatter crystal.
") to_chat(C, "That was a really dense idea.") C.visible_message("A bright flare of radiation is seen from [C]'s head, shortly before you hear a sickening sizzling!") + C.ghostize() var/obj/item/organ/brain/rip_u = locate(/obj/item/organ/brain) in C.internal_organs - rip_u.Remove() + rip_u.Remove(C) qdel(rip_u) - return - return ..() /obj/machinery/power/supermatter_crystal/attack_paw(mob/user) dust_mob(user, cause = "monkey attack") @@ -559,9 +802,14 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) dust_mob(user, cause = "alien attack") /obj/machinery/power/supermatter_crystal/attack_animal(mob/living/simple_animal/S) + var/murder + if(!S.melee_damage_upper && !S.melee_damage_lower) + murder = S.friendly_verb_continuous + else + murder = S.attack_verb_continuous dust_mob(S, \ - "[S] unwisely [S.attack_verb_continuous] [src], and [S.p_their()] body burns brilliantly before flashing into ash!", \ - "You unwisely [S.attack_verb_simple] [src], and your vision glows brightly as your body crumbles to dust. Oops.", \ + "[S] unwisely [murder] [src], and [S.p_their()] body burns brilliantly before flashing into ash!", \ + "You unwisely touch [src], and your vision glows brightly as your body crumbles to dust. Oops.", \ "simple animal attack") /obj/machinery/power/supermatter_crystal/attack_robot(mob/user) @@ -571,65 +819,103 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) /obj/machinery/power/supermatter_crystal/attack_ai(mob/user) return -/obj/machinery/power/supermatter_crystal/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags) +/obj/machinery/power/supermatter_crystal/on_attack_hand(mob/living/user) . = ..() - if(.) - return dust_mob(user, cause = "hand") /obj/machinery/power/supermatter_crystal/proc/dust_mob(mob/living/nom, vis_msg, mob_msg, cause) - if(nom.incorporeal_move || nom.status_flags & GODMODE) + if(nom.incorporeal_move || nom.status_flags & GODMODE) //try to keep supermatter sliver's + hemostat's dust conditions in sync with this too return if(!vis_msg) - vis_msg = "[nom] reaches out and touches [src], inducing a resonance... [nom.p_their()] body starts to glow and bursts into flames before flashing into ash" + vis_msg = "[nom] reaches out and touches [src], inducing a resonance... [nom.p_their()] body starts to glow and burst into flames before flashing into dust!" if(!mob_msg) mob_msg = "You reach out and touch [src]. Everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"" if(!cause) cause = "contact" - nom.visible_message(vis_msg, mob_msg, "You hear an unearthly noise as a wave of heat washes over you.") + nom.visible_message(vis_msg, mob_msg, "You hear an unearthly noise as a wave of heat washes over you.") investigate_log("has been attacked ([cause]) by [key_name(nom)]", INVESTIGATE_SUPERMATTER) - playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1) + playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE) Consume(nom) /obj/machinery/power/supermatter_crystal/attackby(obj/item/W, mob/living/user, params) if(!istype(W) || (W.item_flags & ABSTRACT) || !istype(user)) return - if (istype(W, /obj/item/melee/roastingstick)) + if(istype(W, /obj/item/melee/roastingstick)) return ..() + if(istype(W, /obj/item/clothing/mask/cigarette)) + var/obj/item/clothing/mask/cigarette/cig = W + var/clumsy = HAS_TRAIT(user, TRAIT_CLUMSY) + if(clumsy) + var/which_hand = BODY_ZONE_L_ARM + if(!(user.active_hand_index % 2)) + which_hand = BODY_ZONE_R_ARM + var/obj/item/bodypart/dust_arm = user.get_bodypart(which_hand) + dust_arm.dismember() + user.visible_message("The [W] flashes out of existence on contact with \the [src], resonating with a horrible sound...",\ + "Oops! The [W] flashes out of existence on contact with \the [src], taking your arm with it! That was clumsy of you!") + playsound(src, 'sound/effects/supermatter.ogg', 150, TRUE) + Consume(dust_arm) + qdel(W) + return + if(cig.lit || user.a_intent != INTENT_HELP) + user.visible_message("A hideous sound echoes as [W] is ashed out on contact with \the [src]. That didn't seem like a good idea...") + playsound(src, 'sound/effects/supermatter.ogg', 150, TRUE) + Consume(W) + radiation_pulse(src, 150, 4) + return ..() + else + cig.light() + user.visible_message("As [user] lights \their [W] on \the [src], silence fills the room...",\ + "Time seems to slow to a crawl as you touch \the [src] with \the [W].\n\The [W] flashes alight with an eerie energy as you nonchalantly lift your hand away from \the [src]. Damn.") + playsound(src, 'sound/effects/supermatter.ogg', 50, TRUE) + radiation_pulse(src, 50, 3) + return if(istype(W, /obj/item/scalpel/supermatter)) + var/obj/item/scalpel/supermatter/scalpel = W to_chat(user, "You carefully begin to scrape \the [src] with \the [W]...") if(W.use_tool(src, user, 60, volume=100)) - to_chat(user, "You extract a sliver from \the [src]. \The [src] begins to react violently!") - new /obj/item/nuke_core/supermatter_sliver(drop_location()) - matter_power += 200 + if (scalpel.usesLeft) + to_chat(user, "You extract a sliver from \the [src]. \The [src] begins to react violently!") + new /obj/item/nuke_core/supermatter_sliver(drop_location()) + matter_power += 800 + scalpel.usesLeft-- + if (!scalpel.usesLeft) + to_chat(user, "A tiny piece of \the [W] falls off, rendering it useless!") + else + to_chat(user, "You fail to extract a sliver from \The [src]! \the [W] isn't sharp enough anymore.") else if(user.dropItemToGround(W)) user.visible_message("As [user] touches \the [src] with \a [W], silence fills the room...",\ "You touch \the [src] with \the [W], and everything suddenly goes silent.\n\The [W] flashes into dust as you flinch away from \the [src].",\ - "Everything suddenly goes silent.") + "Everything suddenly goes silent.") investigate_log("has been attacked ([W]) by [key_name(user)]", INVESTIGATE_SUPERMATTER) Consume(W) - playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1) + playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE) radiation_pulse(src, 150, 4) + else if(Adjacent(user)) //if the item is stuck to the person, kill the person too instead of eating just the item. + var/vis_msg = "[user] reaches out and touches [src] with [W], inducing a resonance... [W] starts to glow briefly before the light continues up to [user]'s body. [user.p_they(TRUE)] bursts into flames before flashing into dust!" + var/mob_msg = "You reach out and touch [src] with [W]. Everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"" + dust_mob(user, vis_msg, mob_msg) + /obj/machinery/power/supermatter_crystal/wrench_act(mob/user, obj/item/tool) + ..() if (moveable) default_unfasten_wrench(user, tool, time = 20) return TRUE /obj/machinery/power/supermatter_crystal/Bumped(atom/movable/AM) if(isliving(AM)) - AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [AM.p_their()] body starts to glow and catch flame before flashing into ash.",\ + AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... [AM.p_their()] body starts to glow and burst into flames before flashing into dust!",\ "You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\ - "You hear an unearthly noise as a wave of heat washes over you.") + "You hear an unearthly noise as a wave of heat washes over you.") else if(isobj(AM) && !iseffect(AM)) AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.", null,\ - "You hear a loud crack as you are washed with a wave of heat.") + "You hear a loud crack as you are washed with a wave of heat.") else return - playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, 1) - + playsound(get_turf(src), 'sound/effects/supermatter.ogg', 50, TRUE) Consume(AM) /obj/machinery/power/supermatter_crystal/intercept_zImpact(atom/movable/AM, levels) @@ -645,7 +931,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) message_admins("[src] has consumed [key_name_admin(user)] [ADMIN_JMP(src)].") investigate_log("has consumed [key_name(user)].", INVESTIGATE_SUPERMATTER) user.dust(force = TRUE) - matter_power += 200 + if(power_changes) + matter_power += 200 else if(istype(AM, /obj/singularity)) return else if(isobj(AM)) @@ -656,19 +943,29 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) message_admins("[src] has consumed [AM], [suspicion] [ADMIN_JMP(src)].") investigate_log("has consumed [AM] - [suspicion].", INVESTIGATE_SUPERMATTER) qdel(AM) - if(!iseffect(AM)) + if(!iseffect(AM) && power_changes) matter_power += 200 //Some poor sod got eaten, go ahead and irradiate people nearby. radiation_pulse(src, 3000, 2, TRUE) - var/list/viewers = fov_viewers(world.view, src) for(var/mob/living/L in range(10)) investigate_log("has irradiated [key_name(L)] after consuming [AM].", INVESTIGATE_SUPERMATTER) + var/list/viewers = fov_viewers(world.view, src) if(L in viewers) L.show_message("As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.", MSG_VISUAL,\ "The unearthly ringing subsides and you notice you have new radiation burns.", MSG_AUDIBLE) else - L.show_message("You hear an unearthly ringing and notice your skin is covered in fresh radiation burns.", MSG_AUDIBLE) + L.show_message("You hear an unearthly ringing and notice your skin is covered in fresh radiation burns.", MSG_AUDIBLE) + +/obj/machinery/power/supermatter_crystal/proc/consume_turf(turf/T) + var/oldtype = T.type + var/turf/newT = T.ScrapeAway() + if(newT.type == oldtype) + return + playsound(T, 'sound/effects/supermatter.ogg', 50, 1) + T.visible_message("[T] smacks into [src] and rapidly flashes to ash.",\ + "You hear a loud crack as you are washed with a wave of heat.") + CALCULATE_ADJACENT_TURFS(T) //Do not blow up our internal radio /obj/machinery/power/supermatter_crystal/contents_explosion(severity, target) @@ -685,6 +982,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) anchored = FALSE gasefficency = 0.125 explosion_power = 12 + layer = ABOVE_MOB_LAYER moveable = TRUE /obj/machinery/power/supermatter_crystal/shard/engine @@ -699,6 +997,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) name = "anchored supermatter shard" takes_damage = FALSE produces_gas = FALSE + power_changes = FALSE + processes = FALSE //SHUT IT DOWN moveable = FALSE anchored = TRUE @@ -707,101 +1007,183 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal) base_icon_state = "darkmatter" icon_state = "darkmatter" -/obj/machinery/power/supermatter_crystal/proc/supermatter_pull(turf/center, pull_range = 10) - playsound(src.loc, 'sound/weapons/marauder.ogg', 100, 1, extrarange = 7) - for(var/atom/P in orange(pull_range,center)) - if(ismovable(P)) - var/atom/movable/pulled_object = P - if(ishuman(P)) - var/mob/living/carbon/human/H = P - H.apply_effect(40, EFFECT_KNOCKDOWN, 0) - if(pulled_object && !pulled_object.anchored && !ishuman(P)) - step_towards(pulled_object,center) - step_towards(pulled_object,center) - step_towards(pulled_object,center) - step_towards(pulled_object,center) +/obj/machinery/power/supermatter_crystal/proc/supermatter_pull(turf/center, pull_range = 3) + playsound(center, 'sound/weapons/marauder.ogg', 100, TRUE, extrarange = pull_range - world.view) + for(var/atom/movable/P in orange(pull_range,center)) + if((P.anchored || P.move_resist >= MOVE_FORCE_EXTREMELY_STRONG)) //move resist memes. + if(istype(P, /obj/structure/closet)) + var/obj/structure/closet/toggle = P + toggle.open() + continue + if(ismob(P)) + var/mob/M = P + if(M.mob_negates_gravity()) + continue //You can't pull someone nailed to the deck + step_towards(P,center) /obj/machinery/power/supermatter_crystal/proc/supermatter_anomaly_gen(turf/anomalycenter, type = FLUX_ANOMALY, anomalyrange = 5) var/turf/L = pick(orange(anomalyrange, anomalycenter)) if(L) switch(type) if(FLUX_ANOMALY) - var/obj/effect/anomaly/flux/A = new(L, 300) + var/obj/effect/anomaly/flux/A = new(L, 300, FALSE) A.explosive = FALSE if(GRAVITATIONAL_ANOMALY) - new /obj/effect/anomaly/grav(L, 250) + new /obj/effect/anomaly/grav(L, 250, FALSE) if(PYRO_ANOMALY) - new /obj/effect/anomaly/pyro(L, 200) + new /obj/effect/anomaly/pyro(L, 200, FALSE) -/obj/machinery/power/supermatter_crystal/proc/supermatter_zap(atom/zapstart, range = 3, power) - . = zapstart.dir - if(power < 1000) +/obj/machinery/power/supermatter_crystal/proc/supermatter_zap(atom/zapstart = src, range = 5, zap_str = 4000, zap_flags = ZAP_SUPERMATTER_FLAGS, list/targets_hit = list()) + if(QDELETED(zapstart)) return + . = zapstart.dir + //If the strength of the zap decays past the cutoff, we stop + if(zap_str < zap_cutoff) + return + var/atom/target + var/target_type = LOWEST + var/list/arctargets = list() + //Making a new copy so additons further down the recursion do not mess with other arcs + //Lets put this ourself into the do not hit list, so we don't curve back to hit the same thing twice with one arc + for(var/test in oview(zapstart, range)) + if(!(zap_flags & ZAP_ALLOW_DUPLICATES) && LAZYACCESS(targets_hit, test)) + continue - var/target_atom - var/mob/living/target_mob - var/obj/machinery/target_machine - var/obj/structure/target_structure - var/list/arctargetsmob = list() - var/list/arctargetsmachine = list() - var/list/arctargetsstructure = list() + if(istype(test, /obj/vehicle/ridden/bicycle/)) + var/obj/vehicle/ridden/bicycle/bike = test + if(!(bike.obj_flags & BEING_SHOCKED) && bike.can_buckle)//God's not on our side cause he hates idiots. + if(target_type != BIKE) + arctargets = list() + arctargets += test + target_type = BIKE - if(prob(20)) //let's not hit all the engineers with every beam and/or segment of the arc - for(var/mob/living/Z in oview(zapstart, range+2)) - arctargetsmob += Z - if(arctargetsmob.len) - var/mob/living/H = pick(arctargetsmob) - var/atom/A = H - target_mob = H - target_atom = A + if(target_type > COIL) + continue - else - for(var/obj/machinery/X in oview(zapstart, range+2)) - arctargetsmachine += X - if(arctargetsmachine.len) - var/obj/machinery/M = pick(arctargetsmachine) - var/atom/A = M - target_machine = M - target_atom = A + if(istype(test, /obj/machinery/power/tesla_coil/)) + var/obj/machinery/power/tesla_coil/coil = test + if(coil.anchored && !(coil.obj_flags & BEING_SHOCKED) && !coil.panel_open && prob(70))//Diversity of death + if(target_type != COIL) + arctargets = list() + arctargets += test + target_type = COIL - else - for(var/obj/structure/Y in oview(zapstart, range+2)) - arctargetsstructure += Y - if(arctargetsstructure.len) - var/obj/structure/O = pick(arctargetsstructure) - var/atom/A = O - target_structure = O - target_atom = A + if(target_type > ROD) + continue - if(target_atom) - zapstart.Beam(target_atom, icon_state="nzcrentrs_power", time=5) - var/zapdir = get_dir(zapstart, target_atom) + if(istype(test, /obj/machinery/power/grounding_rod/)) + var/obj/machinery/power/grounding_rod/rod = test + //We're adding machine damaging effects, rods need to be surefire + if(rod.anchored && !rod.panel_open) + if(target_type != ROD) + arctargets = list() + arctargets += test + target_type = ROD + + if(target_type > LIVING) + continue + + if(istype(test, /mob/living/)) + var/mob/living/alive = test + if(!(HAS_TRAIT(alive, TRAIT_TESLA_SHOCKIMMUNE)) && !(alive.flags_1 & SHOCKED_1) && alive.stat != DEAD && prob(20))//let's not hit all the engineers with every beam and/or segment of the arc + if(target_type != LIVING) + arctargets = list() + arctargets += test + target_type = LIVING + + if(target_type > MACHINERY) + continue + + if(istype(test, /obj/machinery/)) + var/obj/machinery/machine = test + if(!(machine.obj_flags & BEING_SHOCKED) && prob(40)) + if(target_type != MACHINERY) + arctargets = list() + arctargets += test + target_type = MACHINERY + + if(target_type > OBJECT) + continue + + if(istype(test, /obj/)) + var/obj/object = test + if(!(object.obj_flags & BEING_SHOCKED)) + if(target_type != OBJECT) + arctargets = list() + arctargets += test + target_type = OBJECT + + if(arctargets.len)//Pick from our pool + target = pick(arctargets) + + if(!QDELETED(target))//If we found something + //Do the animation to zap to it from here + if(!(zap_flags & ZAP_ALLOW_DUPLICATES)) + LAZYSET(targets_hit, target, TRUE) + zapstart.Beam(target, icon_state=zap_icon, time=5) + var/zapdir = get_dir(zapstart, target) if(zapdir) . = zapdir - if(target_mob) - target_mob.electrocute_act(rand(5,10), "Supermatter Discharge Bolt", 1, SHOCK_NOSTUN) - if(prob(15)) - supermatter_zap(target_mob, 5, power / 2) - supermatter_zap(target_mob, 5, power / 2) - else - supermatter_zap(target_mob, 5, power / 1.5) + //Going boom should be rareish + if(prob(80)) + zap_flags &= ~ZAP_MACHINE_EXPLOSIVE + if(target_type == COIL) + //In the best situation we can expect this to grow up to 2120kw before a delam/IT'S GONE TOO FAR FRED SHUT IT DOWN + //The formula for power gen is zap_str * zap_mod / 2 * capacitor rating, between 1 and 4 + var/multi = 10 + switch(power)//Between 7k and 9k it's 20, above that it's 40 + if(SEVERE_POWER_PENALTY_THRESHOLD to CRITICAL_POWER_PENALTY_THRESHOLD) + multi = 20 + if(CRITICAL_POWER_PENALTY_THRESHOLD to INFINITY) + multi = 40 + target.zap_act(zap_str * multi, zap_flags, list()) + zap_str /= 3 //Coils should take a lot out of the power of the zap - else if(target_machine) - if(prob(15)) - supermatter_zap(target_machine, 5, power / 2) - supermatter_zap(target_machine, 5, power / 2) - else - supermatter_zap(target_machine, 5, power / 1.5) + else if(target_type == ROD) + //We can expect this to do very little, maybe shock the poor soul buckled to it, but that's all. + //This is one of our endpoints, if the bolt hits a grounding rod, it stops jumping + target.zap_act(zap_str, zap_flags, list()) + return + + else if(isliving(target))//If we got a fleshbag on our hands + var/mob/living/creature = target + creature.set_shocked() + addtimer(CALLBACK(creature, /mob/living/proc/reset_shocked), 10) + //3 shots a human with no resistance. 2 to crit, one to death. This is at at least 10000 power. + //There's no increase after that because the input power is effectivly capped at 10k + //Does 1.5 damage at the least + var/shock_damage = ((zap_flags & ZAP_MOB_DAMAGE) ? (power / 200) - 10 : rand(5,10)) + creature.electrocute_act(shock_damage, "Supermatter Discharge Bolt", 1, ((zap_flags & ZAP_MOB_STUN) ? SHOCK_TESLA : SHOCK_NOSTUN)) + zap_str /= 1.5 //Meatsacks are conductive, makes working in pairs more destructive - else if(target_structure) - if(prob(15)) - supermatter_zap(target_structure, 5, power / 2) - supermatter_zap(target_structure, 5, power / 2) else - supermatter_zap(target_structure, 5, power / 1.5) + target.zap_act(zap_str, zap_flags, list()) + zap_str /= 2 // worse then living things, better then coils + //This gotdamn variable is a boomer and keeps giving me problems + var/turf/T = get_turf(target) + var/pressure = 1 + if(T && T.return_air()) + pressure = max(1,T.return_air().return_pressure()) + //We get our range with the strength of the zap and the pressure, the higher the former and the lower the latter the better + var/new_range = clamp(zap_str / pressure * 10, 2, 7) + var/zap_count = 1 + if(prob(5)) + zap_str -= (zap_str/10) + zap_count += 1 + for(var/j in 1 to zap_count) + if(zap_count > 1) + targets_hit = targets_hit.Copy() //Pass by ref begone + supermatter_zap(target, new_range, zap_str, zap_flags, targets_hit) #undef HALLUCINATION_RANGE #undef GRAVITATIONAL_ANOMALY #undef FLUX_ANOMALY #undef PYRO_ANOMALY +#undef BIKE +#undef COIL +#undef ROD +#undef LIVING +#undef MACHINERY +#undef OBJECT +#undef LOWEST diff --git a/code/modules/procedural_mapping/mapGenerator.dm b/code/modules/procedural_mapping/mapGenerator.dm index f509c409ce..323f74d0ef 100644 --- a/code/modules/procedural_mapping/mapGenerator.dm +++ b/code/modules/procedural_mapping/mapGenerator.dm @@ -147,8 +147,16 @@ set category = "Debug" var/datum/mapGenerator/nature/N = new() - var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text - var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text + var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text|null + + if (isnull(startInput)) + return + + var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text|null + + if (isnull(endInput)) + return + //maxx maxy and current z so that if you fuck up, you only fuck up one entire z level instead of the entire universe if(!startInput || !endInput) to_chat(src, "Missing Input") diff --git a/code/modules/projectiles/ammunition/_firing.dm b/code/modules/projectiles/ammunition/_firing.dm index 0ef4c680aa..437b7dcc5a 100644 --- a/code/modules/projectiles/ammunition/_firing.dm +++ b/code/modules/projectiles/ammunition/_firing.dm @@ -36,6 +36,14 @@ if(isgun(fired_from)) var/obj/item/gun/G = fired_from BB.damage *= G.projectile_damage_multiplier + if(HAS_TRAIT(user, TRAIT_INSANE_AIM)) + BB.ricochets_max = max(BB.ricochets_max, 10) //bouncy! + BB.ricochet_chance = max(BB.ricochet_chance, 100) //it wont decay so we can leave it at 100 for always bouncing + BB.ricochet_auto_aim_range = max(BB.ricochet_auto_aim_range, 3) + BB.ricochet_auto_aim_angle = max(BB.ricochet_auto_aim_angle, 360) //it can turn full circle and shoot you in the face because our aim? is insane. + BB.ricochet_decay_chance = 0 + BB.ricochet_decay_damage = max(BB.ricochet_decay_damage, 0.1) + BB.ricochet_incidence_leeway = 0 if(reagents && BB.reagents) reagents.trans_to(BB, reagents.total_volume) //For chemical darts/bullets diff --git a/code/modules/projectiles/ammunition/ballistic/revolver.dm b/code/modules/projectiles/ammunition/ballistic/revolver.dm index 693b258e3d..c13a3c953d 100644 --- a/code/modules/projectiles/ammunition/ballistic/revolver.dm +++ b/code/modules/projectiles/ammunition/ballistic/revolver.dm @@ -14,9 +14,13 @@ /obj/item/ammo_casing/a357/match name = ".357 match bullet casing" desc = "A .357 bullet casing, manufactured to exceedingly high standards." - caliber = "357" projectile_type = /obj/item/projectile/bullet/a357/match +/obj/item/ammo_casing/a357/dumdum + name = ".357 DumDum bullet casing" + desc = "A .357 bullet casing. Usage of this ammunition will constitute a war crime in your area." + projectile_type = /obj/item/projectile/bullet/a357/dumdum + // 7.62x38mmR (Nagant Revolver) /obj/item/ammo_casing/n762 @@ -68,4 +72,4 @@ /obj/item/ammo_casing/c38/dumdum name = ".38 DumDum bullet casing" desc = "A .38 DumDum bullet casing." - projectile_type = /obj/item/projectile/bullet/c38/dumdum \ No newline at end of file + projectile_type = /obj/item/projectile/bullet/c38/dumdum diff --git a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm index 0d9183d1a4..0b00c89c02 100644 --- a/code/modules/projectiles/boxes_magazines/ammo_boxes.dm +++ b/code/modules/projectiles/boxes_magazines/ammo_boxes.dm @@ -16,6 +16,11 @@ name = "speed loader (.357 AP)" ammo_type = /obj/item/ammo_casing/a357/ap +/obj/item/ammo_box/a357/dumdum + name = "speed loader (.357 DumDum)" + desc = "Designed to quickly reload revolvers. Usage of these rounds will constitute a war crime in your area." + ammo_type = /obj/item/ammo_casing/a357/dumdum + /obj/item/ammo_box/c38 name = "speed loader (.38 rubber)" desc = "Designed to quickly reload revolvers." diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index c3248bab2f..ee073dbfcd 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -29,7 +29,7 @@ trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers var/sawn_desc = null //description change if weapon is sawn-off var/sawn_off = FALSE - + /// can we be put into a turret var/can_turret = TRUE /// can we be put in a circuit @@ -310,8 +310,6 @@ randomized_gun_spread = rand(0, spread) else if(burst_size > 1 && burst_spread) randomized_gun_spread = rand(0, burst_spread) - if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex - bonus_spread += 25 var/randomized_bonus_spread = rand(0, bonus_spread) if(burst_size > 1) @@ -603,10 +601,16 @@ var/penalty = (last_fire + GUN_AIMING_TIME + fire_delay) - world.time if(penalty > 0) //Yet we only penalize users firing it multiple times in a haste. fire_delay isn't necessarily cumbersomeness. aiming_delay = penalty - if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE)) //To be removed in favor of something less tactless later. + if(SEND_SIGNAL(user, COMSIG_COMBAT_MODE_CHECK, COMBAT_MODE_ACTIVE) || HAS_TRAIT(user, TRAIT_INSANE_AIM)) //To be removed in favor of something less tactless later. base_inaccuracy /= 1.5 if(stamloss > STAMINA_NEAR_SOFTCRIT) //This can null out the above bonus. base_inaccuracy *= 1 + (stamloss - STAMINA_NEAR_SOFTCRIT)/(STAMINA_NEAR_CRIT - STAMINA_NEAR_SOFTCRIT)*0.5 + if(HAS_TRAIT(user, TRAIT_POOR_AIM)) //nice shootin' tex + if(!HAS_TRAIT(user, TRAIT_INSANE_AIM)) + bonus_spread += 25 + else + //you have both poor aim and insane aim, why? + bonus_spread += rand(0,50) var/mult = max((GUN_AIMING_TIME + aiming_delay + user.last_click_move - world.time)/GUN_AIMING_TIME, -0.5) //Yes, there is a bonus for taking time aiming. if(mult < 0) //accurate weapons should provide a proper bonus with negative inaccuracy. the opposite is true too. mult *= 1/inaccuracy_modifier diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 43384280d4..99a0bedc4d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -327,16 +327,18 @@ if(!trajectory) return var/turf/T = get_turf(A) - if(check_ricochet(A) && A.handle_ricochet(src)) //if you can ricochet, attempt to ricochet off the object - on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above - var/datum/point/pcache = trajectory.copy_to() - if(hitscan) - store_hitscan_collision(pcache) - decayedRange = max(0, decayedRange - reflect_range_decrease) - ricochet_chance *= ricochet_decay_chance - damage *= ricochet_decay_damage - range = decayedRange - return TRUE + if(check_ricochet_flag(A) && check_ricochet(A)) //if you can ricochet, attempt to ricochet off the object + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) //if allowed, use autoaim to ricochet into someone, otherwise default to ricocheting off the object from above + var/datum/point/pcache = trajectory.copy_to() + if(hitscan) + store_hitscan_collision(pcache) + decayedRange = max(0, decayedRange - reflect_range_decrease) + ricochet_chance *= ricochet_decay_chance + damage *= ricochet_decay_damage + range = decayedRange + return TRUE var/distance = get_dist(T, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. if(def_zone && check_zone(def_zone) != BODY_ZONE_CHEST) @@ -680,7 +682,8 @@ if(!ignore_source_check && firer) var/mob/M = firer if((target == firer) || ((target == firer.loc) && ismecha(firer.loc)) || (target in firer.buckled_mobs) || (istype(M) && (M.buckled == target))) - return FALSE + if(!ricochets) //if it has ricocheted, it can hit the firer. + return FALSE if(!ignore_loc && (loc != target.loc)) return FALSE if(target in passthrough) diff --git a/code/modules/projectiles/projectile/bullets/revolver.dm b/code/modules/projectiles/projectile/bullets/revolver.dm index 95d43ba1ce..ec3cadc31a 100644 --- a/code/modules/projectiles/projectile/bullets/revolver.dm +++ b/code/modules/projectiles/projectile/bullets/revolver.dm @@ -126,4 +126,15 @@ ricochet_auto_aim_angle = 50 ricochet_auto_aim_range = 6 ricochet_incidence_leeway = 80 - ricochet_decay_chance = 1 \ No newline at end of file + ricochet_decay_chance = 1 + +/obj/item/projectile/bullet/a357/dumdum + name = ".357 DumDum bullet" // the warcrime bullet + damage = 40 + armour_penetration = -20 + wound_bonus = 45 + bare_wound_bonus = 45 + sharpness = SHARP_EDGED + embedding = list(embed_chance=90, fall_chance=2, jostle_chance=5, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10) + wound_falloff_tile = -1 + embed_falloff_tile = -5 diff --git a/code/modules/projectiles/projectile/special/plasma.dm b/code/modules/projectiles/projectile/special/plasma.dm index 33559fa92c..77509cb574 100644 --- a/code/modules/projectiles/projectile/special/plasma.dm +++ b/code/modules/projectiles/projectile/special/plasma.dm @@ -2,7 +2,7 @@ name = "plasma blast" icon_state = "plasmacutter" damage_type = BRUTE - damage = 20 + damage = 10 range = 4 dismemberment = 20 impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser @@ -32,12 +32,12 @@ return BULLET_ACT_FORCE_PIERCE /obj/item/projectile/plasma/adv - damage = 28 + damage = 14 range = 5 mine_range = 5 /obj/item/projectile/plasma/adv/mech - damage = 40 + damage = 20 range = 9 mine_range = 3 @@ -52,4 +52,4 @@ dismemberment = 0 damage = 10 range = 4 - mine_range = 0 \ No newline at end of file + mine_range = 0 diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index b6de7b2eab..b60ee8d108 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -883,7 +883,7 @@ if(istype(O, /obj/item/stack/sheet/metal)) var/obj/item/stack/sheet/metal/M = O reac_volume = min(reac_volume, M.amount) - new/obj/item/stack/tile/bronze(get_turf(M), reac_volume) + new/obj/item/stack/sheet/bronze(get_turf(M), reac_volume) M.use(reac_volume) /datum/reagent/nitrogen @@ -2331,6 +2331,32 @@ M.adjustStaminaLoss(-0.25*REM) // the more wounds, the more stamina regen ..() +datum/reagent/eldritch + name = "Eldritch Essence" + description = "Strange liquid that defies the laws of physics" + taste_description = "Ag'hsj'saje'sh" + color = "#1f8016" + +/datum/reagent/eldritch/on_mob_life(mob/living/carbon/M) + if(IS_HERETIC(M)) + M.drowsyness = max(M.drowsyness-5, 0) + M.AdjustAllImmobility(-40, FALSE) + M.adjustStaminaLoss(-15, FALSE) + M.adjustToxLoss(-3, FALSE) + M.adjustOxyLoss(-3, FALSE) + M.adjustBruteLoss(-3, FALSE) + M.adjustFireLoss(-3, FALSE) + if(ishuman(M) && M.blood_volume < BLOOD_VOLUME_NORMAL) + M.blood_volume += 3 + else + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150) + M.adjustToxLoss(2, FALSE) + M.adjustFireLoss(2, FALSE) + M.adjustOxyLoss(2, FALSE) + M.adjustBruteLoss(2, FALSE) + holder.remove_reagent(type, 1) + return TRUE + /datum/reagent/cellulose name = "Cellulose Fibers" description = "A crystaline polydextrose polymer, plants swear by this stuff." diff --git a/code/modules/research/nanites/nanite_cloud_controller.dm b/code/modules/research/nanites/nanite_cloud_controller.dm index 1a0a5dbd4d..44ebe11c29 100644 --- a/code/modules/research/nanites/nanite_cloud_controller.dm +++ b/code/modules/research/nanites/nanite_cloud_controller.dm @@ -36,8 +36,9 @@ /obj/machinery/computer/nanite_cloud_controller/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user))// ||!user.put_in_active_hand(disk)) - disk.forceMove(drop_location()) + disk.forceMove(drop_location()) + if(istype(user) && user.Adjacent(src)) + user.put_in_active_hand(disk) disk = null /obj/machinery/computer/nanite_cloud_controller/proc/get_backup(cloud_id) diff --git a/code/modules/research/nanites/nanite_program_hub.dm b/code/modules/research/nanites/nanite_program_hub.dm index 5a330f7a4f..495c788845 100644 --- a/code/modules/research/nanites/nanite_program_hub.dm +++ b/code/modules/research/nanites/nanite_program_hub.dm @@ -53,8 +53,9 @@ /obj/machinery/nanite_program_hub/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk)) - disk.forceMove(drop_location()) + disk.forceMove(drop_location()) + if(istype(user) && Adjacent(user)) + user.put_in_active_hand(disk) disk = null /obj/machinery/nanite_program_hub/AltClick(mob/user) diff --git a/code/modules/research/nanites/nanite_programmer.dm b/code/modules/research/nanites/nanite_programmer.dm index 804f256cf6..b6a2c8b28b 100644 --- a/code/modules/research/nanites/nanite_programmer.dm +++ b/code/modules/research/nanites/nanite_programmer.dm @@ -39,8 +39,9 @@ /obj/machinery/nanite_programmer/proc/eject(mob/living/user) if(!disk) return - if(!istype(user) || !Adjacent(user))// || !user.put_in_active_hand(disk)) - disk.forceMove(drop_location()) + disk.forceMove(drop_location()) + if(istype(user) && user.Adjacent(src)) + user.put_in_active_hand(disk) disk = null program = null diff --git a/code/modules/ruins/lavaland_ruin_code.dm b/code/modules/ruins/lavaland_ruin_code.dm index 45b6939f42..e572f1ee02 100644 --- a/code/modules/ruins/lavaland_ruin_code.dm +++ b/code/modules/ruins/lavaland_ruin_code.dm @@ -93,7 +93,7 @@ /obj/item/stack/sheet/mineral/adamantine = /datum/species/golem/adamantine, /obj/item/stack/sheet/plastic = /datum/species/golem/plastic, /obj/item/stack/tile/brass = /datum/species/golem/clockwork, - /obj/item/stack/tile/bronze = /datum/species/golem/bronze, + /obj/item/stack/sheet/bronze = /datum/species/golem/bronze, /obj/item/stack/sheet/cardboard = /datum/species/golem/cardboard, /obj/item/stack/sheet/leather = /datum/species/golem/leather, /obj/item/stack/sheet/bone = /datum/species/golem/bone, diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 57502e2cd5..2272a14612 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -224,7 +224,15 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th /obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf return -/obj/effect/proc_holder/spell/proc/can_target(mob/living/target) +/** + * can_target: Checks if we are allowed to cast the spell on a target. + * + * Arguments: + * * target The atom that is being targeted by the spell. + * * user The mob using the spell. + * * silent If the checks should not give any feedback messages. + */ +/obj/effect/proc_holder/spell/proc/can_target(atom/target, mob/user, silent = FALSE) return TRUE /obj/effect/proc_holder/spell/proc/start_recharge() @@ -296,6 +304,13 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th /obj/effect/proc_holder/spell/proc/cast(list/targets,mob/user = usr) return +/obj/effect/proc_holder/spell/proc/view_or_range(distance = world.view, center=usr, type="view") + switch(type) + if("view") + . = view(distance,center) + if("range") + . = range(distance,center) + /obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge switch(charge_type) if("recharge") @@ -345,7 +360,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th switch(max_targets) if(0) //unlimited for(var/mob/living/target in view_or_range(range, user, selection_type)) - if(!can_target(target)) + if(!can_target(target, user, TRUE)) continue targets += target if(1) //single target can be picked @@ -357,7 +372,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th for(var/mob/living/M in view_or_range(range, user, selection_type)) if(!include_user && user == M) continue - if(!can_target(M)) + if(!can_target(M, user, TRUE)) continue possible_targets += M @@ -365,7 +380,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th //Adds a safety check post-input to make sure those targets are actually in range. var/mob/M if(!random_target) - M = input("Choose the target for the spell.", "Targeting") as null|mob in possible_targets + M = input("Choose the target for the spell.", "Targeting") as null|mob in sortNames(possible_targets) else switch(random_target_priority) if(TARGET_RANDOM) @@ -385,7 +400,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th else var/list/possible_targets = list() for(var/mob/living/target in view_or_range(range, user, selection_type)) - if(!can_target(target)) + if(!can_target(target, user, TRUE)) continue possible_targets += target for(var/i=1,i<=max_targets,i++) @@ -411,7 +426,7 @@ GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) //needed for th var/list/targets = list() for(var/turf/target in view_or_range(range,user,selection_type)) - if(!can_target(target)) + if(!can_target(target, user, TRUE)) continue if(!(target in view_or_range(inner_radius,user,selection_type))) targets += target diff --git a/code/modules/spells/spell_types/cone_spells.dm b/code/modules/spells/spell_types/cone_spells.dm new file mode 100644 index 0000000000..63bae4b7cf --- /dev/null +++ b/code/modules/spells/spell_types/cone_spells.dm @@ -0,0 +1,117 @@ +/obj/effect/proc_holder/spell/cone + name = "Cone of Nothing" + desc = "Does nothing in a cone! Wow!" + school = "evocation" + charge_max = 100 + clothes_req = FALSE + invocation = "FUKAN NOTHAN" + invocation_type = "shout" + sound = 'sound/magic/forcewall.ogg' + action_icon_state = "shield" + range = -1 + cooldown_min = 0.5 SECONDS + ///This controls how many levels the cone has, increase this value to make a bigger cone. + var/cone_levels = 3 + ///This value determines if the cone penetrates walls. + var/respect_density = FALSE + +/obj/effect/proc_holder/spell/cone/choose_targets(mob/user = usr) + perform(null, user=user) + +///This proc creates a list of turfs that are hit by the cone +/obj/effect/proc_holder/spell/cone/proc/cone_helper(var/turf/starter_turf, var/dir_to_use, var/cone_levels = 3) + var/list/turfs_to_return = list() + var/turf/turf_to_use = starter_turf + var/turf/left_turf + var/turf/right_turf + var/right_dir + var/left_dir + switch(dir_to_use) + if(NORTH) + left_dir = WEST + right_dir = EAST + if(SOUTH) + left_dir = EAST + right_dir = WEST + if(EAST) + left_dir = NORTH + right_dir = SOUTH + if(WEST) + left_dir = SOUTH + right_dir = NORTH + + + for(var/i in 1 to cone_levels) + var/list/level_turfs = list() + turf_to_use = get_step(turf_to_use, dir_to_use) + level_turfs += turf_to_use + if(i != 1) + left_turf = get_step(turf_to_use, left_dir) + level_turfs += left_turf + right_turf = get_step(turf_to_use, right_dir) + level_turfs += right_turf + for(var/left_i in 1 to i -calculate_cone_shape(i)) + if(left_turf.density && respect_density) + break + left_turf = get_step(left_turf, left_dir) + level_turfs += left_turf + for(var/right_i in 1 to i -calculate_cone_shape(i)) + if(right_turf.density && respect_density) + break + right_turf = get_step(right_turf, right_dir) + level_turfs += right_turf + turfs_to_return += list(level_turfs) + if(i == cone_levels) + continue + if(turf_to_use.density && respect_density) + break + return turfs_to_return + +/obj/effect/proc_holder/spell/cone/cast(list/targets,mob/user = usr) + var/list/cone_turfs = cone_helper(get_turf(user), user.dir, cone_levels) + for(var/list/turf_list in cone_turfs) + do_cone_effects(turf_list) + +///This proc does obj, mob and turf cone effects on all targets in a list +/obj/effect/proc_holder/spell/cone/proc/do_cone_effects(list/target_turf_list, level) + for(var/target_turf in target_turf_list) + if(!target_turf) //if turf is no longer there + continue + do_turf_cone_effect(target_turf, level) + if(isopenturf(target_turf)) + var/turf/open/open_turf = target_turf + for(var/movable_content in open_turf) + if(isobj(movable_content)) + do_obj_cone_effect(movable_content, level) + else if(isliving(movable_content)) + do_mob_cone_effect(movable_content, level) + +///This proc deterimines how the spell will affect turfs. +/obj/effect/proc_holder/spell/cone/proc/do_turf_cone_effect(turf/target_turf, level) + return + +///This proc deterimines how the spell will affect objects. +/obj/effect/proc_holder/spell/cone/proc/do_obj_cone_effect(obj/target_obj, level) + return + +///This proc deterimines how the spell will affect mobs. +/obj/effect/proc_holder/spell/cone/proc/do_mob_cone_effect(mob/living/target_mob, level) + return + +///This proc adjusts the cones width depending on the level. +/obj/effect/proc_holder/spell/cone/proc/calculate_cone_shape(current_level) + var/end_taper_start = round(cone_levels * 0.8) + if(current_level > end_taper_start) + return (current_level % end_taper_start) * 2 //someone more talented and probably come up with a better formula. + else + return 2 + +///This type of cone gradually affects each level of the cone instead of affecting the entire area at once. +/obj/effect/proc_holder/spell/cone/staggered + +/obj/effect/proc_holder/spell/cone/staggered/cast(list/targets,mob/user = usr) + var/level_counter = 0 + var/list/cone_turfs = cone_helper(get_turf(user), user.dir, cone_levels) + for(var/list/turf_list in cone_turfs) + level_counter++ + addtimer(CALLBACK(src, .proc/do_cone_effects, turf_list, level_counter), 2 * level_counter) diff --git a/code/modules/spells/spell_types/dumbfire.dm b/code/modules/spells/spell_types/dumbfire.dm index 6931b4ac31..424c702a31 100644 --- a/code/modules/spells/spell_types/dumbfire.dm +++ b/code/modules/spells/spell_types/dumbfire.dm @@ -49,8 +49,8 @@ var/projectile_type = text2path(proj_type) projectile = new projectile_type(user) else if(istype(proj_type, /obj/effect/proc_holder/spell)) - projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) - var/obj/effect/proc_holder/spell/targeted/trigger/T = projectile + projectile = new /obj/effect/proc_holder/spell/pointed/trigger(user) + var/obj/effect/proc_holder/spell/pointed/trigger/T = projectile T.linked_spells += proj_type else projectile = new proj_type(user) diff --git a/code/modules/spells/spell_types/ethereal_jaunt.dm b/code/modules/spells/spell_types/ethereal_jaunt.dm index 8cf51d45c6..9d91b6534d 100644 --- a/code/modules/spells/spell_types/ethereal_jaunt.dm +++ b/code/modules/spells/spell_types/ethereal_jaunt.dm @@ -17,7 +17,7 @@ action_icon_state = "jaunt" /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets,mob/user = usr) //magnets, so mostly hardcoded - playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) + play_sound("enter",user) for(var/mob/living/target in targets) INVOKE_ASYNC(src, .proc/do_jaunt, target) @@ -42,7 +42,7 @@ ADD_TRAIT(target, TRAIT_MOBILITY_NOMOVE, src) target.update_mobility() holder.reappearing = 1 - playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) + play_sound("exit",target) sleep(25 - jaunt_in_time) new jaunt_in_type(mobloc, holder.dir) target.setDir(holder.dir) @@ -63,6 +63,13 @@ steam.set_up(10, 0, mobloc) steam.start() +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/play_sound(type,mob/living/target) + switch(type) + if("enter") + playsound(get_turf(target), 'sound/magic/ethereal_enter.ogg', 50, TRUE, -1) + if("exit") + playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) + /obj/effect/dummy/phased_mob/spell_jaunt name = "water" icon = 'icons/effects/effects.dmi' diff --git a/code/modules/spells/spell_types/mind_transfer.dm b/code/modules/spells/spell_types/mind_transfer.dm deleted file mode 100644 index d2ef015d1d..0000000000 --- a/code/modules/spells/spell_types/mind_transfer.dm +++ /dev/null @@ -1,88 +0,0 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer - name = "Mind Transfer" - desc = "This spell allows the user to switch bodies with a target." - - school = "transmutation" - charge_max = 600 - clothes_req = NONE - invocation = "GIN'YU CAPAN" - invocation_type = "whisper" - range = 1 - cooldown_min = 200 //100 deciseconds reduction per rank - var/unconscious_amount_caster = 400 //how much the caster is stunned for after the spell - var/unconscious_amount_victim = 400 //how much the victim is stunned for after the spell - - action_icon_state = "mindswap" - -/* -Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. -Make sure spells that are removed from spell_list are actually removed and deleted when mind transferring. -Also, you never added distance checking after target is selected. I've went ahead and did that. -*/ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/living/user = usr, distanceoverride, silent = FALSE) - if(!targets.len) - if(!silent) - to_chat(user, "No mind found!") - return - - if(targets.len > 1) - if(!silent) - to_chat(user, "Too many minds! You're not a hive damnit!") - return - - var/mob/living/target = targets[1] - - var/t_He = target.p_they(TRUE) - var/t_is = target.p_are() - - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - if(!silent) - to_chat(user, "[t_He] [t_is] too far away!") - return - - if(ismegafauna(target)) - if(!silent) - to_chat(user, "This creature is too powerful to control!") - return - - if(target.stat == DEAD) - if(!silent) - to_chat(user, "You don't particularly want to be dead!") - return - - if(!target.key || !target.mind) - if(!silent) - to_chat(user, "[t_He] appear[target.p_s()] to be catatonic! Not even magic can affect [target.p_their()] vacant mind.") - return - - if(user.suiciding) - if(!silent) - to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") - return - - var/datum/mind/TM = target.mind - if(target.anti_magic_check(TRUE, FALSE) || TM.has_antag_datum(/datum/antagonist/wizard) || TM.has_antag_datum(/datum/antagonist/cult) || TM.has_antag_datum(/datum/antagonist/clockcult) || TM.has_antag_datum(/datum/antagonist/changeling) || TM.has_antag_datum(/datum/antagonist/rev) || target.key[1] == "@") - if(!silent) - to_chat(user, "[target.p_their(TRUE)] mind is resisting your spell!") - return - - var/mob/living/victim = target//The target of the spell whos body will be transferred to. - var/mob/living/caster = user//The wizard/whomever doing the body transferring. - - //MIND TRANSFER BEGIN - var/mob/dead/observer/ghost = victim.ghostize(FALSE, TRUE) - caster.mind.transfer_to(victim) - - ghost.mind.transfer_to(caster) - if(ghost.key) - ghost.transfer_ckey(caster) //have to transfer the key since the mind was not active - qdel(ghost) - - //MIND TRANSFER END - - //Here we knock both mobs out for a time. - caster.Unconscious(unconscious_amount_caster) - victim.Unconscious(unconscious_amount_victim) - SEND_SOUND(caster, sound('sound/magic/mandswap.ogg')) - SEND_SOUND(victim, sound('sound/magic/mandswap.ogg'))// only the caster and victim hear the sounds, that way no one knows for sure if the swap happened - return TRUE diff --git a/code/modules/spells/spell_types/barnyard.dm b/code/modules/spells/spell_types/pointed/barnyard.dm similarity index 52% rename from code/modules/spells/spell_types/barnyard.dm rename to code/modules/spells/spell_types/pointed/barnyard.dm index 4b972e8030..61e3cf7127 100644 --- a/code/modules/spells/spell_types/barnyard.dm +++ b/code/modules/spells/spell_types/pointed/barnyard.dm @@ -1,51 +1,54 @@ -/obj/effect/proc_holder/spell/targeted/barnyardcurse +/obj/effect/proc_holder/spell/pointed/barnyardcurse name = "Curse of the Barnyard" desc = "This spell dooms an unlucky soul to possess the speech and facial attributes of a barnyard animal." school = "transmutation" charge_type = "recharge" charge_max = 150 charge_counter = 0 - clothes_req = NONE - stat_allowed = 0 + clothes_req = FALSE + stat_allowed = FALSE invocation = "KN'A FTAGHU, PUCK 'BTHNK!" invocation_type = "shout" range = 7 cooldown_min = 30 - selection_type = "range" - var/list/compatible_mobs = list(/mob/living/carbon/human, /mob/living/carbon/monkey) - + ranged_mousepointer = 'icons/effects/mouse_pointers/barn_target.dmi' action_icon_state = "barn" + active_msg = "You prepare to curse a target..." + deactive_msg = "You dispel the curse..." + /// List of mobs which are allowed to be a target of the spell + var/static/list/compatible_mobs_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/monkey)) -/obj/effect/proc_holder/spell/targeted/barnyardcurse/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/pointed/barnyardcurse/cast(list/targets, mob/user) if(!targets.len) - to_chat(user, "No target found in range.") - return + to_chat(user, "No target found in range!") + return FALSE + if(!can_target(targets[1], user)) + return FALSE var/mob/living/carbon/target = targets[1] - - if(!(target.type in compatible_mobs)) - to_chat(user, "You are unable to curse [target]'s head!") - return - - if(!(target in oview(range))) - to_chat(user, "[target.p_theyre(TRUE)] too far away!") - return - if(target.anti_magic_check()) to_chat(user, "The spell had no effect!") target.visible_message("[target]'s face bursts into flames, which instantly burst outward, leaving [target] unharmed!", \ - "Your face starts burning up, but the flames are repulsed by your anti-magic protection!") - return + "Your face starts burning up, but the flames are repulsed by your anti-magic protection!") + return FALSE var/list/masks = list(/obj/item/clothing/mask/pig/cursed, /obj/item/clothing/mask/cowmask/cursed, /obj/item/clothing/mask/horsehead/cursed) - var/choice = pick(masks) var/obj/item/clothing/mask/magichead = new choice(get_turf(target)) - magichead.flags_inv = null + target.visible_message("[target]'s face bursts into flames, and a barnyard animal's head takes its place!", \ "Your face burns up, and shortly after the fire you realise you have the face of a barnyard animal!") if(!target.dropItemToGround(target.wear_mask)) qdel(target.wear_mask) - target.equip_to_slot_if_possible(magichead, SLOT_WEAR_MASK, 1, 1) - + target.equip_to_slot_if_possible(magichead, ITEM_SLOT_MASK, 1, 1) target.flash_act() + +/obj/effect/proc_holder/spell/pointed/barnyardcurse/can_target(atom/target, mob/user, silent) + . = ..() + if(!.) + return FALSE + if(!is_type_in_typecache(target, compatible_mobs_typecache)) + if(!silent) + to_chat(user, "You are unable to curse [target]!") + return FALSE + return TRUE diff --git a/code/modules/spells/spell_types/pointed/blind.dm b/code/modules/spells/spell_types/pointed/blind.dm new file mode 100644 index 0000000000..a773c5ad8d --- /dev/null +++ b/code/modules/spells/spell_types/pointed/blind.dm @@ -0,0 +1,35 @@ +/obj/effect/proc_holder/spell/pointed/trigger/blind + name = "Blind" + desc = "This spell temporarily blinds a single target." + school = "transmutation" + charge_max = 300 + clothes_req = FALSE + invocation = "STI KALY" + invocation_type = "whisper" + message = "Your eyes cry out in pain!" + cooldown_min = 50 //12 deciseconds reduction per rank + starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind", "/obj/effect/proc_holder/spell/targeted/genetic/blind") + ranged_mousepointer = 'icons/effects/mouse_pointers/blind_target.dmi' + action_icon_state = "blind" + active_msg = "You prepare to blind a target..." + +/obj/effect/proc_holder/spell/targeted/inflict_handler/blind + amt_eye_blind = 10 + amt_eye_blurry = 20 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/blind + mutations = list(BLINDMUT) + duration = 300 + charge_max = 400 // needs to be higher than the duration or it'll be permanent + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/pointed/trigger/blind/can_target(atom/target, mob/user, silent) + . = ..() + if(!.) + return FALSE + if(!isliving(target)) + if(!silent) + to_chat(user, "You can only blind living beings!") + return FALSE + return TRUE diff --git a/code/modules/spells/spell_types/pointed/mind_transfer.dm b/code/modules/spells/spell_types/pointed/mind_transfer.dm new file mode 100644 index 0000000000..28d646f6b6 --- /dev/null +++ b/code/modules/spells/spell_types/pointed/mind_transfer.dm @@ -0,0 +1,103 @@ +/obj/effect/proc_holder/spell/pointed/mind_transfer + name = "Mind Transfer" + desc = "This spell allows the user to switch bodies with a target next to him." + school = "transmutation" + charge_max = 600 + clothes_req = FALSE + invocation = "GIN'YU CAPAN" + invocation_type = "whisper" + range = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + ranged_mousepointer = 'icons/effects/mouse_pointers/mindswap_target.dmi' + action_icon_state = "mindswap" + active_msg = "You prepare to swap minds with a target..." + /// For how long is the caster stunned for after the spell + var/unconscious_amount_caster = 40 SECONDS + /// For how long is the victim stunned for after the spell + var/unconscious_amount_victim = 40 SECONDS + +/obj/effect/proc_holder/spell/pointed/mind_transfer/cast(list/targets, mob/living/user, silent = FALSE) + if(!targets.len) + if(!silent) + to_chat(user, "No mind found!") + return FALSE + if(targets.len > 1) + if(!silent) + to_chat(user, "Too many minds! You're not a hive damnit!") + return FALSE + if(!can_target(targets[1], user, silent)) + return FALSE + + var/mob/living/victim = targets[1] //The target of the spell whos body will be transferred to. + var/datum/mind/VM = victim.mind + if(victim.anti_magic_check(TRUE, FALSE) || VM.has_antag_datum(/datum/antagonist/wizard) || VM.has_antag_datum(/datum/antagonist/cult) || VM.has_antag_datum(/datum/antagonist/changeling) || VM.has_antag_datum(/datum/antagonist/rev) || victim.key[1] == "@") + if(!silent) + to_chat(user, "[victim.p_their(TRUE)] mind is resisting your spell!") + return FALSE + if(istype(victim, /mob/living/simple_animal/hostile/guardian)) + var/mob/living/simple_animal/hostile/guardian/stand = victim + if(stand.summoner) + victim = stand.summoner + + //You should not be able to enter one of the most powerful side-antags as a fucking wizard. + if(istype(victim,/mob/living/simple_animal/slaughter)) + to_chat(user, "The devilish contract doesn't include the 'mind swappable' package, please try again another lifetime.") + return + + //MIND TRANSFER BEGIN + var/mob/dead/observer/ghost = victim.ghostize() + user.mind.transfer_to(victim) + + ghost.mind.transfer_to(user) + if(ghost.key) + user.key = ghost.key //have to transfer the key since the mind was not active + qdel(ghost) + //MIND TRANSFER END + + //Here we knock both mobs out for a time. + user.Unconscious(unconscious_amount_caster) + victim.Unconscious(unconscious_amount_victim) + SEND_SOUND(user, sound('sound/magic/mandswap.ogg')) + SEND_SOUND(victim, sound('sound/magic/mandswap.ogg')) // only the caster and victim hear the sounds, that way no one knows for sure if the swap happened + return TRUE + +/obj/effect/proc_holder/spell/pointed/mind_transfer/can_target(atom/target, mob/user, silent) + . = ..() + if(!.) + return FALSE + if(!isliving(target)) + if(!silent) + to_chat(user, "You can only swap minds with living beings!") + return FALSE + if(user == target) + if(!silent) + to_chat(user, "You can't swap minds with yourself!") + return FALSE + + var/mob/living/victim = target + var/t_He = victim.p_they(TRUE) + + if(ismegafauna(victim)) + if(!silent) + to_chat(user, "This creature is too powerful to control!") + return FALSE + if(victim.stat == DEAD) + if(!silent) + to_chat(user, "You don't particularly want to be dead!") + return FALSE + if(!victim.key || !victim.mind) + if(!silent) + to_chat(user, "[t_He] appear[victim.p_s()] to be catatonic! Not even magic can affect [victim.p_their()] vacant mind.") + return FALSE + if(user.suiciding) + if(!silent) + to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") + return FALSE + if(istype(victim, /mob/living/simple_animal/hostile/guardian)) + var/mob/living/simple_animal/hostile/guardian/stand = victim + if(stand.summoner) + if(stand.summoner == user) + if(!silent) + to_chat(user, "Swapping minds with your own guardian would just put you back into your own head!") + return FALSE + return TRUE diff --git a/code/modules/spells/spell_types/pointed/pointed.dm b/code/modules/spells/spell_types/pointed/pointed.dm new file mode 100644 index 0000000000..7b942dee27 --- /dev/null +++ b/code/modules/spells/spell_types/pointed/pointed.dm @@ -0,0 +1,105 @@ +/obj/effect/proc_holder/spell/pointed + name = "pointed spell" + ranged_mousepointer = 'icons/effects/mouse_pointers/throw_target.dmi' + action_icon_state = "projectile" + /// Message showing to the spell owner upon deactivating pointed spell. + var/deactive_msg = "You dispel the magic..." + /// Message showing to the spell owner upon activating pointed spell. + var/active_msg = "You prepare to use the spell on a target..." + /// Variable dictating if the user is allowed to cast a spell on himself. + var/self_castable = FALSE + /// Variable dictating if the spell will use turf based aim assist + var/aim_assist = TRUE + +/obj/effect/proc_holder/spell/pointed/Trigger(mob/user, skip_can_cast = TRUE) + if(!istype(user)) + return + var/msg + if(!can_cast(user)) + msg = "You can no longer cast [name]!" + remove_ranged_ability(msg) + return + if(active) + msg = "[deactive_msg]" + remove_ranged_ability(msg) + else + msg = "[active_msg] Left-click to activate spell on a target!" + add_ranged_ability(user, msg, TRUE) + on_activation(user) + +/obj/effect/proc_holder/spell/pointed/on_lose(mob/living/user) + remove_ranged_ability() + +/obj/effect/proc_holder/spell/pointed/remove_ranged_ability(msg) + . = ..() + on_deactivation(ranged_ability_user) + +/obj/effect/proc_holder/spell/pointed/add_ranged_ability(mob/living/user, msg, forced) + . = ..() + on_activation(user) + +/** + * on_activation: What happens upon pointed spell activation. + * + * Arguments: + * * user The mob interacting owning the spell. + */ +/obj/effect/proc_holder/spell/pointed/proc/on_activation(mob/user) + return + +/** + * on_activation: What happens upon pointed spell deactivation. + * + * Arguments: + * * user The mob interacting owning the spell. + */ +/obj/effect/proc_holder/spell/pointed/proc/on_deactivation(mob/user) + return + +/obj/effect/proc_holder/spell/pointed/update_icon() + if(!action) + return + if(active) + action.button_icon_state = "[action_icon_state]1" + else + action.button_icon_state = "[action_icon_state]" + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/pointed/InterceptClickOn(mob/living/caller, params, atom/target) + if(..()) + return TRUE + if(aim_assist && isturf(target)) + var/list/possible_targets = list() + for(var/A in target) + if(intercept_check(caller, A, TRUE)) + possible_targets += A + if(possible_targets.len == 1) + target = possible_targets[1] + if(!intercept_check(caller, target)) + return TRUE + if(!cast_check(FALSE, caller)) + return TRUE + perform(list(target), user = caller) + remove_ranged_ability() + return TRUE // Do not do any underlying actions after the spell cast + +/** + * intercept_check: Specific spell checks for InterceptClickOn() targets. + * + * Arguments: + * * user The mob using the ranged spell via intercept. + * * target The atom that is being targeted by the spell via intercept. + * * silent If the checks should produce not any feedback messages for the user. + */ +/obj/effect/proc_holder/spell/pointed/proc/intercept_check(mob/user, atom/target, silent = FALSE) + if(!self_castable && target == user) + if(!silent) + to_chat(user, "You cannot cast the spell on yourself!") + return FALSE + if(!(target in view_or_range(range, user, selection_type))) + if(!silent) + to_chat(user, "[target.p_theyre(TRUE)] too far away!") + return FALSE + if(!can_target(target, user, silent)) + return FALSE + return TRUE diff --git a/code/modules/spells/spell_types/projectile.dm b/code/modules/spells/spell_types/projectile.dm index be305520a2..3a8f48cf7c 100644 --- a/code/modules/spells/spell_types/projectile.dm +++ b/code/modules/spells/spell_types/projectile.dm @@ -37,8 +37,8 @@ var/projectile_type = text2path(proj_type) projectile = new projectile_type(user) if(istype(proj_type, /obj/effect/proc_holder/spell)) - projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) - var/obj/effect/proc_holder/spell/targeted/trigger/T = projectile + projectile = new /obj/effect/proc_holder/spell/pointed/trigger(user) + var/obj/effect/proc_holder/spell/pointed/trigger/T = projectile T.linked_spells += proj_type projectile.icon = proj_icon projectile.icon_state = proj_icon_state diff --git a/code/modules/spells/spell_types/trigger.dm b/code/modules/spells/spell_types/trigger.dm index 39cff63d98..df579d9243 100644 --- a/code/modules/spells/spell_types/trigger.dm +++ b/code/modules/spells/spell_types/trigger.dm @@ -1,30 +1,26 @@ -/obj/effect/proc_holder/spell/targeted/trigger +/obj/effect/proc_holder/spell/pointed/trigger name = "Trigger" desc = "This spell triggers another spell or a few." - var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks -/obj/effect/proc_holder/spell/targeted/trigger/Initialize() +/obj/effect/proc_holder/spell/pointed/trigger/Initialize() . = ..() - for(var/spell in starting_spells) var/spell_to_add = text2path(spell) new spell_to_add(src) //should result in adding to contents, needs testing -/obj/effect/proc_holder/spell/targeted/trigger/Destroy() +/obj/effect/proc_holder/spell/pointed/trigger/Destroy() for(var/spell in contents) qdel(spell) linked_spells = null starting_spells = null return ..() -/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets,mob/user = usr) +/obj/effect/proc_holder/spell/pointed/trigger/cast(list/targets,mob/user = usr) playMagSound() for(var/mob/living/target in targets) for(var/obj/effect/proc_holder/spell/spell in contents) spell.perform(list(target),0) for(var/obj/effect/proc_holder/spell/spell in linked_spells) spell.perform(list(target),0) - - return \ No newline at end of file diff --git a/code/modules/spells/spell_types/wizard.dm b/code/modules/spells/spell_types/wizard.dm index 13c47ab9ac..14f359ef81 100644 --- a/code/modules/spells/spell_types/wizard.dm +++ b/code/modules/spells/spell_types/wizard.dm @@ -207,40 +207,12 @@ summon_type = list(/mob/living/simple_animal/hostile/netherworld) cast_sound = 'sound/magic/summonitems_generic.ogg' -/obj/effect/proc_holder/spell/targeted/trigger/blind - name = "Blind" - desc = "This spell temporarily blinds a single person and does not require wizard garb." - - school = "transmutation" - charge_max = 300 - clothes_req = NONE - invocation = "STI KALY" - invocation_type = "whisper" - message = "Your eyes cry out in pain!" - cooldown_min = 50 //12 deciseconds reduction per rank - - starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind","/obj/effect/proc_holder/spell/targeted/genetic/blind") - - action_icon_state = "blind" - /obj/effect/proc_holder/spell/aoe_turf/conjure/creature/cult name = "Summon Creatures (DANGEROUS)" clothes_req = SPELL_CULT_GARB charge_max = 5000 summon_amt = 2 - - -/obj/effect/proc_holder/spell/targeted/inflict_handler/blind - amt_eye_blind = 10 - amt_eye_blurry = 20 - sound = 'sound/magic/blind.ogg' - -/obj/effect/proc_holder/spell/targeted/genetic/blind - mutations = list(BLINDMUT) - duration = 300 - sound = 'sound/magic/blind.ogg' - /obj/effect/proc_holder/spell/aoe_turf/repulse name = "Repulse" desc = "This spell throws everything around the user away." diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index 4c3f30a786..472734b74b 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -180,16 +180,37 @@ reload() /obj/machinery/bsa/full/proc/fire(mob/user, turf/bullseye) - var/turf/point = get_front_turf() - for(var/turf/T in getline(get_step(point,dir),get_target_turf())) - T.ex_act(EXPLODE_DEVASTATE) - point.Beam(get_target_turf(),icon_state="bsa_beam",time=50,maxdistance = world.maxx) //ZZZAP - - message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike.") - explosion(bullseye,ex_power,ex_power*2,ex_power*4) - reload() + var/turf/point = get_front_turf() + var/turf/target = get_target_turf() + var/atom/movable/blocker + for(var/T in getline(get_step(point, dir), target)) + var/turf/tile = T + if(SEND_SIGNAL(tile, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) + blocker = tile + else + for(var/AM in tile) + var/atom/movable/stuff = AM + if(SEND_SIGNAL(stuff, COMSIG_ATOM_BSA_BEAM) & COMSIG_ATOM_BLOCKS_BSA_BEAM) + blocker = stuff + break + if(blocker) + target = tile + break + else + tile.ex_act(EXPLODE_DEVASTATE) + point.Beam(target, icon_state = "bsa_beam", time = 50, maxdistance = world.maxx) //ZZZAP + new /obj/effect/temp_visual/bsa_splash(point, dir) + + if(!blocker) + message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)].") + log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)].") + explosion(bullseye, ex_power, ex_power*2, ex_power*4) + else + message_admins("[ADMIN_LOOKUPFLW(user)] has launched an artillery strike targeting [ADMIN_VERBOSEJMP(bullseye)] but it was blocked by [blocker] at [ADMIN_VERBOSEJMP(target)].") + log_game("[key_name(user)] has launched an artillery strike targeting [AREACOORD(bullseye)] but it was blocked by [blocker] at [AREACOORD(target)].") + /obj/machinery/bsa/full/proc/reload() ready = FALSE use_power(power_used_per_shot) diff --git a/code/modules/uplink/uplink_items/uplink_clothing.dm b/code/modules/uplink/uplink_items/uplink_clothing.dm index 014e0452b5..c26a9ae1f0 100644 --- a/code/modules/uplink/uplink_items/uplink_clothing.dm +++ b/code/modules/uplink/uplink_items/uplink_clothing.dm @@ -97,3 +97,9 @@ item = /obj/item/clothing/gloves/tackler/combat/insulated include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) cost = 2 + +/datum/uplink_item/device_tools/syndicate_eyepatch + name = "Mechanical Eyepatch" + desc = "An eyepatch that connects itself to your eye socket, enhancing your shooting to an impossible degree, allowing your bullets to ricochet far more often than usual." + item = /obj/item/clothing/glasses/eyepatch/syndicate + cost = 8 diff --git a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm index f1c27c640b..28d02cf79b 100644 --- a/code/modules/uplink/uplink_items/uplink_stealthdevices.dm +++ b/code/modules/uplink/uplink_items/uplink_stealthdevices.dm @@ -112,13 +112,13 @@ name = "Radio Jammer" desc = "This device will disrupt any nearby outgoing radio communication when activated. Does not affect binary chat." item = /obj/item/jammer - cost = 5 + cost = 2 /datum/uplink_item/stealthy_tools/smugglersatchel name = "Smuggler's Satchel" desc = "This satchel is thin enough to be hidden in the gap between plating and tiling; great for stashing \ your stolen goods. Comes with a crowbar and a floor tile inside. Properly hidden satchels have been \ - known to survive intact even beyond the current shift. " + known to survive intact even beyond the current shift, but this is just a myth. " item = /obj/item/storage/backpack/satchel/flat - cost = 2 + cost = 1 surplus = 30 diff --git a/code/modules/vehicles/_vehicle.dm b/code/modules/vehicles/_vehicle.dm index ac7fa879f4..12e9f365d0 100644 --- a/code/modules/vehicles/_vehicle.dm +++ b/code/modules/vehicles/_vehicle.dm @@ -18,6 +18,7 @@ var/canmove = TRUE var/emulate_door_bumps = TRUE //when bumping a door try to make occupants bump them to open them. var/default_driver_move = TRUE //handle driver movement instead of letting something else do it like riding datums. + var/enclosed = FALSE // is the rider protected from bullets? assume no var/list/autogrant_actions_passenger //plain list of typepaths var/list/autogrant_actions_controller //assoc list "[bitflag]" = list(typepaths) var/list/mob/occupant_actions //assoc list mob = list(type = action datum assigned to mob) @@ -166,3 +167,9 @@ if(trailer && .) var/dir_to_move = get_dir(trailer.loc, newloc) step(trailer, dir_to_move) + +/obj/vehicle/bullet_act(obj/item/projectile/Proj) //wrapper + if (!enclosed && length(occupants) && !Proj.force_hit && (Proj.def_zone == BODY_ZONE_HEAD || Proj.def_zone == BODY_ZONE_CHEST)) //allows bullets to hit drivers + occupants[1].bullet_act(Proj) // driver dinkage + return BULLET_ACT_HIT + . = ..() diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm index edaab8b982..28f6b1cca8 100644 --- a/code/modules/vehicles/sealed.dm +++ b/code/modules/vehicles/sealed.dm @@ -1,4 +1,5 @@ /obj/vehicle/sealed + enclosed = TRUE // you're in a sealed vehicle dont get dinked idiot var/enter_delay = 20 flags_1 = BLOCK_FACE_ATOM_1 diff --git a/config/game_options.txt b/config/game_options.txt index 1585d45e6d..a5b0d0b8c4 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -116,6 +116,7 @@ CONTINUOUS CHANGELING CONTINUOUS WIZARD #CONTINUOUS MONKEY CONTINUOUS BLOODSUCKER +CONTINUOUS HERESY ##Note: do not toggle continuous off for these modes, as they have no antagonists and would thus end immediately! diff --git a/config/spaceRuinBlacklist.txt b/config/spaceRuinBlacklist.txt index 90682f5bad..969e4135f6 100644 --- a/config/spaceRuinBlacklist.txt +++ b/config/spaceRuinBlacklist.txt @@ -52,3 +52,4 @@ #_maps/RandomRuins/SpaceRuins/arcade.dmm #_maps/RandomRuins/SpaceRuins/spacehermit.dmm #_maps/RandomRuins/SpaceRuins/advancedlab.dmm +#_maps/RandomRuins/SpaceRuins/spacediner.dmm diff --git a/html/changelog.html b/html/changelog.html index 9831498c56..43bac219ab 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -50,6 +50,178 @@ -->
+

18 August 2020

+

DeltaFire15 updated:

+
    +
  • kindle cast time: 15ds -> 25ds
  • +
  • Moved the Belligerent Scripture to where it should be in the code
  • +
+

Detective-Google updated:

+
    +
  • glass floors
  • +
  • uncrowbarrable plasma floors tweak:disco inferno's plasma floors can no longer be crowbarred.
  • +
  • ghost cafe has funky fresh art
  • +
  • you can actually remove glass floors now
  • +
  • get_equipped_items is hopefully less gross
  • +
  • plasma cutters are no longer gay
  • +
+

Hatterhat updated:

+
    +
  • Slaughter demons (and laughter demons, being a subtype) are MOB_SIZE_LARGE, with one of the more immediate effects being able to mark them with a crusher and backstab them.
  • +
  • The funny blyat men have stumbled upon another surplus of Mosin-Nagants and are starting to pack them into crates again.
  • +
  • Vehicle riders can now, by default, get shot in the face and/or chest.
  • +
  • Adminspawn only .357 DumDum rounds! Because sometimes the other guy just really needs to hurt.
  • +
  • Bluespace beakers now have a chemical window through the side that shows chemical overlays.
  • +
  • Plant DNA manipulators now let you chuck things over them. Or they WOULD, if LETPASSTHROW worked half a damn.
  • +
+

LetterN updated:

+
    +
  • uplink implant states
  • +
  • tweaks how role assigning works
  • +
+

MrJWhit updated:

+
    +
  • Gives ashwalkers nightvision
  • +
  • Makes tesla blast people, not the environment, to save the server.
  • +
+

TheObserver-sys updated:

+
    +
  • moves Garlic sprites from growing.dmi to growing_vegetable.dmi
  • +
  • Removes the unused Electric Lime mutation, it just takes up space with no actual function nor sprites.
  • +
  • Gives Catnip growing sprites
  • +
  • Removes redundant images in growing.dmi
  • +
+

kiwedespars updated:

+
    +
  • 10 force to a fucking rubber cock.
  • +
+

lolman360 updated:

+
    +
  • shotgun stripper clip nerf. ammoboxes can now accept a load_delay that happens when they attack a magazine, internal or external.
  • +
+

ported from tg updated:

+
    +
  • bronze airlocks and windows can now be built
  • +
  • i also tweaked bronze flooring to be cheaper.
  • +
+

silicons updated:

+
    +
  • stamina draining projectiles without stamina for their primary damage type now has their stamina damage taken into account for shield blocking, rather than the block being done for free for that.
  • +
+

timothyteakettle updated:

+
    +
  • snowflake code tidyup
  • +
  • snowflake code for mutant bodypart selection has been rewritten to be ~14x shorter
  • +
  • meat type and horns can now be selected by any species
  • +
+ +

17 August 2020

+

DeltaFire15 updated:

+
    +
  • Cogscarabs are no longer always Pogscarabs
  • +
+

Strazyplus updated:

+
    +
  • Added drakeborgs
  • +
  • Added drakeplushies
  • +
  • added drakeborg sprites
  • +
  • added drakeplushie sprites
  • +
  • changed some code - added drakeplushies to backpack loadout Removed duplicate voresleeper belly sprites from engdrake & jantidrake. [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/)
  • +
  • Added CC BY-NC-SA 3.0 license details to icon/mob/cyborg moved drakeborg.dmi to icon/mob/cyborg
  • +
+ +

16 August 2020

+

kiwedespars updated:

+
    +
  • nerfed hypereut chaplain weapon.
  • +
  • 50% rng blockchance -> 0%
  • +
  • parry made much worse because it's an actual weapon and a roundstart one at that.
  • +
+

zeroisthebiggay updated:

+
    +
  • tips
  • +
+ +

15 August 2020

+

LetterN updated:

+
    +
  • missing anomaly core icons
  • +
  • wrong state. blame the tg vertion i copied
  • +
+

silicons updated:

+
    +
  • the 8 rotation limit from clockwork chairs has been removed. please don't abuse this.
  • +
  • ethereals can now wear underwear
  • +
+ +

14 August 2020

+

silicons updated:

+
    +
  • abductors can buy things
  • +
+ +

13 August 2020

+

LetterN updated:

+
    +
  • Removes fermisleepers and reverts them to tg ones
  • +
+ +

12 August 2020

+

DeltaFire15 updated:

+
    +
  • hellgun single-pack classification: goodies -> armory
  • +
+

Detective-Google updated:

+
    +
  • hallway table hallway table
  • +
+

Hatterhat updated:

+
    +
  • The temporal katana is now slightly more worthy of the 2 spell point cost, with a smaller, antimagic respecting timestop, less force, and no random blockchance. Society has progressed past the need for blockchance.
  • +
+

LetterN updated:

+
    +
  • Mafia Component
  • +
  • Fixed missing icons and handtele
  • +
+

Putnam3145 updated:

+
    +
  • a whole lot of jank regarding funny part sprite display.
  • +
+

Toriate updated:

+
    +
  • Opossums have migrated into the maintenance tunnels! Seek them out at your own peril!
  • +
+

ancientpower updated:

+
    +
  • Doors added to the west side of box medbay to make things a bit more manageable.
  • +
+

kappa-sama updated:

+
    +
  • smuggler satchel cost 2->1
  • +
  • radio jammer cost 5->2
  • +
  • smuggler satchel uplink description now implies that persistence is disabled
  • +
+

lolman360 updated:

+
    +
  • renameable necklace (accessory, attaches to suit) and ring (glove slot.)
  • +
  • custom rename is now 2048 characters? i think it's characters.
  • +
+

silicons updated:

+
    +
  • You can now use anything as an emoji by doing :/obj/item/path/to/item:. This works for any /atom or subtype.
  • +
+

timothyteakettle updated:

+
    +
  • syndicate agents now have access to mechanical aim enhancers which allow them to aim bullets to bounce off walls
  • +
  • ricochets work properly now for the bullets that support them
  • +
+

zeroisthebiggay updated:

+
    +
  • hair and some sechuds
  • +
  • ce hardsuit radproofing
  • +
+

11 August 2020

Hatterhat updated:

    @@ -1262,156 +1434,6 @@
  • medbots can now be tipped over
  • added more medbot sounds
- -

17 June 2020

-

SmArtKar updated:

-
    -
  • New ID icons
  • -
  • Sutures and Meshes
  • -
-

Trilbyspaceclone updated:

-
    -
  • Gin export takes gin now
  • -
- -

16 June 2020

-

Ghommie updated:

-
    -
  • You can't blink nor use LOOC/AOOC as a petrified statue anymore.
  • -
-

Trilbyspaceclone updated:

-
    -
  • BEPIS decal painter has been moved to venders, replacing it being the flashdark
  • -
-

kevinz000 updated:

-
    -
  • projectile ricochets now use a less hilariously terrible way of being handled and should be easier to w
  • -
  • projectile runtime/ricocheting
  • -
  • Lobotomy no longer has a 50% chance of giving you a nigh-unremovable trauma, but does 50 brain damage even on success. On failure, it will give you a lobotomy-class trauma.
  • -
  • spinesnapping from tackling now only gives a lobotomy class trauma instead of magic.
  • -
-

timothyteakettle updated:

-
    -
  • Food carts now function as intended, allowing the pouring and mixing of drinks.
  • -
  • slimes can now change their color using alter form
  • -
- -

15 June 2020

-

Anturk, kevinz000 updated:

-
    -
  • VV now properly allows access to datums in associative lists
  • -
  • SDQL2 printout has been upgraded for the 5th time.
  • -
-

kevinz000 updated:

-
    -
  • plushies now work. credits to timothytea.
  • -
  • You can now tape knives to cleanbots
  • -
-

kevinz000 (port from VOREStation) updated:

-
    -
  • Ported zoomba skins for cyborgs.
  • -
- -

14 June 2020

-

Bhijn updated:

-
    -
  • raw HTML can no longer be used in flavortext
  • -
  • Added Lesbian Visibility Day (April 26th)
  • -
  • Added Bisexual Visibility Day (September 23rd)
  • -
  • Added Coming Out Day! (October 11th)
  • -
  • Added Intersex Awareness Day (October 26th)
  • -
  • Added Transgender Awareness Week (November 13th - 19th)
  • -
  • Added the Transgender Day of Remembrance (November 20th)
  • -
  • Added Asexual Awareness Week (Last full week of October, this year it'll be Oct. 25-31)
  • -
  • Added the Stonewall Riot Anniversary (June 28)
  • -
  • Moth week is now *actually* the last full week of July, instead of the 4th of every weekday + the 5th Sunday.
  • -
-

Floof Ball / Kathrin Morrison / Floof Ball#0798 updated:

-
    -
  • Improvised Pistol + 32 ACP ammo.
  • -
  • Improvised Energy Gun. Fires 5 shots of 10 burn damage each. Can be upgraded with a lens made from glassworking and T4 parts for a minor buff.
  • -
  • Ammo + gun part loot spawners for mappers.
  • -
  • New sprites for Improvised Rifle, the ability to sling the rifle and a sprite for that.
  • -
  • New sprites for the Improvised Shotgun and its sling sprite.
  • -
  • Improvised shotguns are now two-handed only.
  • -
  • Improvised shotguns now only have a much less harsh 0.9* modifier, keeping them two hits to crit with slugs and buckshot. It can no longer be dual-wielded but can still be sawn off for w_class medium (can fit in backpacks).
  • -
  • Missing handsaw icons added in.
  • -
  • Crafting table cleaned up into sections.
  • -
-

Ghommie updated:

-
    -
  • changed the weak attack message prefix from "inefficiently" to "limply", "feebly" and "saplessly" and lowered the threshold.
  • -
  • Fixing old beserker hardsuits having the wrong helmet type.
  • -
-

The0bserver and Stewydeadmike updated:

-
    -
  • Hey, there's a bit of dust in this recipe book! Recipes using peas? How many recipes does this book even have?
  • -
  • Adds 6 new food items, and 1 new consumable reagent using all forms of the recently discovered peas. Ask your local botanist and chef for them today!
  • -
-

YakumoChen updated:

-
    -
  • Adds polychrome options to loadout
  • -
  • Adds risque polychrome options to Kinkmate vendors
  • -
-

kevinz000 updated:

-
    -
  • emissive blockers can no longer be radioactive.
  • -
  • Ghosts can now scan air inside most objects that contain air by clicking on them.
  • -
  • Directional blocking has been added, keybound to G. This will reduce a portion of incoming damage if done with eligible items at the cost of stamina damage incurred to the user based on damage blocked as well as while active, as well as in most cases preventing the user from doing any attacks while this is active.
  • -
  • Parrying has been added, keybound to F. Timing-based counterattacks, effect heavily dependent on the item, WIP.
  • -
  • Disks are now smaller.
  • -
- -

12 June 2020

-

EmeraldSundisk updated:

-
    -
  • The Detective's Office has been commandeered in order to serve a Head of Security. Detectives will find their new office within starboard maintenance.
  • -
  • Adds the Head of Security's standard equipment to their new office.
  • -
  • Slight adjustment/expansion to the main security department
  • -
  • Additional maintenance work
  • -
-

timothyteakettle updated:

-
    -
  • Crabs, cockroaches, slimes and crabs are now small enough to fit in pet carriers
  • -
- -

11 June 2020

-

Ghommie updated:

-
    -
  • Balanced vending machine prices to be generally more affordable, a minority has been priced up though.
  • -
- -

10 June 2020

-

DeltaFire15 updated:

-
    -
  • Golems / simillar now inherit the neutered antag datum if the creator is neutered.
  • -
-

Ghommie updated:

-
    -
  • Fixing missing pill type buttons from the chem master UI.
  • -
-

Naksu updated:

-
    -
  • Lighting corner updates are ever so slightly faster.
  • -
-

Putnam for helping me code the contamination clearing on people updated:

-
    -
  • Lab made Zeolites have been remade anew and more affective now that they refined the best possable way to mix and make a supper Zeolite capable of clearing contamination form not only people but items!
  • -
-

Trilbyspaceclone updated:

-
    -
  • Tank Dispender has been moved into toxin storage from toxins
  • -
-

kevinz000 updated:

-
    -
  • traitor classes can now be poplocked. hijack/glorious death are now locked to 25/20 respectively.
  • -
-

timothyteakettle updated:

-
    -
  • adds a new fermichem, used for creating sentient plushies!
  • -
  • Mice can now breed using cheese wedges
  • -
  • Royal cheese can be crafted to convert a mouse into king rat
  • -
GoonStation 13 Development Team diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 3b2ae6d9b7..f8749c8dc5 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -26872,3 +26872,129 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. - tweak: tracer no longer gives you full stamheals per use zeroisthebiggay: - rscadd: volaju two +2020-08-12: + DeltaFire15: + - balance: 'hellgun single-pack classification: goodies -> armory' + Detective-Google: + - bugfix: hallway table hallway table + Hatterhat: + - balance: The temporal katana is now slightly more worthy of the 2 spell point + cost, with a smaller, antimagic respecting timestop, less force, and no random + blockchance. Society has progressed past the need for blockchance. + LetterN: + - rscadd: Mafia Component + - bugfix: Fixed missing icons and handtele + Putnam3145: + - bugfix: a whole lot of jank regarding funny part sprite display. + Toriate: + - rscadd: Opossums have migrated into the maintenance tunnels! Seek them out at + your own peril! + ancientpower: + - tweak: Doors added to the west side of box medbay to make things a bit more manageable. + kappa-sama: + - tweak: smuggler satchel cost 2->1 + - tweak: radio jammer cost 5->2 + - bugfix: smuggler satchel uplink description now implies that persistence is disabled + lolman360: + - rscadd: renameable necklace (accessory, attaches to suit) and ring (glove slot.) + - tweak: custom rename is now 2048 characters? i think it's characters. + silicons: + - rscadd: You can now use anything as an emoji by doing :/obj/item/path/to/item:. + This works for any /atom or subtype. + timothyteakettle: + - rscadd: syndicate agents now have access to mechanical aim enhancers which allow + them to aim bullets to bounce off walls + - bugfix: ricochets work properly now for the bullets that support them + zeroisthebiggay: + - imageadd: hair and some sechuds + - balance: ce hardsuit radproofing +2020-08-13: + LetterN: + - rscdel: Removes fermisleepers and reverts them to tg ones +2020-08-14: + silicons: + - bugfix: abductors can buy things +2020-08-15: + LetterN: + - bugfix: missing anomaly core icons + - bugfix: wrong state. blame the tg vertion i copied + silicons: + - tweak: the 8 rotation limit from clockwork chairs has been removed. please don't + abuse this. + - tweak: ethereals can now wear underwear +2020-08-16: + kiwedespars: + - balance: nerfed hypereut chaplain weapon. + - balance: 50% rng blockchance -> 0% + - balance: parry made much worse because it's an actual weapon and a roundstart + one at that. + zeroisthebiggay: + - rscadd: tips + - rscdel: tips +2020-08-17: + DeltaFire15: + - bugfix: Cogscarabs are no longer always Pogscarabs + Strazyplus: + - rscadd: Added drakeborgs + - rscadd: Added drakeplushies + - imageadd: added drakeborg sprites + - imageadd: added drakeplushie sprites + - code_imp: changed some code - added drakeplushies to backpack loadout Removed + duplicate voresleeper belly sprites from engdrake & jantidrake. [CC BY-NC-SA + 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/) + - rscadd: Added CC BY-NC-SA 3.0 license details to icon/mob/cyborg moved drakeborg.dmi + to icon/mob/cyborg +2020-08-18: + DeltaFire15: + - balance: 'kindle cast time: 15ds -> 25ds' + - tweak: Moved the Belligerent Scripture to where it should be in the code + Detective-Google: + - rscadd: glass floors + - rscadd: uncrowbarrable plasma floors tweak:disco inferno's plasma floors can no + longer be crowbarred. + - rscadd: ghost cafe has funky fresh art + - bugfix: you can actually remove glass floors now + - code_imp: get_equipped_items is hopefully less gross + - balance: plasma cutters are no longer gay + Hatterhat: + - balance: Slaughter demons (and laughter demons, being a subtype) are MOB_SIZE_LARGE, + with one of the more immediate effects being able to mark them with a crusher + and backstab them. + - balance: The funny blyat men have stumbled upon another surplus of Mosin-Nagants + and are starting to pack them into crates again. + - balance: Vehicle riders can now, by default, get shot in the face and/or chest. + - rscadd: Adminspawn only .357 DumDum rounds! Because sometimes the other guy just + really needs to hurt. + - rscadd: Bluespace beakers now have a chemical window through the side that shows + chemical overlays. + - rscadd: Plant DNA manipulators now let you chuck things over them. Or they WOULD, + if LETPASSTHROW worked half a damn. + LetterN: + - bugfix: uplink implant states + - tweak: tweaks how role assigning works + MrJWhit: + - rscadd: Gives ashwalkers nightvision + - balance: Makes tesla blast people, not the environment, to save the server. + TheObserver-sys: + - bugfix: moves Garlic sprites from growing.dmi to growing_vegetable.dmi + - rscdel: Removes the unused Electric Lime mutation, it just takes up space with + no actual function nor sprites. + - imageadd: Gives Catnip growing sprites + - imagedel: Removes redundant images in growing.dmi + kiwedespars: + - rscadd: 10 force to a fucking rubber cock. + lolman360: + - balance: shotgun stripper clip nerf. ammoboxes can now accept a load_delay that + happens when they attack a magazine, internal or external. + ported from tg: + - rscadd: bronze airlocks and windows can now be built + - balance: i also tweaked bronze flooring to be cheaper. + silicons: + - tweak: stamina draining projectiles without stamina for their primary damage type + now has their stamina damage taken into account for shield blocking, rather + than the block being done for free for that. + timothyteakettle: + - refactor: snowflake code tidyup + - refactor: snowflake code for mutant bodypart selection has been rewritten to be + ~14x shorter + - tweak: meat type and horns can now be selected by any species diff --git a/html/changelogs/AutoChangeLog-pr-13111.yml b/html/changelogs/AutoChangeLog-pr-13111.yml deleted file mode 100644 index 54a937713b..0000000000 --- a/html/changelogs/AutoChangeLog-pr-13111.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "zeroisthebiggay" -delete-after: True -changes: - - imageadd: "hair and some sechuds" diff --git a/icons/effects/160x160.dmi b/icons/effects/160x160.dmi index 0a97573a9b..2adedb6c03 100644 Binary files a/icons/effects/160x160.dmi and b/icons/effects/160x160.dmi differ diff --git a/icons/effects/96x96.dmi b/icons/effects/96x96.dmi index b60ff97b2b..34f4adf6ce 100644 Binary files a/icons/effects/96x96.dmi and b/icons/effects/96x96.dmi differ diff --git a/icons/effects/beam.dmi b/icons/effects/beam.dmi index 0c4784553c..e5bff44ac4 100644 Binary files a/icons/effects/beam.dmi and b/icons/effects/beam.dmi differ diff --git a/icons/effects/beam_splash.dmi b/icons/effects/beam_splash.dmi new file mode 100644 index 0000000000..d7deb3e927 Binary files /dev/null and b/icons/effects/beam_splash.dmi differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index a2fce4678f..52164e1d33 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/eldritch.dmi b/icons/effects/eldritch.dmi new file mode 100644 index 0000000000..82549dccf0 Binary files /dev/null and b/icons/effects/eldritch.dmi differ diff --git a/icons/effects/mouse_pointers/barn_target.dmi b/icons/effects/mouse_pointers/barn_target.dmi new file mode 100644 index 0000000000..475a2b88b1 Binary files /dev/null and b/icons/effects/mouse_pointers/barn_target.dmi differ diff --git a/icons/effects/mouse_pointers/blind_target.dmi b/icons/effects/mouse_pointers/blind_target.dmi new file mode 100644 index 0000000000..1d33fc7a4c Binary files /dev/null and b/icons/effects/mouse_pointers/blind_target.dmi differ diff --git a/icons/effects/mouse_pointers/cult_target.dmi b/icons/effects/mouse_pointers/cult_target.dmi new file mode 100644 index 0000000000..650feb3361 Binary files /dev/null and b/icons/effects/mouse_pointers/cult_target.dmi differ diff --git a/icons/effects/mouse_pointers/mecha_mouse-disable.dmi b/icons/effects/mouse_pointers/mecha_mouse-disable.dmi new file mode 100644 index 0000000000..48924c58c2 Binary files /dev/null and b/icons/effects/mouse_pointers/mecha_mouse-disable.dmi differ diff --git a/icons/effects/mouse_pointers/mecha_mouse.dmi b/icons/effects/mouse_pointers/mecha_mouse.dmi new file mode 100644 index 0000000000..4b46a44684 Binary files /dev/null and b/icons/effects/mouse_pointers/mecha_mouse.dmi differ diff --git a/icons/effects/mouse_pointers/mindswap_target.dmi b/icons/effects/mouse_pointers/mindswap_target.dmi new file mode 100644 index 0000000000..32ccda154d Binary files /dev/null and b/icons/effects/mouse_pointers/mindswap_target.dmi differ diff --git a/icons/effects/mouse_pointers/overload_machine_target.dmi b/icons/effects/mouse_pointers/overload_machine_target.dmi new file mode 100644 index 0000000000..8bc67cdab6 Binary files /dev/null and b/icons/effects/mouse_pointers/overload_machine_target.dmi differ diff --git a/icons/effects/mouse_pointers/override_machine_target.dmi b/icons/effects/mouse_pointers/override_machine_target.dmi new file mode 100644 index 0000000000..77dbb4ba32 Binary files /dev/null and b/icons/effects/mouse_pointers/override_machine_target.dmi differ diff --git a/icons/effects/mouse_pointers/throw_target.dmi b/icons/effects/mouse_pointers/throw_target.dmi new file mode 100644 index 0000000000..660eafbf2b Binary files /dev/null and b/icons/effects/mouse_pointers/throw_target.dmi differ diff --git a/icons/effects/mouse_pointers/wrap_target.dmi b/icons/effects/mouse_pointers/wrap_target.dmi new file mode 100644 index 0000000000..2e9a338c9e Binary files /dev/null and b/icons/effects/mouse_pointers/wrap_target.dmi differ diff --git a/icons/mob/actions/actions_ecult.dmi b/icons/mob/actions/actions_ecult.dmi new file mode 100644 index 0000000000..0a130f006e Binary files /dev/null and b/icons/mob/actions/actions_ecult.dmi differ diff --git a/icons/mob/actions/backgrounds.dmi b/icons/mob/actions/backgrounds.dmi index 07839588ce..6b983df95a 100644 Binary files a/icons/mob/actions/backgrounds.dmi and b/icons/mob/actions/backgrounds.dmi differ diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index 13f97d3761..50bf65b27f 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/clothing/eyes.dmi b/icons/mob/clothing/eyes.dmi index cd2b84a143..876159c258 100644 Binary files a/icons/mob/clothing/eyes.dmi and b/icons/mob/clothing/eyes.dmi differ diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi index 0ad9452994..16571a4aa1 100644 Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ diff --git a/icons/mob/clothing/neck.dmi b/icons/mob/clothing/neck.dmi index 86dec62a41..7bfecb4158 100644 Binary files a/icons/mob/clothing/neck.dmi and b/icons/mob/clothing/neck.dmi differ diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi index c94e8a46d3..08d276a484 100644 Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ diff --git a/icons/mob/clothing/underwear.dmi b/icons/mob/clothing/underwear.dmi index f0c2cde93d..8cf1144a68 100644 Binary files a/icons/mob/clothing/underwear.dmi and b/icons/mob/clothing/underwear.dmi differ diff --git a/icons/mob/cyborg/Drakeborg-licensing.txt b/icons/mob/cyborg/Drakeborg-licensing.txt new file mode 100644 index 0000000000..f2d3ca925c --- /dev/null +++ b/icons/mob/cyborg/Drakeborg-licensing.txt @@ -0,0 +1,69 @@ +Drakeborg & drakeplushies are created by deviantart.com/mizartz + +https://creativecommons.org/licenses/by-nc-sa/3.0/ +Attribution-NonCommercial-ShareAlike 3.0 Unported + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. +License +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + +"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. +"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(g) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. +"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. +"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, Noncommercial, ShareAlike. +"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. +"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. +"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. +"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. +"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. +"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + +to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; +to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; +to Distribute and Publicly Perform the Work including as incorporated in Collections; and, +to Distribute and Publicly Perform Adaptations. +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights described in Section 4(e). + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + +You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(d), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(d), as requested. +You may Distribute or Publicly Perform an Adaptation only under: (i) the terms of this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-NonCommercial-ShareAlike 3.0 US) ("Applicable License"). You must include a copy of, or the URI, for Applicable License with every copy of each Adaptation You Distribute or Publicly Perform. You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License. You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. +You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in con-nection with the exchange of copyrighted works. +If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(d) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. +For the avoidance of doubt: + +Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; +Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and, +Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c). +Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING AND TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO THIS EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + +This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. +Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. +8. Miscellaneous + +Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. +Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. +If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. +No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. +This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. +The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. +Creative Commons Notice +Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License. + +Creative Commons may be contacted at https://creativecommons.org/. \ No newline at end of file diff --git a/icons/mob/cyborg/drakemech.dmi b/icons/mob/cyborg/drakemech.dmi new file mode 100644 index 0000000000..6a4845d983 Binary files /dev/null and b/icons/mob/cyborg/drakemech.dmi differ diff --git a/icons/mob/eldritch_mobs.dmi b/icons/mob/eldritch_mobs.dmi new file mode 100644 index 0000000000..8a16d53f88 Binary files /dev/null and b/icons/mob/eldritch_mobs.dmi differ diff --git a/icons/mob/hud.dmi b/icons/mob/hud.dmi index c6dc7130c7..1d2007fd63 100644 Binary files a/icons/mob/hud.dmi and b/icons/mob/hud.dmi differ diff --git a/icons/mob/inhands/64x64_lefthand.dmi b/icons/mob/inhands/64x64_lefthand.dmi index 6b47171066..6dc8d82753 100644 Binary files a/icons/mob/inhands/64x64_lefthand.dmi and b/icons/mob/inhands/64x64_lefthand.dmi differ diff --git a/icons/mob/inhands/64x64_righthand.dmi b/icons/mob/inhands/64x64_righthand.dmi index 3750e28906..ca87f74a6f 100644 Binary files a/icons/mob/inhands/64x64_righthand.dmi and b/icons/mob/inhands/64x64_righthand.dmi differ diff --git a/icons/mob/mob.dmi b/icons/mob/mob.dmi index de09fb1c63..23f0e2cc13 100644 Binary files a/icons/mob/mob.dmi and b/icons/mob/mob.dmi differ diff --git a/icons/obj/assemblies/new_assemblies.dmi b/icons/obj/assemblies/new_assemblies.dmi index 2283b84c43..32eda1eb49 100644 Binary files a/icons/obj/assemblies/new_assemblies.dmi and b/icons/obj/assemblies/new_assemblies.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index aea15e2e39..cd12f4a457 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/clothing/accessories.dmi b/icons/obj/clothing/accessories.dmi index c62a88c829..7d13e3f802 100644 Binary files a/icons/obj/clothing/accessories.dmi and b/icons/obj/clothing/accessories.dmi differ diff --git a/icons/obj/clothing/glasses.dmi b/icons/obj/clothing/glasses.dmi index e8ba88a12f..4fce479a9d 100644 Binary files a/icons/obj/clothing/glasses.dmi and b/icons/obj/clothing/glasses.dmi differ diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi index 60304999fa..8fbb2abe1e 100644 Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi index e8a360bc87..3476b16258 100644 Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi index c280ee786f..7dac61a663 100644 Binary files a/icons/obj/decals.dmi and b/icons/obj/decals.dmi differ diff --git a/icons/obj/eldritch.dmi b/icons/obj/eldritch.dmi new file mode 100644 index 0000000000..50c2913708 Binary files /dev/null and b/icons/obj/eldritch.dmi differ diff --git a/icons/obj/food/piecake.dmi b/icons/obj/food/piecake.dmi index 5638235217..935f7e8ad5 100644 Binary files a/icons/obj/food/piecake.dmi and b/icons/obj/food/piecake.dmi differ diff --git a/icons/obj/hydroponics/growing.dmi b/icons/obj/hydroponics/growing.dmi index 5415f47528..0866791ed6 100644 Binary files a/icons/obj/hydroponics/growing.dmi and b/icons/obj/hydroponics/growing.dmi differ diff --git a/icons/obj/hydroponics/growing_vegetables.dmi b/icons/obj/hydroponics/growing_vegetables.dmi index e174b4fb0b..ce0beb86ce 100644 Binary files a/icons/obj/hydroponics/growing_vegetables.dmi and b/icons/obj/hydroponics/growing_vegetables.dmi differ diff --git a/icons/obj/license.txt b/icons/obj/license.txt new file mode 100644 index 0000000000..2e27ba80f2 --- /dev/null +++ b/icons/obj/license.txt @@ -0,0 +1,3 @@ +icons/obj/plushies.dmi's icon state of secdrake and meddrake by Mizartz. It has been licensed under the CC BY-NC-SA 3.0 license. + +CC BY-NC-SA 3.0 https://creativecommons.org/licenses/by-nc-sa/3.0/ \ No newline at end of file diff --git a/icons/obj/machines/research.dmi b/icons/obj/machines/research.dmi index 7d64c494fd..7dcd4e6bcb 100644 Binary files a/icons/obj/machines/research.dmi and b/icons/obj/machines/research.dmi differ diff --git a/icons/obj/mafia.dmi b/icons/obj/mafia.dmi new file mode 100644 index 0000000000..c44b80aba1 Binary files /dev/null and b/icons/obj/mafia.dmi differ diff --git a/icons/obj/plushes.dmi b/icons/obj/plushes.dmi index 11d02a46cc..ac0c338016 100644 Binary files a/icons/obj/plushes.dmi and b/icons/obj/plushes.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 92e76f78bb..94568b8633 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/icons/obj/reagentfillings.dmi b/icons/obj/reagentfillings.dmi index d7a9a03c41..1ac941bae3 100644 Binary files a/icons/obj/reagentfillings.dmi and b/icons/obj/reagentfillings.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 83dd4f5df8..f2b84bbe03 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/icons/turf/floors/glass.dmi b/icons/turf/floors/glass.dmi new file mode 100644 index 0000000000..adf6f57aaa Binary files /dev/null and b/icons/turf/floors/glass.dmi differ diff --git a/icons/turf/floors/reinf_glass.dmi b/icons/turf/floors/reinf_glass.dmi new file mode 100644 index 0000000000..dda99cd07f Binary files /dev/null and b/icons/turf/floors/reinf_glass.dmi differ diff --git a/modular_citadel/code/modules/client/loadout/backpack.dm b/modular_citadel/code/modules/client/loadout/backpack.dm index fbc498df64..0b700b11e2 100644 --- a/modular_citadel/code/modules/client/loadout/backpack.dm +++ b/modular_citadel/code/modules/client/loadout/backpack.dm @@ -98,3 +98,7 @@ name = "A diamond ring box" path = /obj/item/storage/fancy/ringbox/diamond cost = 5 + +/datum/gear/backpack/necklace//this is here because loadout doesn't support proper accessories + name = "A renameable necklace" + path = /obj/item/clothing/accessory/necklace diff --git a/modular_citadel/code/modules/client/loadout/gloves.dm b/modular_citadel/code/modules/client/loadout/gloves.dm index 85be8bd487..ffa4724f63 100644 --- a/modular_citadel/code/modules/client/loadout/gloves.dm +++ b/modular_citadel/code/modules/client/loadout/gloves.dm @@ -24,3 +24,7 @@ name = "A diamond ring" path = /obj/item/clothing/gloves/ring/diamond cost = 4 + +/datum/gear/gloves/customring + name = "A ring, renameable" + path = /obj/item/clothing/gloves/ring/custom diff --git a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm index 03a124e306..b864a9fe25 100644 --- a/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm +++ b/modular_citadel/code/modules/projectiles/guns/pumpenergy.dm @@ -12,7 +12,7 @@ /obj/item/gun/energy/pumpaction/emp_act(severity) //makes it not rack itself when emp'd cell.use(round(cell.charge / severity)) - chambered = 0 //we empty the chamber + chambered = null //we empty the chamber update_icon() /obj/item/gun/energy/pumpaction/process() //makes it not rack itself when self-charging @@ -20,7 +20,7 @@ charge_tick++ if(charge_tick < charge_delay) return - charge_tick = 0 + charge_tick = null if(selfcharge == EGUN_SELFCHARGE_BORG) var/atom/owner = loc if(istype(owner, /obj/item/robot_module)) @@ -44,7 +44,7 @@ if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... var/obj/item/ammo_casing/energy/shot = chambered cell.use(shot.e_cost)//... drain the cell cell - chambered = 0 //either way, released the prepared shot + chambered = null //either way, released the prepared shot /obj/item/gun/energy/pumpaction/post_set_firemode() var/has_shot = chambered diff --git a/modular_citadel/icons/mob/mam_snouts.dmi b/modular_citadel/icons/mob/mam_snouts.dmi index ab1a4654b4..4f6682f789 100644 Binary files a/modular_citadel/icons/mob/mam_snouts.dmi and b/modular_citadel/icons/mob/mam_snouts.dmi differ diff --git a/modular_citadel/icons/mob/widerobot.dmi b/modular_citadel/icons/mob/widerobot.dmi index 50c29bb75f..29eb35c715 100644 Binary files a/modular_citadel/icons/mob/widerobot.dmi and b/modular_citadel/icons/mob/widerobot.dmi differ diff --git a/sound/ambience/antag/ecult_op.ogg b/sound/ambience/antag/ecult_op.ogg new file mode 100644 index 0000000000..9944e833a6 Binary files /dev/null and b/sound/ambience/antag/ecult_op.ogg differ diff --git a/sound/effects/footstep/rustystep1.ogg b/sound/effects/footstep/rustystep1.ogg new file mode 100644 index 0000000000..bf90d52779 Binary files /dev/null and b/sound/effects/footstep/rustystep1.ogg differ diff --git a/sound/machines/sm/accent/delam/1.ogg b/sound/machines/sm/accent/delam/1.ogg new file mode 100644 index 0000000000..75c79f89ab Binary files /dev/null and b/sound/machines/sm/accent/delam/1.ogg differ diff --git a/sound/machines/sm/accent/delam/10.ogg b/sound/machines/sm/accent/delam/10.ogg new file mode 100644 index 0000000000..c87b63b526 Binary files /dev/null and b/sound/machines/sm/accent/delam/10.ogg differ diff --git a/sound/machines/sm/accent/delam/11.ogg b/sound/machines/sm/accent/delam/11.ogg new file mode 100644 index 0000000000..c7f678245b Binary files /dev/null and b/sound/machines/sm/accent/delam/11.ogg differ diff --git a/sound/machines/sm/accent/delam/12.ogg b/sound/machines/sm/accent/delam/12.ogg new file mode 100644 index 0000000000..a395942183 Binary files /dev/null and b/sound/machines/sm/accent/delam/12.ogg differ diff --git a/sound/machines/sm/accent/delam/13.ogg b/sound/machines/sm/accent/delam/13.ogg new file mode 100644 index 0000000000..934f17947d Binary files /dev/null and b/sound/machines/sm/accent/delam/13.ogg differ diff --git a/sound/machines/sm/accent/delam/14.ogg b/sound/machines/sm/accent/delam/14.ogg new file mode 100644 index 0000000000..4175e5b947 Binary files /dev/null and b/sound/machines/sm/accent/delam/14.ogg differ diff --git a/sound/machines/sm/accent/delam/15.ogg b/sound/machines/sm/accent/delam/15.ogg new file mode 100644 index 0000000000..dcf73deb84 Binary files /dev/null and b/sound/machines/sm/accent/delam/15.ogg differ diff --git a/sound/machines/sm/accent/delam/16.ogg b/sound/machines/sm/accent/delam/16.ogg new file mode 100644 index 0000000000..20bc19399b Binary files /dev/null and b/sound/machines/sm/accent/delam/16.ogg differ diff --git a/sound/machines/sm/accent/delam/17.ogg b/sound/machines/sm/accent/delam/17.ogg new file mode 100644 index 0000000000..b517fb3d3d Binary files /dev/null and b/sound/machines/sm/accent/delam/17.ogg differ diff --git a/sound/machines/sm/accent/delam/18.ogg b/sound/machines/sm/accent/delam/18.ogg new file mode 100644 index 0000000000..4ef138d27a Binary files /dev/null and b/sound/machines/sm/accent/delam/18.ogg differ diff --git a/sound/machines/sm/accent/delam/19.ogg b/sound/machines/sm/accent/delam/19.ogg new file mode 100644 index 0000000000..f638a6971b Binary files /dev/null and b/sound/machines/sm/accent/delam/19.ogg differ diff --git a/sound/machines/sm/accent/delam/2.ogg b/sound/machines/sm/accent/delam/2.ogg new file mode 100644 index 0000000000..5b480daa2e Binary files /dev/null and b/sound/machines/sm/accent/delam/2.ogg differ diff --git a/sound/machines/sm/accent/delam/20.ogg b/sound/machines/sm/accent/delam/20.ogg new file mode 100644 index 0000000000..6072bc6227 Binary files /dev/null and b/sound/machines/sm/accent/delam/20.ogg differ diff --git a/sound/machines/sm/accent/delam/21.ogg b/sound/machines/sm/accent/delam/21.ogg new file mode 100644 index 0000000000..1223dd946d Binary files /dev/null and b/sound/machines/sm/accent/delam/21.ogg differ diff --git a/sound/machines/sm/accent/delam/22.ogg b/sound/machines/sm/accent/delam/22.ogg new file mode 100644 index 0000000000..9ccfb9b55a Binary files /dev/null and b/sound/machines/sm/accent/delam/22.ogg differ diff --git a/sound/machines/sm/accent/delam/23.ogg b/sound/machines/sm/accent/delam/23.ogg new file mode 100644 index 0000000000..6399a8376a Binary files /dev/null and b/sound/machines/sm/accent/delam/23.ogg differ diff --git a/sound/machines/sm/accent/delam/24.ogg b/sound/machines/sm/accent/delam/24.ogg new file mode 100644 index 0000000000..b51d359807 Binary files /dev/null and b/sound/machines/sm/accent/delam/24.ogg differ diff --git a/sound/machines/sm/accent/delam/25.ogg b/sound/machines/sm/accent/delam/25.ogg new file mode 100644 index 0000000000..823f22f136 Binary files /dev/null and b/sound/machines/sm/accent/delam/25.ogg differ diff --git a/sound/machines/sm/accent/delam/26.ogg b/sound/machines/sm/accent/delam/26.ogg new file mode 100644 index 0000000000..24b2a2f040 Binary files /dev/null and b/sound/machines/sm/accent/delam/26.ogg differ diff --git a/sound/machines/sm/accent/delam/27.ogg b/sound/machines/sm/accent/delam/27.ogg new file mode 100644 index 0000000000..4b4b145b7b Binary files /dev/null and b/sound/machines/sm/accent/delam/27.ogg differ diff --git a/sound/machines/sm/accent/delam/28.ogg b/sound/machines/sm/accent/delam/28.ogg new file mode 100644 index 0000000000..7bc71bf0e6 Binary files /dev/null and b/sound/machines/sm/accent/delam/28.ogg differ diff --git a/sound/machines/sm/accent/delam/29.ogg b/sound/machines/sm/accent/delam/29.ogg new file mode 100644 index 0000000000..7fec2f271c Binary files /dev/null and b/sound/machines/sm/accent/delam/29.ogg differ diff --git a/sound/machines/sm/accent/delam/3.ogg b/sound/machines/sm/accent/delam/3.ogg new file mode 100644 index 0000000000..5b57cc2707 Binary files /dev/null and b/sound/machines/sm/accent/delam/3.ogg differ diff --git a/sound/machines/sm/accent/delam/30.ogg b/sound/machines/sm/accent/delam/30.ogg new file mode 100644 index 0000000000..ed1ec7d89f Binary files /dev/null and b/sound/machines/sm/accent/delam/30.ogg differ diff --git a/sound/machines/sm/accent/delam/31.ogg b/sound/machines/sm/accent/delam/31.ogg new file mode 100644 index 0000000000..0baa82e246 Binary files /dev/null and b/sound/machines/sm/accent/delam/31.ogg differ diff --git a/sound/machines/sm/accent/delam/32.ogg b/sound/machines/sm/accent/delam/32.ogg new file mode 100644 index 0000000000..e925b32d67 Binary files /dev/null and b/sound/machines/sm/accent/delam/32.ogg differ diff --git a/sound/machines/sm/accent/delam/33.ogg b/sound/machines/sm/accent/delam/33.ogg new file mode 100644 index 0000000000..9ddec0e84a Binary files /dev/null and b/sound/machines/sm/accent/delam/33.ogg differ diff --git a/sound/machines/sm/accent/delam/4.ogg b/sound/machines/sm/accent/delam/4.ogg new file mode 100644 index 0000000000..aa4f4da071 Binary files /dev/null and b/sound/machines/sm/accent/delam/4.ogg differ diff --git a/sound/machines/sm/accent/delam/5.ogg b/sound/machines/sm/accent/delam/5.ogg new file mode 100644 index 0000000000..be438f6f15 Binary files /dev/null and b/sound/machines/sm/accent/delam/5.ogg differ diff --git a/sound/machines/sm/accent/delam/6.ogg b/sound/machines/sm/accent/delam/6.ogg new file mode 100644 index 0000000000..b89d52a564 Binary files /dev/null and b/sound/machines/sm/accent/delam/6.ogg differ diff --git a/sound/machines/sm/accent/delam/7.ogg b/sound/machines/sm/accent/delam/7.ogg new file mode 100644 index 0000000000..3a9cfc62ca Binary files /dev/null and b/sound/machines/sm/accent/delam/7.ogg differ diff --git a/sound/machines/sm/accent/delam/8.ogg b/sound/machines/sm/accent/delam/8.ogg new file mode 100644 index 0000000000..7bc0a727fa Binary files /dev/null and b/sound/machines/sm/accent/delam/8.ogg differ diff --git a/sound/machines/sm/accent/delam/9.ogg b/sound/machines/sm/accent/delam/9.ogg new file mode 100644 index 0000000000..5c1bd37405 Binary files /dev/null and b/sound/machines/sm/accent/delam/9.ogg differ diff --git a/sound/machines/sm/accent/normal/1.ogg b/sound/machines/sm/accent/normal/1.ogg new file mode 100644 index 0000000000..e92beed7fe Binary files /dev/null and b/sound/machines/sm/accent/normal/1.ogg differ diff --git a/sound/machines/sm/accent/normal/10.ogg b/sound/machines/sm/accent/normal/10.ogg new file mode 100644 index 0000000000..9efb616f0b Binary files /dev/null and b/sound/machines/sm/accent/normal/10.ogg differ diff --git a/sound/machines/sm/accent/normal/11.ogg b/sound/machines/sm/accent/normal/11.ogg new file mode 100644 index 0000000000..2af0981ef1 Binary files /dev/null and b/sound/machines/sm/accent/normal/11.ogg differ diff --git a/sound/machines/sm/accent/normal/12.ogg b/sound/machines/sm/accent/normal/12.ogg new file mode 100644 index 0000000000..2fab78b02d Binary files /dev/null and b/sound/machines/sm/accent/normal/12.ogg differ diff --git a/sound/machines/sm/accent/normal/13.ogg b/sound/machines/sm/accent/normal/13.ogg new file mode 100644 index 0000000000..784e84e4a8 Binary files /dev/null and b/sound/machines/sm/accent/normal/13.ogg differ diff --git a/sound/machines/sm/accent/normal/14.ogg b/sound/machines/sm/accent/normal/14.ogg new file mode 100644 index 0000000000..af170394dd Binary files /dev/null and b/sound/machines/sm/accent/normal/14.ogg differ diff --git a/sound/machines/sm/accent/normal/15.ogg b/sound/machines/sm/accent/normal/15.ogg new file mode 100644 index 0000000000..05c88c6b29 Binary files /dev/null and b/sound/machines/sm/accent/normal/15.ogg differ diff --git a/sound/machines/sm/accent/normal/16.ogg b/sound/machines/sm/accent/normal/16.ogg new file mode 100644 index 0000000000..46b0e33980 Binary files /dev/null and b/sound/machines/sm/accent/normal/16.ogg differ diff --git a/sound/machines/sm/accent/normal/17.ogg b/sound/machines/sm/accent/normal/17.ogg new file mode 100644 index 0000000000..e432b2ee02 Binary files /dev/null and b/sound/machines/sm/accent/normal/17.ogg differ diff --git a/sound/machines/sm/accent/normal/18.ogg b/sound/machines/sm/accent/normal/18.ogg new file mode 100644 index 0000000000..1e0e91abc8 Binary files /dev/null and b/sound/machines/sm/accent/normal/18.ogg differ diff --git a/sound/machines/sm/accent/normal/19.ogg b/sound/machines/sm/accent/normal/19.ogg new file mode 100644 index 0000000000..31de063e02 Binary files /dev/null and b/sound/machines/sm/accent/normal/19.ogg differ diff --git a/sound/machines/sm/accent/normal/2.ogg b/sound/machines/sm/accent/normal/2.ogg new file mode 100644 index 0000000000..05e3c9ff17 Binary files /dev/null and b/sound/machines/sm/accent/normal/2.ogg differ diff --git a/sound/machines/sm/accent/normal/20.ogg b/sound/machines/sm/accent/normal/20.ogg new file mode 100644 index 0000000000..36810bd8f1 Binary files /dev/null and b/sound/machines/sm/accent/normal/20.ogg differ diff --git a/sound/machines/sm/accent/normal/21.ogg b/sound/machines/sm/accent/normal/21.ogg new file mode 100644 index 0000000000..306e8856e5 Binary files /dev/null and b/sound/machines/sm/accent/normal/21.ogg differ diff --git a/sound/machines/sm/accent/normal/22.ogg b/sound/machines/sm/accent/normal/22.ogg new file mode 100644 index 0000000000..38286aa98b Binary files /dev/null and b/sound/machines/sm/accent/normal/22.ogg differ diff --git a/sound/machines/sm/accent/normal/23.ogg b/sound/machines/sm/accent/normal/23.ogg new file mode 100644 index 0000000000..89f85fed91 Binary files /dev/null and b/sound/machines/sm/accent/normal/23.ogg differ diff --git a/sound/machines/sm/accent/normal/24.ogg b/sound/machines/sm/accent/normal/24.ogg new file mode 100644 index 0000000000..7c12a3e768 Binary files /dev/null and b/sound/machines/sm/accent/normal/24.ogg differ diff --git a/sound/machines/sm/accent/normal/25.ogg b/sound/machines/sm/accent/normal/25.ogg new file mode 100644 index 0000000000..f89175ceb1 Binary files /dev/null and b/sound/machines/sm/accent/normal/25.ogg differ diff --git a/sound/machines/sm/accent/normal/26.ogg b/sound/machines/sm/accent/normal/26.ogg new file mode 100644 index 0000000000..9efd1d8ef3 Binary files /dev/null and b/sound/machines/sm/accent/normal/26.ogg differ diff --git a/sound/machines/sm/accent/normal/27.ogg b/sound/machines/sm/accent/normal/27.ogg new file mode 100644 index 0000000000..1fb1edbb5a Binary files /dev/null and b/sound/machines/sm/accent/normal/27.ogg differ diff --git a/sound/machines/sm/accent/normal/28.ogg b/sound/machines/sm/accent/normal/28.ogg new file mode 100644 index 0000000000..890c5ea429 Binary files /dev/null and b/sound/machines/sm/accent/normal/28.ogg differ diff --git a/sound/machines/sm/accent/normal/29.ogg b/sound/machines/sm/accent/normal/29.ogg new file mode 100644 index 0000000000..cd2aa40714 Binary files /dev/null and b/sound/machines/sm/accent/normal/29.ogg differ diff --git a/sound/machines/sm/accent/normal/3.ogg b/sound/machines/sm/accent/normal/3.ogg new file mode 100644 index 0000000000..38de5571a4 Binary files /dev/null and b/sound/machines/sm/accent/normal/3.ogg differ diff --git a/sound/machines/sm/accent/normal/30.ogg b/sound/machines/sm/accent/normal/30.ogg new file mode 100644 index 0000000000..87d1782768 Binary files /dev/null and b/sound/machines/sm/accent/normal/30.ogg differ diff --git a/sound/machines/sm/accent/normal/31.ogg b/sound/machines/sm/accent/normal/31.ogg new file mode 100644 index 0000000000..9ce3eeb72e Binary files /dev/null and b/sound/machines/sm/accent/normal/31.ogg differ diff --git a/sound/machines/sm/accent/normal/32.ogg b/sound/machines/sm/accent/normal/32.ogg new file mode 100644 index 0000000000..26ca056142 Binary files /dev/null and b/sound/machines/sm/accent/normal/32.ogg differ diff --git a/sound/machines/sm/accent/normal/33.ogg b/sound/machines/sm/accent/normal/33.ogg new file mode 100644 index 0000000000..24964c1ce9 Binary files /dev/null and b/sound/machines/sm/accent/normal/33.ogg differ diff --git a/sound/machines/sm/accent/normal/4.ogg b/sound/machines/sm/accent/normal/4.ogg new file mode 100644 index 0000000000..2e71e976e8 Binary files /dev/null and b/sound/machines/sm/accent/normal/4.ogg differ diff --git a/sound/machines/sm/accent/normal/5.ogg b/sound/machines/sm/accent/normal/5.ogg new file mode 100644 index 0000000000..04852e10f2 Binary files /dev/null and b/sound/machines/sm/accent/normal/5.ogg differ diff --git a/sound/machines/sm/accent/normal/6.ogg b/sound/machines/sm/accent/normal/6.ogg new file mode 100644 index 0000000000..bf06c06bbe Binary files /dev/null and b/sound/machines/sm/accent/normal/6.ogg differ diff --git a/sound/machines/sm/accent/normal/7.ogg b/sound/machines/sm/accent/normal/7.ogg new file mode 100644 index 0000000000..d29821701f Binary files /dev/null and b/sound/machines/sm/accent/normal/7.ogg differ diff --git a/sound/machines/sm/accent/normal/8.ogg b/sound/machines/sm/accent/normal/8.ogg new file mode 100644 index 0000000000..0b94b9dbe0 Binary files /dev/null and b/sound/machines/sm/accent/normal/8.ogg differ diff --git a/sound/machines/sm/accent/normal/9.ogg b/sound/machines/sm/accent/normal/9.ogg new file mode 100644 index 0000000000..545b038be1 Binary files /dev/null and b/sound/machines/sm/accent/normal/9.ogg differ diff --git a/sound/machines/sm/loops/calm.ogg b/sound/machines/sm/loops/calm.ogg new file mode 100644 index 0000000000..cee14fcd13 Binary files /dev/null and b/sound/machines/sm/loops/calm.ogg differ diff --git a/sound/machines/sm/loops/delamming.ogg b/sound/machines/sm/loops/delamming.ogg new file mode 100644 index 0000000000..7d79f0e3c4 Binary files /dev/null and b/sound/machines/sm/loops/delamming.ogg differ diff --git a/sound/machines/sm/supermatter1.ogg b/sound/machines/sm/supermatter1.ogg index 1860e78800..be5185009e 100644 Binary files a/sound/machines/sm/supermatter1.ogg and b/sound/machines/sm/supermatter1.ogg differ diff --git a/sound/machines/sm/supermatter2.ogg b/sound/machines/sm/supermatter2.ogg index fb2d39fe26..5c98d28ed1 100644 Binary files a/sound/machines/sm/supermatter2.ogg and b/sound/machines/sm/supermatter2.ogg differ diff --git a/sound/machines/sm/supermatter3.ogg b/sound/machines/sm/supermatter3.ogg index 93ac3d505b..fb8e09166c 100644 Binary files a/sound/machines/sm/supermatter3.ogg and b/sound/machines/sm/supermatter3.ogg differ diff --git a/sound/music/twilight.ogg b/sound/music/twilight.ogg new file mode 100644 index 0000000000..74b811b3e5 Binary files /dev/null and b/sound/music/twilight.ogg differ diff --git a/strings/round_start_sounds.txt b/strings/round_start_sounds.txt index c67bf6b4a6..177a3ea0a8 100644 --- a/strings/round_start_sounds.txt +++ b/strings/round_start_sounds.txt @@ -25,3 +25,4 @@ sound/music/rocketridersprayer.ogg sound/music/theend.ogg sound/music/flyinghigh.ogg sound/music/samsara.ogg +sound/music/twilight.ogg \ No newline at end of file diff --git a/strings/sillytips.txt b/strings/sillytips.txt index bc59a109f0..e6710de95e 100644 --- a/strings/sillytips.txt +++ b/strings/sillytips.txt @@ -25,6 +25,7 @@ This game is older than most of the people playing it. Do not go gentle into that good night. Flashbangs can weaken blob tiles, allowing for you and the crew to easily destroy them. Just the tip? +You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place Mekhi on a table by clicking on it, or throw them by toggling on throwing. Some people are unable to read text on a game where half of it is based on text. As the Captain, you can use a whetstone to sharpen your fancy fountain pen for extra robustness. As the Lawyer, you are the last bastion of roleplay-focused jobs. Even the curator got a whip to go fight people with, that sellout! @@ -43,5 +44,8 @@ Plasma men are a powerful race with many perks! No really, I swear! So what if t As a Cargo Tech make sure to always buy a tesla to sell back to CC. They love those those. Trust me! Help. Maints. -BZ stops or slows down Lings chem regeneration drastically, make sure to BZ flood the station when lings are confirmed! Admins always regret meme options in their polls. +Putting cat ears on securitrons makes them table people and nya. Mekhi isn't a cat, but he still goes on the table, just roll with it. +As a Changeling, you can live without a head as they are merely vestigal to you, now, finally, you can be a Dullahan without it being Halloween. +People actually have fictional sex between fictional characters in this game. +When in doubt, take a break. A long break, preferably. If the game is wearing down your mental state and it's starting to lose any semblance of fun value, go and do something else for a month or two. By the time you come back, everything you liked will have been changed anyways. diff --git a/strings/tips.txt b/strings/tips.txt index 043405c7a0..a1d2befab3 100644 --- a/strings/tips.txt +++ b/strings/tips.txt @@ -1,83 +1,88 @@ Where the space map levels connect is randomized every round, but are otherwise kept consistent within rounds. Remember that they are not necessarily bidirectional! You can catch thrown items by toggling on your throw mode with an empty hand active. -To crack the safe in the vault, use a stethoscope or explosives on it. +To crack the safe in the vault, have a stethoscope in one of your hands and fiddle with the tumbler or you can alternatively use several concentrated explosive charges on it. Remember that the latter may result in the contents of the safe becoming a pile of ash. You can climb onto a table by dragging yourself onto one. This takes time and drops the items in your hands on the table. Clicking on a table that someone else is climbing onto will knock them down. You can drag other players onto yourself to open the strip menu, letting you remove their equipment or force them to wear something. Note that exosuits or helmets will block your access to the clothing beneath them, and that certain items take longer to strip or put on than others. Clicking on a windoor rather then bumping into it will keep it open, you can click it again to close it. You can spray a fire extinguisher, throw items or fire a gun while floating through space to change your direction. Simply fire opposite to where you want to go. You can change the control scheme by pressing tab. One is WASD, the other is the arrow keys. Keep in mind that hotkeys are also changed with this. -All vending machines can be hacked to obtain some contraband items from them, and many can be fed with coins to gain access to premium items. +All vending machines can be hacked to obtain some contraband items from them, and many may charge extra credits to give you premium items. Firesuits and winter coats offer mild protection from the cold, allowing you to spend longer periods of time near breaches and space than if wearing nothing at all. Glass shards can be welded to make glass, and metal rods can be welded to make metal. Ores can be welded too, but this takes a lot of fuel. If you need to drag multiple people either to safety or to space, bring a locker or crate over and stuff them all in before hauling them off. -You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab will momentarily stun someone, allow you to place Mekhi on a table by clicking on it, or throw them by toggling on throwing. +You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on them once more. An aggressive grab can temporarily stun someone depending on their luck with resisting out of it, allowing you to slam them on a table by clicking on it, or throw them by toggling on throwing. Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking. The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting! You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand. -You can recolor certain items like jumpsuits and gloves in washing machines by also throwing in a crayon. +You can recolor certain items like jumpsuits and gloves in washing machines by also throwing in a crayon. For more advanced fashion you can spray items with a spray can to tint its colors. Some items work better than others at displaying their tints, like sterile and paper masks, or darkly colored gloves. Maintenance is full of equipment that is randomized every round. Look around and see if anything is worth using. -Some roles cannot be antagonists by default, but antag selection is decided first. For instance, you can set Security Officer to High without affecting your chances of becoming an antag -- the game will just select a different role. +Some roles cannot be antagonists by default, but antag selection is decided first. For instance, you can set Security Officer to High without affecting your chances of becoming an antag - the game will just assign you to your next preferred role - or in the case that you have no such preferences set, a random role entirely. There are many places around the station to hide contraband. A few for starters: linen boxes, toilet cisterns, body bags. Experiment to find more! On all maps, you can use a machine in the vault to deposit space cash for cargo points. Otherwise, use it to steal the station's cash and get out before the alarm goes off. -As the Captain, you are one of the highest priority targets on the station. Everything from revolutions, to nuclear operatives, to traitors that need to rob you of your unique lasgun or your life are things to worry about. -As the Captain, always take the nuclear disk and pinpointer with you every shift. It's a good idea to give one of these to another head you can trust with keeping it safe, such as the Head of Security. +As the Captain, you are one of the highest priority targets on the station. Everything from revolutions looking to thwart your rule, to nuclear operatives seeking the disk, to traitors that need to rob you of your several high value items - or your life are all things to be concerned about. +As the Captain, always take the nuclear disk and pinpointer with you every shift. It's a good idea to give one of these to another head you can trust with keeping it safe, such as the Head of Personnel. As the Captain, you have absolute access and control over the station, but this does not mean that being a horrible person won't result in mutiny and a ban. As the Captain, you have a fancy pen that can be used as a holdout dagger or even as a scalpel in surgery! As the Captain, you can purchase a new emergency shuttle using a communications console. Some require credits, while others give you credits in exchange. Keep in mind that purchasing dangerous shuttles will incur the ire of your crew. -As the Chief Medical Officer, your hypospray is like a refillable instant injection syringe that can hold 30 units as opposed to the standard 15. +As the Chief Medical Officer, your hypospray is like the ones that your Medical Doctors can buy, except it comes in a fancy box that can hold several more hypovials than the standard, and already comes preloaded with specially-made high-capacity hypovials that hold double the reagents the standard ones do. As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and geneticists during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting. -As a Medical Doctor, pester Research for improved surgical tools. They work faster, don't cost much and are typically more deadly. +As a Medical Doctor, pester Research for improved surgical tools. They work faster, combine the purposes of several tools in one (scalpel/saw, retractor/hemostat, drill/cautery), and don't cost many materials to boot! +As a Medical Doctor, the surgical saw and drill are both powerful weapons, the saw is sharp and can slice and dice, while the drill can quickly blind someone if aimed for the eyes. The laser scalpel is an upgraded version producible with Research's aid, and it has the highest force of most common place weapons, while still remaining sharp. As a Medical Doctor, your belt can hold a full set of surgical tools. Using sterilizine before each attempt during surgery will reduce your failure chance on tricky steps or when using less-than-optimal equipment. As a Medical Doctor, you can attempt to drain blood from a husk with a syringe to determine the cause. If you can extract blood, it was caused by extreme temperatures or lasers, if there is no blood to extract, you have confirmed the presence of changelings. As a Medical Doctor, while both heal toxin damage, the difference between charcoal and antitoxin is that charcoal will actively remove all other reagents from one's body, while antitoxin only removes various toxins - but can overdose. -As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva. +As a Medical Doctor, you can surgically implant or extract things from people's chests by performing a cavity implant. This could range from inserting a suicide bomb to embedding the nuke disk into the Captain's chest. +As a Medical Doctor, it's of utmost urgency that you tend to anyone who's been hugged by a facehugger. You only have a couple of minutes from the initial attachment to perform organ manipulation to their chest and remove the rapidly developing alien embryo before it bursts out and immediately kills your patient. As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone. Using disarm attempt will intentionally fail the surgery step. As a Medical Doctor, corpses with the "...and their soul has departed" description no longer have a ghost attached to them and aren't usually revivable or cloneable. However it may prove useful to be creative in your revivification techniques with these bodies. -As a Medical Doctor, treating plasmamen is not impossible! Salbutamol stops them from suffocating and showers stop them from burning alive. You can even perform surgery on them by doing the procedure on a roller bed under a shower. +As a Medical Doctor, treating plasmamen is not impossible! Salbutamol and epinephrine stops them from suffocating due to lack of internals and showers stop them from burning alive. You can even perform surgery on them by doing the procedure on a roller bed under a shower. As a Medical Doctor, you can point your penlight at people to create a medical hologram. This lets them know that you're coming to treat them. As a Medical Doctor, you can extract implants by holding an empty implant case in your offhand while performing the extraction step. As a Medical Doctor, clone scanning people will implant them with a health tracker that displays their vitals in the clone records. Useful to check on crew members that didn't activate suit sensors! -As a Medical Doctor, medical gauze stops bleeding as well as healing 5 brute damage, this even works on the dead! Make sure to always have some gauze on you to stop bleeding before dragging someone. -As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment! +As a Medical Doctor, medical gauze is an incredibly underrated tool. It can be used to entirely halt a limb from bleeding or sling one that's been shattered until it can be given proper attention. This even works on the dead, too! Be sure to stop someone's bleeding whether they're in critical condition or a corpse, as dragging someone whom is bleeding will rapidly deplete them of all their blood. +As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. See which chemicals have the best synergy, both in healing, and in harming. Experiment! As a Chemist, some chemicals can only be synthesized by heating up the contents in the chemical heater. As a Chemist, you will be expected to supply crew with certain chemicals. For example, clonexadone and mannitol for the cryo tubes, unstable mutagen and saltpetre for botany as well as healing pills and patches for the front desk. As a Chemist, you can make 100u bottles from plastic sheets. The ChemMaster can produce infinite 30u glass bottles as well. +As a Chemist, be sure to stock up some hypovials with useful chemicals for any doctors looking to heal on the go, you can also print out the deluxe hypovials at an autolathe specifically for the CMO's special hypospray. +As a Chemist, the reagent dartgun, while neutered in its ability to harm - can still be loaded up with morphine for a ghetto sedation weapon, and a quick shot of charcoal can make a slime hybrid regret their life choices in an instant. As a Geneticist, you can eject someone from cloning early by clicking on the cloner pod with your ID. Note that they will suffer more genetic damage and may lose vital organs from this. -As a Geneticist, becoming a hulk makes you capable of dealing high melee damage, stunlocking people, and punching through walls. However, you can't fire guns, will lose your hulk status if you take too much damage, and are not considered a human by the AI while you are a hulk. +As a Geneticist, becoming a hulk makes you capable of dealing high melee damage, becoming immune to most traditional stuns, and punching through walls. However, you can't fire guns, and will lose your hulk status if you take too much damage. As the Virologist, your viruses can range from healing powers so great that you can heal out of critical status, or diseases so dangerous they can kill the entire crew with airborne spontaneous combustion. Experiment! As the Virologist, you only require small amounts of vaccine to heal a sick patient. Work with the Chemist to distribute your cures more efficiently. As the Research Director, you can take AIs out of their cores by loading them into an intelliCard, and then from there into an AI system integrity restorer computer to revive and/or repair them. As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled. As the Research Director, you can upgrade your modular console with better computer parts to speed up its functions. This can be useful when using the AI system integrity restorer. As the Research Director, your console's NTnet monitoring tool can be used to retrieve airlock passkeys, provided that someone used a door remote. -As a Scientist, you can use the mutation toxin obtained from green slimes to turn yourself into a jelly mutant. Each subspecies has unique features - for example telepathic powers, duplicating bodies or integrating slime extracts! +As a Scientist, you can use the mutation toxin obtained from green slimes to turn yourself into a jelly mutant. Each subspecies has unique features - for example telepathic powers, duplicating bodies or integrating slime extracts for several unique effects! As a Scientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract. -As a Scientist, you can disable anomalies by scanning them with an analyzer, then send a signal on the frequency it gives you with a remote signalling device. This will leave behind an anomaly core, which can be used to construct a Phazon mech, or be used in the destructive analyzer for a 10,000 point bonus! +As a Scientist, you can disable anomalies by scanning them with an analyzer, and then sending a signal on the frequency it gives you with a remote signalling device. Alternatively, you can print out anomaly defusal tools which can instantly disable an anomaly at the protolathe with some research, both of these methods will leave behind an anomaly core, which can be used to construct a Phazon mech, or be used in the destructive analyzer for a 10,000 point bonus! As a Scientist, researchable stock parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions. As a Scientist, you can generate research points by letting the tachyon-doppler array record increasingly large explosions. As a Scientist, getting drunk just enough will speed up research. Skol! As a Scientist, you can get points by placing slime cores into the destructive analyzer! This even works with crossbred slime cores. -As a Scientist, work with botanists to get different types of seeds, as each type of seed can be used in the destructive analyzer for a small amount of Rnd type points! +As a Scientist, you can get a minuscule amount of points by sacrificing a packet of seeds from Hydroponics into the destructive analyzer! While each individual one may not yield many points per, you can quite easily amass a very large variety of seeds, which could add up over time for a couple extra minutes shaved off of maxing out RND. As a Roboticist, keep an ear out for anomaly announcements. If you get your hands on an anomaly core, you can build a Phazon mech! As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage from lasers, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil. As a Roboticist, you can reset a cyborg's module by cutting and mending the reset wire with a wire cutter. +As a Roboticist, pay mind when toying with a cyborg's wires. It's best to pulse wires before immediately cutting them, as cutting them right away without knowing what they do may sever them from the AI, or disable their camera. As a Roboticist, you can greatly help out Shaft Miners by building a Firefighter APLU equipped with a hydraulic clamp and plasma cutter. The mech is ash storm proof and can even walk across lava! -As a Roboticist, you can augment people with cyborg limbs. Augmented limbs can easily be repaired with cables and welders. +As a Roboticist, you can augment people with cyborg limbs. Augmented limbs are immune to the vacuum of space and temperatures while they can very easily be repaired with welders (brute) and cable coils (burn). As a Roboticist, you can use your printer that is linked to the ore silo to teleport mats into your work place! As a Roboticist, you can upgrade cleanbots with adv mops and brooms to make them faster and better! -As a Roboticist, you can upgrade medical bots with diamond-tipped syringes, MK.II Hypospray, dispenser-sleeper-chemheater boards to make them inject faster, harder and better chems! -As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them. +As a Roboticist, you can upgrade medical bots with diamond-tipped syringes, hyposprays, and chemistry machine boards to make their injections pierce hardsuits, work faster, and inject higher quality medicines! +As the AI, you can click on people's names when they speak over the radio to jump your eye to them. This only works if there are cameras that can see them and are not wearing anything which would obsfuscate their face or tracking capabilities. As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt. -As the AI, you can take pictures with your camera and upload them to newscasters. -As a Cyborg, choose your module carefully, as only cutting and mending your reset wire will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. -As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands. +As the AI, you can take pictures with your camera and upload them to newscasters. Cyborgs also share from this pool of pictures. +As a Cyborg, choose your module carefully, as only having your reset wire cut and mended by someone capable of manipulation will let you repick it. If possible, refrain from choosing a module until a situation that requires one occurs. +As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes and EMPs can easily stunlock you and you fall short in performing any tasks which require hands. As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world! As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds. As a Service Cyborg, your spray can knocks people down. However, it is blocked by gas masks. -As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them. -As a Medical Cyborg, you can fully perform surgery and even augment people. Best of all, they have a 0% failure chance. -As a Janitor Cyborg, you are the bane of all slaughter demons and even Bubblegum himself. Cleaning up blood stains will severely gimp them. -As a Janitor Cyborg, you get a fancy bottle of drying agent! If you want to be nice, spray the janitor boots with them to magically upgrade them to absorbent galoshes. +As an Engineering Cyborg, you can attach air alarm/fire alarm/APC frames to walls by placing them on the floor and using a screwdriver on them. Alternatively, you can use your in-built pseudo-hand manipulator to show those organics who's boss! It can even perform complex tasks such as removing cells from APCs, or inserting plasma canisters into radiation collectors. +As a Medical Cyborg, you can fully perform surgery and even augment people. Best of all, they have a 0% failure chance, even if done on the floor. +As a Janitor Cyborg, you are the bane of all slaughter demons and can even foil Bubblegum himself. Cleaning up blood stains will severely gimp them, although the latter may just turn you into robotic paste. +As a Janitor Cyborg, you get a fancy bottle of drying agent! If you want to be nice, spray the janitor's galoshes with them to magically upgrade them to absorbent galoshes which automatically dry tiles. As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints. As the Chief Engineer, your hardsuit is significantly better than everybody else's. It has the best features of both engineering and atmospherics hardsuits - boasting nigh-invulnerability to radiation and all atmospheric conditions. As the Chief Engineer, you can spy on and even forge PDA communications with the message monitor console! The key is in your office. @@ -94,14 +99,14 @@ As an Engineer, you can convert tesla coils into corona analyzers by using a scr As an Engineer, you can use radiation collectors to generate research points. Load them with a 50/50 oxygen/tritium tank and use a multitool to switch them to research mode. As an Engineer, don't underestimate the humble P.A.C.M.A.N. generators. With upgraded parts, a couple units working in tandem are sufficient to take over for an exploded engine or shattered solars. As an Engineer, your departmental protolathe and circuit printer can manufacture the necessary circuit boards and components to build just about anything. Make extra medical machinery everywhere! Build a gibber for security! Set up an array of emitters pointing down the hall! The possibilities are endless! -As an Engineer, you can pry open secure storage by disabling the engine room APC's main breaker. This is obviously a bad idea if the engine is running. -Don't forget that Cargo has access to a meteor defense satellite that can be ordered BEFORE meteors hit the station. Any idle Engineers should have this on their to-do list. -As an Engineer, your RCD can be reloaded with mineral sheets instead of just compressed matter cartridges. +As an Engineer, you can pry open secure storage by disabling the engine room APC's environmental breaker. This is obviously a bad idea if the engine is running. +As an Engineer, don't forget that Cargo has access to a meteor defense satellite that can be ordered BEFORE meteors hit the station. Any idle Engineers should have this on their to-do list. +As an Engineer, your RCD can be reloaded with mineral sheets instead of just compressed matter cartridges. Materials which are combined alloys of other materials (such as reinforced glass and plasteel) provide more matter per sheet to the RCD. As an Atmospheric Technician, you can unwrench a pipe regardless of the pressures of the gases inside, but if they're too high they can burst out and injure you! As an Atmospheric Technician, look into replacing your gas pumps with volumetric gas pumps, as those move air in flat numerical amounts, rather than percentages which leave trace gases. -As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof rigsuit. +As an Atmospheric Technician, you are better suited to fighting fires than anyone else. As such, you have access to better firesuits, backpack firefighter tanks, and a completely heat and fire proof hardsuit. As an Atmospheric Technician, your backpack firefighter tank can launch resin. This resin will extinguish fires and replace any gases with a safe, room-temperature airmix. -As an Atmospheric Technician, your ATMOS holofan projector blocks gases while allowing objects to pass through. With it, you can quickly contain gas spills, fires and hull breaches. Or, use it to seal a plasmaman cloning room. +As an Atmospheric Technician, your ATMOS holofan projectors can blocks gases and heat while allowing objects to pass through. With it, you can quickly contain gas spills, fires and hull breaches. Or, use it to create a plasmaman friendly lounge. As an Atmospheric Technician, burning a plasma/oxygen mix inside the incinerator will not only produce power, but also gases such as tritium and water vapor. As an Atmospheric Technician, you can change the layer of a pipe by clicking with it on a wrenched pipe or other atmos component of the desired layer. As an Atmospheric Technician, you can take a few cans worth of N2/N2O and cool it down at local freezers. This is a good idea when dealing with (or preparing for) a supermatter meltdown. @@ -111,65 +116,70 @@ As the Head of Security, don't let the power go to your head. You may have high As the Warden, your duty is to be the watchdog of the brig and handler of prisoners when little is happening, and to hand out equipment and weapons to the security officers when a crisis strikes. As the Warden, keep a close eye on the armory at all times, as it is a favored strike point of nuclear operatives and cocky traitors. As the Warden, if a prisoner's crimes are heinous enough you can put them in permabrig or the gulag. Make sure to check on them once in a while! -As the Warden, never underestimate the power of tech slugs! Scattershot fires a cone of weaker lasers, Ion slugs fires EMPs that only effect the tiles they hit, and Pulse slugs fire a singular laser that can one-hit almost any wall! -As the Warden, you can use a surgical saw on riot shotguns to shorten the barrel, making them able to fit in your backpack. +As the Warden, never underestimate the power of tech slugs! Scattershot fires a cone of weaker lasers with little damage fall off, Ion slugs fires EMPs that only effect the tiles they hit, and Pulse slugs fire a singular laser that can one-hit almost any wall! +As the Warden, you can use a surgical saw on riot shotguns to shorten the barrel, making them able to fit in your backpack. Make sure to empty them prior lest you blast yourself in the face! As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals. As the Warden, you can use handcuffs on orange prisoner shoes to turn them into cuffed shoes, forcing prisoners to walk and potentially thwarting an escape. -As the Warden, tracker implants can be used on sec officers. Doing this will let you track their corpse even without suits, though the implant will biodegrade after 5 minutes. -As the Warden, cryostasis shotgun darts hold 10u of chemicals that will not react untill it hits someone. -As the Warden, chemical implants can be loaded with a cocktail of healing or combat chems, perfect for the Hos or other sec officers to use. Be sure to keep a eye on them though, it will not auto inject! EMPs or starvation mite lead to the chemical implant to go off as well. -As the Warden, tracker implants can be used on sec officers. Doing this will let you be able to message them when telecoms are out, or when you suspect coms are compromised. This is also good against rogue AIs as the prisoner tracker doesn't leave logs or alarms for the AI. +As the Warden, tracker implants can be used on crewmembers. Doing this will let you track their person even without suit sensors and even instantly teleport to them at the local teleporter, although the implant will biodegrade after 5 minutes if its holder ever expires. +As the Warden, cryostasis shotgun darts hold 10u of chemicals that will not react until it hits someone. +As the Warden, chemical implants can be loaded with a cocktail of healing or combat chems, perfect for the HoS or other security officers to make use of in a pinch. Be sure to keep a eye on them though, as they cannot be injected without the prisoner management console! EMPs or starvation might lead to the chemical implant going off preemptively. +As the Warden, tracker implants can be used on your security officers. Doing this will let you be able to message them when telecomms are out, or when you suspect comms are compromised. This is also good against rogue AIs as the prisoner tracker doesn't leave logs or alarms for the AI. As a Security Officer, remember that correlation does not equal causation. Someone may have just been at the wrong place at the wrong time! -As a Security Officer, remember that your belt can hold more then one stun baton. -As a Security Officer, remember harm battoning someone in the head can deconvert them form a being a rev! This sadly doesn't work against the cult, nor does this protect them from getting reconverted. -As a Security Officer, remember that you can attach a sec-lite to your taser or your helmet! +As a Security Officer, remember that your belt can hold more than one stun baton. +As a Security Officer, remember harm beating someone in the head with a blunt object can deconvert them form a being a revolutionary! This sadly doesn't work against either cult, nor does this protect them from getting reconverted unlike a mindshield implant. +As a Security Officer, remember that you can attach a seclite to your taser or your helmet! As a Security Officer, communicate and coordinate with your fellow officers using the security channel (:s) to avoid confusion. -As a Security Officer, your sechuds or HUDsunglasses can not only see crewmates' job assignments and criminal status, but also if they are mindshield implanted. Use this to your advantage in a revolution to definitively tell who is on your side! +As a Security Officer, your security HUDglasses can not only see crewmates' job assignments and criminal status, but also if they are mindshield implanted. Use this to your advantage in a revolution to definitively tell who is on your side! As a Security Officer, mindshield implants can only prevent someone from being turned into a cultist: unlike revolutionaries, it will not de-cult them if they have already been converted. -As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them. -As a Security Officer, you can take out the power cell on your baton to replace it with a better or fully charged one. Just use a screwdriver on your baton to remove the old cell -As a Security Officer, you can place riot shotguns on your armor, this even works with winter sec coats! +As a Security Officer, examining someone while wearing your security HUDglasses can allow you to swiftly edit their records and criminal status. Be sure to set someone to WANTED if you can't catch up to them, as it'll alert other officers of who's the bad guy, and cause the little security droids to chase after them for you. +As a Security Officer, you can take out the power cell on your baton to replace it with a better or fully charged one. Just use a screwdriver on your baton to remove the old cell. +As a Security Officer, you can just about any firearm on your vest, this even works with other non-standard armor-substitutes like security winter coats! As the Detective, people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department. As the Detective, you can use your forensics scanner from a distance. -As the Detective, your revolver can be loaded with .357 ammunition obtained from a hacked autolathe. Firing it has a decent chance to blow up your revolver. +As the Detective, your revolver can be loaded with .357 ammunition. Use a screwdriver to permanently modify your revolver into using this type of ammunition, be warned however, firing it has a decent chance to cause the revolver to misfire and shoot you in the foot. As the Lawyer, try to negotiate with the Warden if sentences seem too high for the crime. As the Lawyer, you can try to convince the captain and Head of Security to hold trials for prisoners in the courtroom. As the Head of Personnel, you are not higher ranking than other heads of staff, even though you are expected to take the Captain's place first should he go missing. If the situation seems too rough for you, consider allowing another head to become temporary Captain. -As the Head of Personnel, you are just as large a target as the Captain because of the potential power your ID and computer can hand out. +As the Head of Personnel, you are just as large a target as the Captain because of the potential power your ID and computer can hand out and your comparative vulnerability. As the Mime, your invisible wall power blocks people as well as projectiles. You can use it in a pinch to delay your pursuer. -As the Mime, you can use :r and :l to speak through your ventriloquist dummy. +As the Mime, you can use :r and :l to speak through your ventriloquist dummy. Sadly, this only works if your vow is broken, but at least you don't have to sacrifice your dignity by actually talking. As the Mime, your oath of silence is your source of power. Breaking it robs you of your powers and of your honor. +As the Mime, breaking your vow of silence is seen as incredibly dishonorable. Most people will seek to trouble and generally ignore a talking Mime. As the Clown, if you lose your banana peel, you can still slip people with your PDA! Honk! As the Clown, eating bananas heals you slightly. Honk! As the Clown, your Holy Grail is the mineral bananium, which can be given to the Roboticist to build you a fun and robust mech beloved by everyone. -As the Clown, you can use your stamp on a sheet of cardboard as the first step of making a honkbot. Fun for the whole crew! -As the Chaplain, your null rod has a lot of functions: it can convert water into holy water, which if spread on the ground prevents wizards from jaunting away, can destroy cultist runes by hitting them, and is a very powerful weapon to boot! -The Chaplain can bless any container with water by hitting it with their bible. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them. -The Chaplain's holy weapon will kill clockwork marauders in two hits. -As the Chaplain, your bible is also a container that can store small items. Depending on your god, your starting bible may come with a surprise! -As the Chaplain, you are much more likely to get a response by praying to the gods than most people. To boost your chances, make altars with colorful crayon runes, lit candles, and wire art. +As the Clown, you can use your stamp on a sheet of flattened cardboard as the first step of making a honkbot. Fun for the whole crew! +As the Clown, your number one way to win over the crew's favor is by telling jokes and putting forth effort into being comedic. Everyone loves a good clown, but everyone despises a bad one. +As the Chaplain, your null rod has a lot of functions: while being an incredibly powerful weapon with an array of potential utilities depending upon the skin you chose for it, it also nulls cultist and wizard magic entirely, making you immune to them both and in some cases even harming the caster so long as you keep it in a pocket or in your hands. +As the Chaplain, you can bless any water container by hitting it with your bible to turn it into holy water. Holy water has a myriad of uses against both cults and large amounts of it are a great contributor to success against them. +As the Chaplain, your null rod will kill clockwork marauders in two hits while actively hindering their overall combat capabilities just by being nearby to them. +As the Chaplain, your bible is also a container that can store a singular small item. Depending on your God, your starting bible may come with a surprise! +As the Chaplain, you are much more likely to get a response by praying to the Gods than most people as your prayers will send a special noise cue directly to them! To further your chances of getting a response even further, pretty up your altar with crayon runes and wire art, and be sure to put a decent amount of effort into your prayers themselves. The Gods don't like lazy bums. As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo. As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations. -As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist. -As a Botanist, you can combine production trait chemicals just like a Chemist. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen! +As a Botanist, you should look into increasing the potency of your plants. This is shown by the size of the plant's sprite, and can increase the amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist. +As a Botanist, you can combine production trait chemicals and mix your own complex chemicals inside of the plants themselves using precursors. Chlorine (blumpkin) + radium and phosphorus (glowshrooms) equals unstable mutagen! +As a Botanist, earthsblood is an incredibly powerful chemical found in Ambrosia Gaia, it heals all types of damages very rapidly but causes lingering brain damage and has a nasty overdose. You can combine the chemicals from watermelons (water), grass (hydrogen), and cherries (sugar) to mix mannitol in with your earthsblood to completely counteract its main drawback! +As a Botanist, Ambrosia Gaia is a plant mutated from Ambrosia Deus, which is a plant mutated from Ambrosia Vulgaris. The reagent contained within this plant known as earthsblood can make your trays and soil plots completely self sufficient when a plant containing such reagent is composted into them, meaning they won't need nutrients or water, and they'll automatically kill their own weeds and pests. As a Cook, you can load your food into snack vending machines. As a Cook, you can rename your custom made food with a pen. As a Cook, any food you make will be much healthier than the junk food found in vendors. Having the crew routinely eating from you will provide minor buffs. -As a Cook, being in the kitchen will make you remember the basics of Close Quarters Cooking. It is highly effective at removing Assistants from your workplace. +As a Cook, being in the kitchen will make you remember the basics of Close Quarters Cooking (CQC). It is highly effective at removing Assistants from your workplace. As a Cook, your Kitchenmate can vend out trays that fit on your belt slot. These trays pick up 7 food items at a time and are a quick way to transport large meals. As a Cook, the advanced roasting stick is used to cook food at a distance, and can be used on SME, singularity, and other objects that cook food normally. +As a Cook, the deep frier is a tool which can turn very large quantities of seemingly useless objects into food, albeit nutritionally poor and awful tasting food, but hey, food is food. As the Bartender, the drinks you start with only give you the basics. If you want more advanced mixtures, look into working with chemistry, hydroponics, or even mining for things to grind up and throw in! -As the Bartender, you can use a circular saw on your shotgun to make it easier to store. -As a Janitor, if someone steals your janicart, you can instead use your space cleaner spray, grenades, water sprayer, exact bloody revenge or order another from Cargo. +As the Bartender, you can use a circular saw on your shotgun to make it easier to store. Make sure to empty them prior lest you blast yourself in the face! +As a Janitor, if someone steals your janicart, you can instead use your spray bottles, soap, and arsenal of slippery objects to exact your bloody revenge.. ..or just order another one from Cargo. As a Janitor, the trash bag can be used to hold more than trash. Tools, medical equipment, smuggled nuclear disks... You name it! -As a Janitor, mousetraps can be used to create bombs or booby-trap containers. -Beware the Curator, for they are not completely defenseless. The curator's whip always disarms people, their laser pointer can blind humans and cyborgs, and can hide items in wirecut books. +As a Janitor, mousetraps can be used as bomb triggers to booby-trap containers. +As the Curator, for what it's worth, your toys and position are fairly robust. You can order a claymore, a whip, or a free space suit all at roundstart. The claymore is fairly underwhelming, however the whip is an incredibly robust weapon capable of always disarming, and that space suit is also better than the ones in EVA. As the Curator, be sure to keep the shelves stocked and the library clean for crew. As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them! As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it. As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more! As a Cargo Technician, you get 400 points per packet! Stamp the manifest and sending back the crate will give you 200 points for the paperwork and 200 points for the crate! -As a Cargo Technician, paperwork is an alternative option to shipping off plasma sheets and other goods. Order Paperwork crates and go into the crafting menu to turn pens and undone paper work into completed grant paper work to get 50 points per sheet! +As a Cargo Technician, paperwork and glass blowing are alternative options to shipping off plasma sheets and other goods. Order their respective kits and get to work! Paperwork can be done quickly via the crafting menu for a quick buck, while glass blowing is much more lucrative, but may require some more effort and time. As the Quartermaster, be sure to check the manifests on crates you receive to make sure all the info is correct. If there's a mistake, stamp the manifest DENIED and send it back in a crate with the items untouched for a refund! As the Quartermaster, you can construct an express supply console that instantly delivers crates by drop pod. The impact will cause a small explosion as well. As a Shaft Miner, the northern side of Lavaland has a lot more rare minerals than on the south. @@ -177,25 +187,25 @@ As a Shaft Miner, every monster on Lavaland has a pattern you can exploit to min As a Shaft Miner, you can harvest goliath plates from goliaths and upgrade your explorer's suit, mining hardsuits as well as Firefighter APLUs with them, greatly reducing incoming melee damage. As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die. As a Shaft Miner, you can craft a variety of equipment from the local fauna. Bone axes, lava boats and ash drake armour are just a few of them! -As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment! +As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you may just be able to create some difficult to obtain substances, or contact your employers to request special objectives! Experiment! As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you! As a Traitor, the Captain and the Head of Security are two of the most difficult to kill targets on the station. If either one is your target, plan carefully. -As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool. +As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool if you manage to nab an autolathe for yourself. As a Traitor, you may sometimes be assigned to hunt other traitors, and in turn be hunted by others. As a Traitor, the syndicate encryption key is very useful for coordinating plans with your fellow traitors -- or, of course, betraying them. As a Traitor, plasma can be injected into many things to sabotage them. Power cells, light bulbs, cigars and e-cigs will all explode when used. As a Nuclear Operative, communication is key! Use :t or :h to speak to your fellow operatives and coordinate an attack plan. -As a Nuclear Operative, you should look into purchasing a syndicate cyborg, as they can provide heavy fire support, full access, are immune to conventional stuns, and can easily take down the AI. +As a Nuclear Operative, you should look into purchasing one of the three Syndicate cyborgs in your uplink, as they can provide useful tactical support, function as walking access machines, are immune to conventional stuns, and can easily take down the AI. As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire. As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life. As a Monkey, you can crawl through air or scrubber vents by alt+left clicking them. You must drop everything you are wearing and holding to do this, however. As a Monkey, you can still wear a few human items, such as backpacks, gas masks and hats, and still have two free hands. As the Malfunctioning AI, you can shunt to an APC if the situation gets bad. This disables your doomsday device if it is active. -As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them. +As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them. Do note that this will prevent you from hacking any cyborg made in the future. As the Malfunctioning AI, look into flooding the station with plasma fires to kill off large portions of the crew, letting you pick off the remaining few with space suits who escaped. -Xenomorphs? Science can craft deadly tech shells like pulse slugs and laser scatter shot that are highly effective against any alien threat. -When fighting aliens, it can be a good idea to turn off the gravity due to the alien's lack of zero-gravity control. -When fighting xenomorph aliens, consider a shield. Shields can block their pounces and be worn on the back, but beware of neurotoxin. +Xenomorphs? Any source of burn damage severely harms them. Science can craft deadly tech shells like pulse slugs and laser scatter shot that are highly effective against any alien threat. +When fighting Aliens, it can be a good idea to turn off the gravity due to the every caste of alien's lack of zero-gravity control, especially Hunters and Drones, which are completely and utterly helpless. +When fighting Aliens, consider a shield. A raised shield can halt their attempts to slash at you, and their disarms will always remove an item in your hand before knocking you over, always having something in your hand, no matter how small or worthless, can save your life. As an Alien, your melee prowess is unmatched, but your ranged abilities are sorely lacking. Make use of corners to force a melee confrontation! As an Alien, you take double damage from all burn attacks, such as lasers, welding tools, and fires. Furthermore, fire can destroy your resin and eggs. Expose areas to space to starve away any flamethrower fires before they can do damage! As an Alien, resin floors not only regenerate your plasma supply, but also passively heal you. Fight on resin floors to gain a home turf advantage! @@ -212,6 +222,7 @@ As the Blob, you can produce a Blobbernaut from a factory for 40 resources. Blob As the Blob, you can expand by clicking, create strong blobs with ctrl-click, rally spores with middle-click, and remove blobs with alt-click. You do not need to have your camera over the tile to do this. As the Blob, removing strong blobs, resource nodes, factories, and nodes will give you 4, 15, 25, and 25 resources back, respectively. As the Blob, talking will send a message to all other overminds and all Blobbernauts, allowing you to direct attacks and coordinate. +As the Blob, always make sure where you land is where you want to be, as it is very unlikely you will be getting too far away from it. Land in key points like the armory or the medical bay to immediately cripple the crew before they even find out you exist. Of course, always take into mind if being immediately discovered may outweight the benefits, and stick to maintenance close to these key points of interest to you. As a Blobbernaut, you can communicate with overminds and other Blobbernauts via :b. As a Blobbernaut, your HUD shows your health and the core health of the overmind that created you. As a Revolutionary, you cannot convert a head of staff or someone who has a mindshield implant, such as a security officer or those they implant. Implants can however be surgically removed, and do not carry over with cloning. Take control of medbay to keep control of conversions! @@ -220,6 +231,10 @@ As a Revolutionary, cargo can be your best friend or your worst nightmare. In th As a Revolutionary, your main power comes from how quickly you spread. Convert people as fast as you can and overwhelm the heads of staff before security can arm up. As a Changeling, the Extract DNA sting counts for your genome absorb objective, but does not let you respec your powers. As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to rechoose your powers, the DNA of whoever you absorbed, the memory of the absorbed, and some samples of things the absorbed said. +As a Changeling, absorbing someone will give you their full memory. This can include things such as a Traitor's uplink, thus absorbing one will allow you to access the Traitor uplink and buy toys for your Changeling self to abuse. +As a Changeling, absorbing another Changeling will permanently boost your chemical reserve, allow you to pick more abilities, and make the victim unable to revive. Be careful when exposing your identity to other Changelings, as they may be out of those wonderful benefits. +As a Changeling, BZ gas will dramatically slow down or even halt your natural chemical regeneration, be sure to avoid it at all costs as some lunatics may try and flood portions of the station to deal with you. +As a Changeling, death is not the end for you! You can revive after two minutes from being dead by triggering your stasis ability, and then waiting for the prompt to resurrect yourself to show up. As a Cultist, do not cause too much chaos before your objective is completed. If the shuttle gets called too soon, you may not have enough time to win. As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face. As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists, stamina damage to Ratvarian scum, and some damage to fellow cultists of Nar-Sie nearby, but will create a fire where the rune stands on use. @@ -239,9 +254,10 @@ You can deconvert Cultists of Nar-Sie and Servants of Ratvar by feeding them lar Tiles sprayed with holy water will permanently block Servants of Ratvar from teleporting onto them. As a Wizard, you can turn people to stone, then animate the resulting statue with a staff of animation to create an extremely powerful minion, for all of 5 minutes at least. As a Wizard, the fireball spell performs very poorly at close range, as it can easily catch you in the blast. It is best used as a form of artillery down long hallways. -As a Wizard, summoning guns will turn a large portion of the crew against themselves, but will also give everyone anything from a pea shooter to a BFG 9000. Use at your own risk! +As a Wizard, summoning guns will turn a large portion of the crew against themselves, but will also give everyone anything from a energy pistol to a pulse rifle. Use at your own risk! As a Wizard, the staff of chaos can fire any type of bolts from the magical wands. This can range from bolts of instant death to healing or reviving someone. As a Wizard, most spells become unusable if you are not wearing your robe, hat, and sandals. +As a Wizard, it's advisable that you don't dump all of your limited spell points into solely offensive spells, if you can't defend yourself then you're sure to get dunked. As an Abductor, you can select where your victims will be sent on the ship control console. As an Abductor Agent, the combat mode vest has much higher resistance to every kind of weapon, and your helmet prevents the AI from tracking you. As an Abductor, the baton can cycle between four modes: stun, sleep, cuff and probe. @@ -260,6 +276,7 @@ As a Drone, you can ping other drones to alert them of areas in the station in n As a Drone, you can repair yourself by using a screwdriver on yourself and standing still! As a Ghost, you can see the inside of a container on the ground by clicking on it. As a Ghost, you can double click on just about anything to follow it. Or just warp around! +As a Ghost, there's a button in the OOC tab labeled Observe, it lets you see through someone's eyes as if you were the one who's playing them. As a Devil, you gain power for every three souls you control, however you also become more obvious. As a Devil, as long as you control at least one other soul, you will automatically resurrect, as long as a banishment ritual is not performed. At which time a Devil's nameth is spake on the tongue of man, the Devil may appeareth. @@ -268,9 +285,10 @@ When hacking doors, cutting and mending the "test light wire" will restore power When hacking, remote singulars pulse when attached to a wire and pinged. This can allow you to hack things or set traps from far away. When crafting most items, you can either manually combine parts or use the crafting menu. Suit storage units not only remove blood and dirt from clothing, but also radiation! +Suit storage units entirely purge radiation from any carbon mob put inside of them when cycling, at the cost of some horrific burns, this is a very effective strategy to clean someone up after they bathed in the engine. Remote devices will work when used through cameras. For example: Bluespace RPEDs and door remotes. Laser pointers can be upgraded by replacing its micro laser with a better one from RnD! Use a screwdriver on it to remove the old laser. Upgrading the laser pointer gives you better odds of stunning a cyborg, and even blinding people with sunglasses. -Being out of combat mode makes makes you deal less damage to people and objects when attacking. -Resting makes you deal less damage to people and objects when attacking. +Being out of combat mode makes makes you deal less damage to people and objects when attacking. This stacks with the penalty incurred by resting. +Resting makes you deal less damage to people and objects when attacking. This stacks with the penalty incurred by being out of combat mode. You do not regenerate as much stamina while in combat mode. Resting (being on the ground) makes you regenerate stamina faster. Remember to be in combat mode while in combat, as otherwise you will be penalized by taking more incoming damage and dealing less damage to your adversary. diff --git a/tgstation.dme b/tgstation.dme index 9306ae0ef1..61a7eec79e 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -558,6 +558,7 @@ #include "code\datums\elements\_element.dm" #include "code\datums\elements\art.dm" #include "code\datums\elements\beauty.dm" +#include "code\datums\elements\bsa_blocker.dm" #include "code\datums\elements\cleaning.dm" #include "code\datums\elements\decal.dm" #include "code\datums\elements\dusts_on_catatonia.dm" @@ -727,6 +728,7 @@ #include "code\game\gamemodes\dynamic\dynamic_rulesets_midround.dm" #include "code\game\gamemodes\dynamic\dynamic_rulesets_roundstart.dm" #include "code\game\gamemodes\dynamic\dynamic_storytellers.dm" +#include "code\game\gamemodes\eldritch_cult\eldritch_cult.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\gangs\dominator.dm" #include "code\game\gamemodes\gangs\dominator_countdown.dm" @@ -1296,6 +1298,7 @@ #include "code\game\turfs\open.dm" #include "code\game\turfs\turf.dm" #include "code\game\turfs\openspace\openspace.dm" +#include "code\game\turfs\openspace\transparent.dm" #include "code\game\turfs\simulated\chasm.dm" #include "code\game\turfs\simulated\dirtystation.dm" #include "code\game\turfs\simulated\floor.dm" @@ -1598,6 +1601,16 @@ #include "code\modules\antagonists\disease\disease_disease.dm" #include "code\modules\antagonists\disease\disease_event.dm" #include "code\modules\antagonists\disease\disease_mob.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_antag.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_book.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_effects.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_items.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_knowledge.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_magic.dm" +#include "code\modules\antagonists\eldritch_cult\eldritch_monster_antag.dm" +#include "code\modules\antagonists\eldritch_cult\knowledge\ash_lore.dm" +#include "code\modules\antagonists\eldritch_cult\knowledge\flesh_lore.dm" +#include "code\modules\antagonists\eldritch_cult\knowledge\rust_lore.dm" #include "code\modules\antagonists\ert\ert.dm" #include "code\modules\antagonists\fugitive\fugitive.dm" #include "code\modules\antagonists\fugitive\fugitive_outfits.dm" @@ -2289,6 +2302,11 @@ #include "code\modules\lighting\lighting_setup.dm" #include "code\modules\lighting\lighting_source.dm" #include "code\modules\lighting\lighting_turf.dm" +#include "code\modules\mafia\_defines.dm" +#include "code\modules\mafia\controller.dm" +#include "code\modules\mafia\map_pieces.dm" +#include "code\modules\mafia\outfits.dm" +#include "code\modules\mafia\roles.dm" #include "code\modules\mapping\map_config.dm" #include "code\modules\mapping\map_orientation_pattern.dm" #include "code\modules\mapping\map_template.dm" @@ -2584,6 +2602,7 @@ #include "code\modules\mob\living\simple_animal\constructs.dm" #include "code\modules\mob\living\simple_animal\corpse.dm" #include "code\modules\mob\living\simple_animal\damage_procs.dm" +#include "code\modules\mob\living\simple_animal\eldritch_demons.dm" #include "code\modules\mob\living\simple_animal\parrot.dm" #include "code\modules\mob\living\simple_animal\pickle.dm" #include "code\modules\mob\living\simple_animal\shade.dm" @@ -2617,6 +2636,7 @@ #include "code\modules\mob\living\simple_animal\friendly\penguin.dm" #include "code\modules\mob\living\simple_animal\friendly\pet.dm" #include "code\modules\mob\living\simple_animal\friendly\plushie.dm" +#include "code\modules\mob\living\simple_animal\friendly\possum.dm" #include "code\modules\mob\living\simple_animal\friendly\sloth.dm" #include "code\modules\mob\living\simple_animal\friendly\snake.dm" #include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm" @@ -3257,9 +3277,9 @@ #include "code\modules\spells\spell.dm" #include "code\modules\spells\spell_types\aimed.dm" #include "code\modules\spells\spell_types\area_teleport.dm" -#include "code\modules\spells\spell_types\barnyard.dm" #include "code\modules\spells\spell_types\bloodcrawl.dm" #include "code\modules\spells\spell_types\charge.dm" +#include "code\modules\spells\spell_types\cone_spells.dm" #include "code\modules\spells\spell_types\conjure.dm" #include "code\modules\spells\spell_types\construct_spells.dm" #include "code\modules\spells\spell_types\curse.dm" @@ -3278,7 +3298,6 @@ #include "code\modules\spells\spell_types\lichdom.dm" #include "code\modules\spells\spell_types\lightning.dm" #include "code\modules\spells\spell_types\mime.dm" -#include "code\modules\spells\spell_types\mind_transfer.dm" #include "code\modules\spells\spell_types\projectile.dm" #include "code\modules\spells\spell_types\rightandwrong.dm" #include "code\modules\spells\spell_types\rod_form.dm" @@ -3295,6 +3314,10 @@ #include "code\modules\spells\spell_types\turf_teleport.dm" #include "code\modules\spells\spell_types\voice_of_god.dm" #include "code\modules\spells\spell_types\wizard.dm" +#include "code\modules\spells\spell_types\pointed\barnyard.dm" +#include "code\modules\spells\spell_types\pointed\blind.dm" +#include "code\modules\spells\spell_types\pointed\mind_transfer.dm" +#include "code\modules\spells\spell_types\pointed\pointed.dm" #include "code\modules\station_goals\bsa.dm" #include "code\modules\station_goals\dna_vault.dm" #include "code\modules\station_goals\shield.dm" diff --git a/tgui/packages/tgui/assets/bg-neutral.svg b/tgui/packages/tgui/assets/bg-neutral.svg new file mode 100644 index 0000000000..1c397616e8 --- /dev/null +++ b/tgui/packages/tgui/assets/bg-neutral.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index e4ec6d285d..69092ce00f 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -22,6 +22,7 @@ import './styles/themes/abductor.scss'; import './styles/themes/cardtable.scss'; import './styles/themes/hackerman.scss'; import './styles/themes/malfunction.scss'; +import './styles/themes/neutral.scss'; import './styles/themes/ntos.scss'; import './styles/themes/paper.scss'; import './styles/themes/retro.scss'; diff --git a/tgui/packages/tgui/interfaces/MafiaPanel.js b/tgui/packages/tgui/interfaces/MafiaPanel.js index e3c2c893ec..3616781fc5 100644 --- a/tgui/packages/tgui/interfaces/MafiaPanel.js +++ b/tgui/packages/tgui/interfaces/MafiaPanel.js @@ -1,11 +1,14 @@ +import { classes } from 'common/react'; import { Fragment } from 'inferno'; +import { multiline } from 'common/string'; import { useBackend } from '../backend'; -import { Box, Button, Flex, LabeledList, Section, TimeDisplay } from '../components'; +import { Box, Button, Collapsible, Flex, NoticeBox, Section, TimeDisplay, Tooltip } from '../components'; import { Window } from '../layouts'; export const MafiaPanel = (props, context) => { const { act, data } = useBackend(context); const { + lobbydata, players, actions, phase, @@ -16,29 +19,124 @@ export const MafiaPanel = (props, context) => { timeleft, all_roles, } = data; + const playerAddedHeight = roleinfo ? players.length * 30 : 7; + const readyGhosts = lobbydata ? lobbydata.filter( + player => player.status === "Ready") : null; return ( - -
- {!!roleinfo && ( - - - + width={650} // 414 or 415 / 444 or 445 + height={293 + playerAddedHeight}> + + {!roleinfo && ( + +
+ }> + + + The lobby currently has {readyGhosts.length} + /12 valid players signed up. + + + {!!lobbydata && lobbydata.map(lobbyist => ( + + + + {lobbyist.name} + + + STATUS: + + +
+ + {lobbyist.status} {lobbyist.spectating} + +
+
+
+
+ ))} +
+
+
+ )} + {!!roleinfo && ( +
- You are a {roleinfo.role} + {!!admin_controls && ( +
+ + + + + + +
+ )} {!!actions && actions.map(action => ( @@ -49,116 +147,323 @@ export const MafiaPanel = (props, context) => { ))} - {!!admin_controls && ( + {!!roleinfo && (
- THESE ARE DEBUG, THEY WILL BREAK THE GAME, DO NOT TOUCH
- Also because an admin did it: do not gib/delete/etc - anyone! It will runtime the game to death!
- - - -
- -
- )} -
- - {!!players && players.map(player => ( - - {!player.alive && (DEAD)} - {player.votes !== undefined && !!player.alive - && (Votes : {player.votes} )} - { - !!player.actions && player.actions.map(action => { - return ( - ); }) - } - ) - )} - -
- {!!judgement_phase && ( -
+ title="Judgement" + buttons={ + - Use these buttons to vote the accused innocent or guilty! + disabled={!judgement_phase} + onClick={() => act("vote_innocent")} /> + {!judgement_phase && ( + + There is nobody on trial at the moment. + + )} + {!!judgement_phase && ( + + It is now time to vote, vote the accused innocent or guilty! + + )} + disabled={!judgement_phase} + onClick={() => act("vote_guilty")} /> + + +
)} - - -
- {!!all_roles && all_roles.map(r => ( - - - {r} - ); }) + } + + + ) + )} + +
+
+ + +
+
-
- -
- {roleinfo !== undefined && !!roleinfo.action_log - && roleinfo.action_log.map(log_line => ( - - {log_line} - - ))} -
+ + {!!roleinfo && ( + +
+ {roleinfo !== undefined && !!roleinfo.action_log + && roleinfo.action_log.map(log_line => ( + + {log_line} + + ))} +
+
+ )} +
+ + + )} + + + {!!admin_controls && ( +
+ +
+ )}
); }; + +const LobbyDisplay = (props, context) => { + const { act, data } = useBackend(context); + const { + phase, + timeleft, + admin_controls, + } = data; + return ( + + [Phase = {phase} | ]{' '} +