diff --git a/.github/assets/60-percent-of-the-time-works-every-time.svg b/.github/assets/60-percent-of-the-time-works-every-time.svg new file mode 100644 index 00000000000..6a81f036817 --- /dev/null +++ b/.github/assets/60-percent-of-the-time-works-every-time.svg @@ -0,0 +1,37 @@ + + 60-percent-of-the-time-works-every-time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/assets/fuck-it-ship-it.svg b/.github/assets/fuck-it-ship-it.svg new file mode 100644 index 00000000000..97baa31cd3f --- /dev/null +++ b/.github/assets/fuck-it-ship-it.svg @@ -0,0 +1,23 @@ + + fuck-it-ship-it + + + + + + + + + + + + + + + + + + + + + diff --git a/.github/assets/made-in-byond.gif b/.github/assets/made-in-byond.gif new file mode 100644 index 00000000000..aed1b7ca243 Binary files /dev/null and b/.github/assets/made-in-byond.gif differ diff --git a/.github/assets/ss1984.gif b/.github/assets/ss1984.gif new file mode 100644 index 00000000000..dd798025214 Binary files /dev/null and b/.github/assets/ss1984.gif differ diff --git a/README.md b/README.md index e8d0c9e5fea..94972af00c4 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,79 @@ -# Paradise -[![CI](https://github.com/ss220-space/Paradise/workflows/CI/badge.svg)](https://github.com/ParadiseSS13/Paradise/actions?query=workflow%3ACI) -[![Render Nanomaps](https://github.com/ss220-space/Paradise/workflows/Render%20Nanomaps/badge.svg)](https://github.com/ParadiseSS13/Paradise/actions?query=workflow%3A%22Render+Nanomaps%22) -[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/ss220-space/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Average time to resolve an issue") -[![Percentage of issues still open](http://isitmaintained.com/badge/open/ss220-space/paradise.svg)](http://isitmaintained.com/project/paradisess13/paradise "Percentage of issues still open") +#

Буквально SS1984

+Буквально SS1984 -[![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) -[![forthebadge](http://forthebadge.com/images/badges/contains-technical-debt.svg)](http://forthebadge.com) -[![forthebadge](http://forthebadge.com/images/badges/fuck-it-ship-it.svg)](http://forthebadge.com) +

+ CI + Render Nanomaps +

-# Ссылки -- [Website](https://ss220.space/) +

+ 60% Works + Made in BYOND + Fuck it, Ship it +

+ +# Ссылки SS1984 + +- [Вебсайт](https://ss220.space/) - [Discord](https://discord.ss220.space) -# Original useful Links +# Апстрим проект Paradise -- [Website](https://www.paradisestation.org/) -- [Discord](https://discordapp.com/invite/YJDsXFE) -- [Documentation](https://codedocs.paradisestation.org) +- [Github](https://github.com/ParadiseSS13/Paradise) +- [Вебсайт](https://www.paradisestation.org/) +- [Документация](https://devdocs.paradisestation.org) -# Useful Documents +# Полезная документация (Устарело) - [Installation Guide](.github/DOWNLOADING.md) - [Contribution Guide](.github/CONTRIBUTING.md) - [Autodocumentation Guide](.github/AUTODOC_GUIDE.md) ---- +> [!TIP] +> Больше всего информации находиться у нас в Discord сервере в закрытых каналах
+> Для получения доступа, обратитесь к каналу [#информация](https://discord.com/channels/617003227182792704/628271712097665025). Там указаны какие есть роли, что они дают и как их получить.
+> Альтернативно, вы можете прочитать документарию апстрим Paradise [Getting Started Guide](https://devdocs.paradisestation.org/contributing/getting_started/) +--- ### LICENSE -Paradise is licensed under the GNU Affero General Public License version 3. -As of 5th January 2015 any new contributions are licensed under the AGPL as well, -if you wish to submit code under the GPL v3 then commits and files must be marked as such -in comments. If you wish to use our code in a closed source manner you may use anything -before commit 445615b8439bf606ff204a42c8e7b6b69d983255, -which is licensed under GPL v3. -The major change here is that if you host a server using any code licensed under AGPL you -are required to provide full source code for your servers users as well, -including addons and modifications you have made. - -See [this](https://www.gnu.org/licenses/why-affero-gpl.html) for more information. - -Any files located in the -`Paradise/goon`, -`Paradise/icons/goonstation`, or -`Paradise/sound/goonstation` -directories, or any subdirectories of mentioned directories are licensed under the -Creative Commons 3.0 BY-NC-SA license -(https://creativecommons.org/licenses/by-nc-sa/3.0) - -All other assets including icons and sound files are licensed under the -Creative Commons 3.0 BY-SA license (https://creativecommons.org/licenses/by-sa/3.0/), -unless otherwise indicated. +> [!CAUTION] +> If you wish to use our code in a closed source manner (i.e. not make it available to the public and/or those who connect to services you offer using this code) you must **only** use code prior to commit [1af3ddef2af85937251e24384c2173c4b6c3222b on 2015/01/05 22:04 GMT](https://github.com/ParadiseSS13/Paradise/commit/1af3ddef2af85937251e24384c2173c4b6c3222b), which is licenced under GPLv3. + +### Click each banner for further information + +--- + +
+AGPLv3 license + +>All code after and including commit [1af3ddef2af85937251e24384c2173c4b6c3222b on 2015/01/05 22:04 GMT](https://github.com/ParadiseSS13/Paradise/commit/1af3ddef2af85937251e24384c2173c4b6c3222b) is licensed under the [GNU Affero General Public License version 3](https://www.gnu.org/licenses/agpl-3.0.en.html) unless otherwise specified within the folder or file. +
+ +
+GPLv3 license + +>All code prior to commit [1af3ddef2af85937251e24384c2173c4b6c3222b on 2015/01/05 22:04 GMT](https://github.com/ParadiseSS13/Paradise/commit/1af3ddef2af85937251e24384c2173c4b6c3222b) is licensed under the [GPL General Public License version 3](https://www.gnu.org/licenses/gpl-3.0.en.html) +
+ +
+MIT license + +>Some files are licenced under the [MIT license](https://opensource.org/license/MIT), these files will clearly specify this licence at the head of each file. +
+ +
+Creative Commons 3.0 BY-NC-SA + +>Any files with the ancestor directories [`Paradise/icons/goonstation`](icons/goonstation) or [`Paradise/sound/goonstation`](sound/goonstation) are licensed under the [Creative Commons 3.0 BY-NC-SA license](https://creativecommons.org/licenses/by-nc-sa/3.0). +> +>Further files or folders may also fall under this licence, and any such instances will be specified within the folder or file. +
+ +
+Creative Commons 3.0 BY-SA + +>All other non-code assets, including icons and sound files, are licensed under the [Creative Commons 3.0 BY-SA license](https://creativecommons.org/licenses/by-sa/3.0/), unless otherwise specified within the folder or file. +
diff --git a/_build_dependencies.sh b/_build_dependencies.sh index a1292ad3e18..194d7f5c50b 100644 --- a/_build_dependencies.sh +++ b/_build_dependencies.sh @@ -8,9 +8,9 @@ export STABLE_BYOND_MAJOR=515 # Stable Byond Minor export STABLE_BYOND_MINOR=1642 # Beta Byond Major -export BETA_BYOND_MAJOR=515 +export BETA_BYOND_MAJOR=516 # Beta Byond Minor -export BETA_BYOND_MINOR=1642 +export BETA_BYOND_MINOR=1648 # For the RUSTG library. Not actually installed by CI but kept as a reference export RUSTG_VERSION=3.3.0-ss220 #For DMJIT librarry diff --git a/_maps/map_files/Delta/delta.dmm b/_maps/map_files/Delta/delta.dmm index c7eb23c4b24..9e076f791e4 100644 --- a/_maps/map_files/Delta/delta.dmm +++ b/_maps/map_files/Delta/delta.dmm @@ -584,6 +584,23 @@ icon_state = "darkbluecorners" }, /area/bridge) +"agb" = ( +/obj/item/ai_module/protect_station{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ai_module/nanotrasen{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/structure/table/glass, +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "agp" = ( /turf/simulated/wall/r_wall, /area/hallway/secondary/entry/westarrival) @@ -11499,6 +11516,21 @@ icon_state = "dark" }, /area/bridge) +"byX" = ( +/obj/machinery/vending/wallmed{ + name = "Emergency NanoMed"; + pixel_x = 26 + }, +/obj/structure/filingcabinet/chestdrawer, +/obj/item/radio/intercom{ + pixel_x = 0; + pixel_y = -26 + }, +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "whitepurple" + }, +/area/toxins/explab) "bzb" = ( /obj/machinery/door/airlock/medical{ autoclose = 0; @@ -13785,6 +13817,15 @@ }, /turf/simulated/floor/plasteel, /area/crew_quarters/locker/locker_toilet) +"bJo" = ( +/obj/machinery/computer/aiupload/cyborg, +/obj/item/radio/intercom/private{ + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "bJp" = ( /obj/machinery/computer/mech_bay_power_console, /turf/simulated/floor/plasteel{ @@ -14827,16 +14868,6 @@ icon_state = "dark" }, /area/crew_quarters/bar) -"bOG" = ( -/obj/item/aiModule/reset, -/obj/structure/table/glass, -/obj/machinery/light{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "bOI" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -16453,33 +16484,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/trading) -"bVZ" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "bWb" = ( /obj/structure/lattice/catwalk, /obj/structure/cable{ @@ -17268,21 +17272,6 @@ }, /turf/simulated/floor/plasteel, /area/maintenance/bar) -"bZY" = ( -/obj/machinery/light/small{ - dir = 8 - }, -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 4 - }, -/obj/machinery/alarm{ - dir = 4; - pixel_x = -22 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "cac" = ( /obj/structure/window/reinforced{ dir = 1; @@ -19059,68 +19048,11 @@ icon_state = "blue" }, /area/hydroponics) -"ciR" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, -/obj/structure/table/glass, -/obj/machinery/door/window{ - base_state = "right"; - dir = 2; - icon_state = "right"; - name = "Core Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "ciS" = ( /turf/simulated/floor/wood/fancy/light{ color = "orange" }, /area/crew_quarters/courtroom) -"ciT" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SMG" - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 2; - pixel_y = -2 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 4; - pixel_y = -4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 6; - pixel_y = -6 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "ciZ" = ( /obj/machinery/firealarm{ pixel_y = 26 @@ -19837,23 +19769,6 @@ icon_state = "dark" }, /area/chapel/office) -"cmL" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/structure/cable{ - icon_state = "2-8" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "cmP" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ @@ -21502,9 +21417,6 @@ icon_state = "grimy" }, /area/crew_quarters/cabin1) -"cwa" = ( -/turf/simulated/wall, -/area/civilian/barber) "cwd" = ( /turf/simulated/floor/plasteel{ dir = 4; @@ -22601,10 +22513,6 @@ "cAr" = ( /turf/simulated/wall/rust, /area/maintenance/asmaint2) -"cAt" = ( -/obj/machinery/portable_atmospherics/canister/nitrogen, -/turf/simulated/floor/plating, -/area/maintenance/trading) "cAv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -22620,20 +22528,6 @@ icon_state = "arrival" }, /area/hallway/secondary/entry) -"cAy" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "cAz" = ( /turf/simulated/wall, /area/maintenance/trading) @@ -24368,10 +24262,6 @@ icon_state = "whiteblue" }, /area/medical/reception) -"cHP" = ( -/obj/effect/decal/cleanable/dirt, -/turf/simulated/wall, -/area/civilian/barber) "cHT" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 @@ -27655,23 +27545,6 @@ icon_state = "whitepurple" }, /area/medical/research/shallway) -"cWa" = ( -/obj/item/aiModule/protectStation{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/item/aiModule/nanotrasen{ - pixel_x = 2; - pixel_y = -2 - }, -/obj/structure/table/glass, -/obj/machinery/light{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "cWb" = ( /obj/effect/decal/cleanable/dirt, /obj/item/chair/wood/wings, @@ -32997,14 +32870,6 @@ }, /turf/simulated/floor/plating, /area/atmos) -"dtj" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "vault" - }, -/area/security/securearmory) "dtm" = ( /obj/structure/table/glass, /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass, @@ -33269,21 +33134,6 @@ /obj/effect/decal/warning_stripes/southwest, /turf/simulated/floor/plasteel, /area/engineering/controlroom) -"duH" = ( -/obj/machinery/vending/wallmed{ - name = "Emergency NanoMed"; - pixel_x = 26 - }, -/obj/structure/filingcabinet/chestdrawer, -/obj/item/radio/intercom{ - pixel_y = -26; - pixel_x = 0 - }, -/turf/simulated/floor/plasteel{ - dir = 6; - icon_state = "whitepurple" - }, -/area/toxins/explab) "duI" = ( /obj/machinery/atmospherics/pipe/simple/heat_exchanging{ dir = 9 @@ -34856,19 +34706,6 @@ icon_state = "grimy" }, /area/library) -"dBN" = ( -/obj/structure/table/reinforced, -/obj/structure/mirror{ - pixel_x = 28 - }, -/obj/item/razor, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "dBO" = ( /obj/effect/spawner/window/reinforced/polarized{ id = "vir2" @@ -35554,10 +35391,6 @@ icon_state = "grimy" }, /area/bridge/meeting_room) -"dFe" = ( -/obj/effect/spawner/window/reinforced, -/turf/simulated/floor/plating, -/area/civilian/barber) "dFg" = ( /turf/simulated/wall, /area/maintenance/library) @@ -38427,6 +38260,34 @@ icon_state = "dark" }, /area/atmos) +"dRD" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/light{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SP-91-RC"; + req_access = list(1) + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 8 + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 4 + }, +/obj/item/ammo_box/magazine/sp91rc, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = -4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "dRE" = ( /obj/machinery/light/small{ dir = 4 @@ -40461,22 +40322,6 @@ icon_state = "darkred" }, /area/maintenance/brig) -"dZt" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/box/teargas, -/obj/item/storage/box/teargas{ - pixel_x = 3; - pixel_y = -3 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "dZu" = ( /obj/structure/window/reinforced{ dir = 4 @@ -42362,6 +42207,16 @@ }, /turf/simulated/floor/plating, /area/crew_quarters/hor) +"ett" = ( +/obj/item/ai_module/reset, +/obj/structure/table/glass, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "etF" = ( /obj/effect/decal/cleanable/blood, /obj/machinery/atmospherics/pipe/simple/visible/universal{ @@ -42847,6 +42702,13 @@ icon_state = "redcorner" }, /area/security/main) +"eAF" = ( +/obj/item/ai_module/quarantine, +/obj/structure/table/glass, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "eAH" = ( /obj/effect/decal/warning_stripes/south, /obj/structure/cable{ @@ -43780,6 +43642,19 @@ }, /turf/simulated/floor/plasteel, /area/atmos) +"eMa" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/grille/broken, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "eMg" = ( /obj/machinery/door/airlock/glass{ name = "Cabin" @@ -44870,6 +44745,13 @@ icon_state = "neutralfull" }, /area/engineering/engine) +"eXZ" = ( +/obj/structure/dispenser/oxygen, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "eYe" = ( /obj/machinery/camera{ c_tag = "Central Ring Hallway West 2"; @@ -45476,6 +45358,16 @@ icon_state = "dark" }, /area/engineering/hardsuitstorage) +"ffh" = ( +/obj/machinery/suit_storage_unit/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "ffi" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -46344,6 +46236,14 @@ icon_state = "greencorner" }, /area/maintenance/garden) +"frC" = ( +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 26 + }, +/obj/machinery/dye_generator, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "frD" = ( /turf/simulated/wall/r_wall, /area/atmos/control) @@ -47667,6 +47567,18 @@ icon_state = "darkblue" }, /area/medical/surgery/north) +"fIl" = ( +/obj/structure/cable{ + icon_state = "2-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 10 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 + }, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "fIm" = ( /obj/machinery/door/airlock/command{ name = "Head of Security"; @@ -48784,6 +48696,13 @@ }, /turf/simulated/floor/plating, /area/security/hos) +"fWv" = ( +/obj/structure/table/reinforced, +/obj/item/clipboard, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/bridge) "fWz" = ( /obj/item/twohanded/required/kirbyplants, /turf/simulated/floor/plasteel{ @@ -49952,6 +49871,21 @@ icon_state = "grimy" }, /area/library) +"gml" = ( +/obj/structure/table/reinforced, +/obj/effect/decal/warning_stripes/yellow/hollow, +/obj/item/vending_refill/custom{ + pixel_x = 3; + pixel_y = 5 + }, +/obj/item/vending_refill/custom{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/hand_labeler, +/obj/item/stack/packageWrap, +/turf/simulated/floor/plasteel, +/area/storage/primary) "gmq" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small, @@ -52122,6 +52056,17 @@ }, /turf/simulated/floor/engine, /area/toxins/test_chamber) +"gMJ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkredfull" + }, +/area/security/securearmory) "gMS" = ( /obj/structure/table/wood, /obj/item/reagent_containers/food/drinks/bottle/vodka{ @@ -53085,6 +53030,19 @@ }, /turf/simulated/floor/plating, /area/crew_quarters/courtroom) +"gWT" = ( +/obj/structure/table/reinforced, +/obj/item/ai_module/reset, +/obj/item/flash, +/obj/item/flash, +/obj/effect/decal/warning_stripes/yellow, +/obj/machinery/requests_console{ + department = "Tech Storage"; + name = "Tech Storage Requests Console"; + pixel_y = 32 + }, +/turf/simulated/floor/plasteel, +/area/storage/tech) "gXb" = ( /turf/simulated/floor/plasteel{ icon_state = "whitepurple" @@ -53111,6 +53069,30 @@ icon_state = "darkbluefull" }, /area/construction/hallway) +"gXi" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/suit/armor/laserproof, +/obj/item/gun/energy/ionrifle, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "gXk" = ( /obj/structure/cable{ icon_state = "1-2" @@ -53922,6 +53904,23 @@ /obj/machinery/vending/artvend, /turf/simulated/floor/plating, /area/maintenance/starboard) +"hip" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "hiB" = ( /obj/machinery/camera{ c_tag = "Central Ring Hallway North 2"; @@ -55222,6 +55221,38 @@ icon_state = "bluefull" }, /area/bridge/checkpoint/south) +"hzg" = ( +/obj/machinery/light, +/obj/machinery/newscaster{ + pixel_y = -30 + }, +/obj/structure/closet/secure_closet/quartermaster, +/obj/item/fulton_core, +/obj/item/extraction_pack, +/obj/item/flash, +/obj/item/megaphone, +/obj/item/gps{ + desc = "A positioning system designed to keep an eye on your fellow workers."; + gpstag = "CARG0"; + icon_state = "gps-m" + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -1; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -3 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 5; + pixel_y = 3 + }, +/obj/item/clipboard, +/obj/item/mining_voucher, +/turf/simulated/floor/plasteel{ + icon_state = "brown" + }, +/area/quartermaster/qm) "hzk" = ( /obj/structure/cable{ icon_state = "4-8" @@ -55917,6 +55948,17 @@ /obj/effect/landmark/tiles/damageturf, /turf/simulated/floor/plating, /area/maintenance/asmaint4) +"hIR" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/security/securearmory) "hIS" = ( /obj/structure/cable{ icon_state = "0-8" @@ -55951,6 +55993,33 @@ }, /turf/simulated/floor/plasteel, /area/engineering/controlroom) +"hJt" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "hJA" = ( /obj/machinery/computer/arcade, /turf/simulated/floor/carpet, @@ -56724,15 +56793,6 @@ icon = 'icons/turf/walls/shuttle/gray_shuttle_wall.dmi' }, /area/toxins/test_area) -"hSo" = ( -/obj/machinery/newscaster{ - pixel_x = -30 - }, -/obj/structure/dresser, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "hSs" = ( /obj/structure/chair/comfy/brown, /obj/effect/landmark/start/detective, @@ -57009,30 +57069,6 @@ }, /turf/simulated/floor/plasteel, /area/engineering/engine) -"hWb" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/item/clothing/suit/armor/laserproof, -/obj/item/gun/energy/ionrifle, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "hWd" = ( /turf/simulated/floor/plasteel{ dir = 1; @@ -57737,24 +57773,6 @@ icon_state = "whitebluecorner" }, /area/medical/biostorage) -"igH" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Security SMG's" - }, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/automatic/wt550, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = 3; - pixel_y = -3 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "igL" = ( /obj/machinery/light/small{ dir = 8 @@ -59230,18 +59248,6 @@ icon_state = "darkred" }, /area/security/evidence) -"ixm" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/camera{ - c_tag = "Secure Armory West"; - dir = 4; - network = list("SS13","Security") - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/securearmory) "ixo" = ( /obj/machinery/door/firedoor, /turf/simulated/floor/plasteel{ @@ -60520,6 +60526,32 @@ icon_state = "neutralcorner" }, /area/security/lobby) +"iOo" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, +/obj/structure/table/glass, +/obj/machinery/door/window{ + base_state = "right"; + dir = 2; + icon_state = "right"; + name = "Core Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "iOv" = ( /obj/structure/cable{ icon_state = "2-4" @@ -60613,13 +60645,6 @@ icon_state = "neutralfull" }, /area/atmos) -"iOV" = ( -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "darkred" - }, -/area/security/securearmory) "iPd" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -61865,6 +61890,34 @@ }, /turf/simulated/floor/engine, /area/medical/chemistry) +"jgj" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/power/apc{ + pixel_y = -26 + }, +/obj/structure/cable, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -6 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jgk" = ( /obj/machinery/light{ dir = 4 @@ -62119,6 +62172,13 @@ }, /turf/simulated/floor/engine, /area/toxins/test_chamber) +"jjF" = ( +/obj/item/ai_module/freeform, +/obj/structure/table/glass, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "jjT" = ( /obj/machinery/dna_scannernew, /turf/simulated/floor/plasteel{ @@ -62168,6 +62228,21 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/greengrid, /area/security/nuke_storage) +"jkH" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/structure/rack/gunrack, +/obj/item/gun/energy/gun{ + pixel_x = -7 + }, +/obj/item/gun/energy/gun{ + pixel_x = 9 + }, +/obj/structure/window/reinforced, +/obj/item/gun/energy/gun, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jkJ" = ( /obj/item/twohanded/required/kirbyplants, /obj/machinery/light{ @@ -62239,6 +62314,33 @@ icon_state = "darkblue" }, /area/turret_protected/ai) +"jlr" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, +/obj/structure/table/glass, +/obj/machinery/door/window{ + base_state = "right"; + dir = 2; + icon_state = "right"; + name = "Core Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "jlz" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -62885,6 +62987,12 @@ /obj/effect/decal/warning_stripes/north, /turf/simulated/floor/plasteel, /area/engineering/engine) +"jta" = ( +/obj/effect/decal/warning_stripes/north, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jtd" = ( /obj/effect/decal/warning_stripes/southwest, /obj/machinery/atmospherics/binary/valve/digital/open{ @@ -63614,15 +63722,6 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel, /area/bridge/vip) -"jBm" = ( -/obj/effect/decal/warning_stripes/north, -/obj/item/radio/intercom{ - pixel_x = 28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "jBs" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -63977,6 +64076,13 @@ icon_state = "neutralfull" }, /area/quartermaster/office) +"jFZ" = ( +/obj/vehicle/ridden/secway, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "jGi" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 9 @@ -64109,6 +64215,12 @@ icon_state = "dark" }, /area/security/prisonershuttle) +"jHR" = ( +/obj/machinery/hologram/holopad, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jHS" = ( /obj/structure/bed/dogbed/pet, /mob/living/simple_animal/pet/cat/white/Penny, @@ -64446,14 +64558,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/brig) -"jKY" = ( -/obj/structure/table/reinforced, -/obj/item/clipboard, -/obj/item/mining_voucher, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/bridge) "jLc" = ( /obj/structure/cable{ icon_state = "4-8" @@ -64680,19 +64784,11 @@ icon_state = "darkred" }, /area/space) -"jPK" = ( -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/rack{ - dir = 8; - layer = 2.9 +"jPT" = ( +/obj/effect/decal/warning_stripes/north, +/obj/item/radio/intercom{ + pixel_x = 28 }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -65530,25 +65626,6 @@ icon_state = "dark" }, /area/crew_quarters/theatre) -"kaB" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = -6; - pixel_y = 6 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "kaE" = ( /obj/structure/sign/securearea, /turf/simulated/wall/r_wall, @@ -65894,37 +65971,6 @@ /obj/structure/closet/radiation, /turf/simulated/floor/engine, /area/toxins/sm_test_chamber) -"kgy" = ( -/obj/machinery/light, -/obj/machinery/newscaster{ - pixel_y = -30 - }, -/obj/structure/closet/secure_closet/quartermaster, -/obj/item/fulton_core, -/obj/item/extraction_pack, -/obj/item/flash, -/obj/item/megaphone, -/obj/item/gps{ - desc = "A positioning system designed to keep an eye on your fellow workers."; - gpstag = "CARG0"; - icon_state = "gps-m" - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -1; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = 5; - pixel_y = 3 - }, -/obj/item/clipboard, -/turf/simulated/floor/plasteel{ - icon_state = "brown" - }, -/area/quartermaster/qm) "kgz" = ( /obj/structure/girder, /turf/simulated/floor/plating, @@ -66233,6 +66279,35 @@ icon_state = "neutralfull" }, /area/assembly/robotics) +"klt" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/lockbox/mindshield, +/obj/item/storage/box/trackimp, +/obj/item/storage/box/chemimp{ + pixel_x = 4; + pixel_y = 3 + }, +/obj/item/lock_buster, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "klv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -66712,16 +66787,6 @@ /obj/structure/noticeboard, /turf/simulated/wall, /area/quartermaster/storage) -"krc" = ( -/obj/item/twohanded/required/kirbyplants, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 26 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "krh" = ( /obj/effect/turf_decal/siding/brown{ dir = 4 @@ -67093,6 +67158,18 @@ /obj/effect/decal/remains/human, /turf/simulated/floor/plating, /area/maintenance/asmaint2) +"kwW" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/camera{ + c_tag = "Secure Armory West"; + dir = 4; + network = list("SS13","Security") + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/securearmory) "kxq" = ( /obj/structure/table/reinforced, /obj/item/storage/box/flashbangs{ @@ -67851,19 +67928,6 @@ icon_state = "grimy" }, /area/chapel/office) -"kGQ" = ( -/obj/structure/table/reinforced, -/obj/item/aiModule/reset, -/obj/item/flash, -/obj/item/flash, -/obj/effect/decal/warning_stripes/yellow, -/obj/machinery/requests_console{ - department = "Tech Storage"; - name = "Tech Storage Requests Console"; - pixel_y = 32 - }, -/turf/simulated/floor/plasteel, -/area/storage/tech) "kGW" = ( /obj/structure/table/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -69873,12 +69937,6 @@ /obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/carpet/arcade, /area/crew_quarters/fitness) -"lko" = ( -/obj/machinery/dye_generator, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "lkA" = ( /obj/structure/chair/office/dark{ dir = 1 @@ -71226,6 +71284,17 @@ icon_state = "whitepurple" }, /area/toxins/test_chamber) +"lBt" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "vault" + }, +/area/security/securearmory) "lBw" = ( /obj/machinery/flasher/portable, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -72527,25 +72596,6 @@ icon_state = "green" }, /area/medical/virology/lab) -"lSA" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/box/flashbangs, -/obj/item/storage/box/flashbangs{ - pixel_x = 3; - pixel_y = -3 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "lSG" = ( /turf/simulated/floor/plasteel{ icon_state = "darkblue" @@ -75043,6 +75093,23 @@ }, /turf/space, /area/aisat) +"mwN" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -7 + }, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = 7 + }, +/obj/item/gun/projectile/shotgun/riot, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Riot shotguns" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "mwW" = ( /obj/machinery/alarm{ pixel_y = 22 @@ -75641,6 +75708,14 @@ icon_state = "fancy-wood-oak-broken7" }, /area/maintenance/banya) +"mCS" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/security/securearmory) "mCW" = ( /turf/simulated/floor/plasteel{ dir = 1; @@ -77697,6 +77772,20 @@ icon_state = "white" }, /area/toxins/lab) +"naT" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "naU" = ( /obj/machinery/computer/prisoner{ dir = 8 @@ -78244,6 +78333,25 @@ }, /turf/simulated/floor/plating, /area/bridge/checkpoint/south) +"niy" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/box/flashbangs, +/obj/item/storage/box/flashbangs{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "niI" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -80210,23 +80318,6 @@ icon_state = "green" }, /area/medical/virology/lab) -"nGa" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -7 - }, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = 7 - }, -/obj/item/gun/projectile/shotgun/riot, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Riot shotguns" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "nGg" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -83175,13 +83266,6 @@ }, /turf/simulated/floor/wood/fancy/light, /area/crew_quarters/courtroom) -"oqv" = ( -/obj/structure/table/reinforced, -/obj/item/stack/packageWrap, -/obj/item/hand_labeler, -/obj/effect/decal/warning_stripes/yellow/hollow, -/turf/simulated/floor/plasteel, -/area/storage/primary) "oqw" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable{ @@ -84127,16 +84211,6 @@ icon_state = "tranquillite" }, /area/crew_quarters/theatre) -"oBA" = ( -/obj/structure/table/reinforced, -/obj/machinery/computer/security/telescreen/entertainment{ - pixel_x = 32 - }, -/obj/item/paper/deltainfo, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "oBH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -85306,25 +85380,6 @@ icon_state = "neutralcorner" }, /area/crew_quarters/locker) -"oPR" = ( -/obj/structure/rack/gunrack, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/gun/energy/laser{ - pixel_x = -8 - }, -/obj/item/gun/energy/laser{ - pixel_x = 10 - }, -/obj/item/gun/energy/laser{ - pixel_x = 1 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "oPZ" = ( /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -86494,6 +86549,10 @@ icon_state = "white" }, /area/medical/sleeper) +"peo" = ( +/obj/structure/table/reinforced, +/turf/simulated/floor/plating, +/area/maintenance/trading) "peP" = ( /obj/structure/bed/dogbed/pet, /mob/living/simple_animal/mouse/hamster/Representative, @@ -87572,20 +87631,6 @@ }, /turf/simulated/floor/plating, /area/crew_quarters/hor) -"prr" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 6 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "prs" = ( /obj/effect/decal/cleanable/blood, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -87850,23 +87895,6 @@ icon_state = "green" }, /area/maintenance/garden) -"puk" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/door/airlock/maintenance{ - req_access = list(12) - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "pus" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/machinery/atmospherics/unary/vent_scrubber/on, @@ -88144,31 +88172,6 @@ icon_state = "neutralfull" }, /area/hallway/primary/central/nw) -"pyk" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/door/window{ - dir = 2; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "pyN" = ( /obj/machinery/power/emitter{ anchored = 1; @@ -89055,6 +89058,14 @@ }, /turf/simulated/floor/plasteel, /area/engineering/engine) +"pGD" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "vault" + }, +/area/security/securearmory) "pGI" = ( /obj/structure/cable{ icon_state = "0-8" @@ -89768,12 +89779,6 @@ }, /turf/simulated/floor/plasteel, /area/security/securehallway) -"pNI" = ( -/obj/effect/decal/warning_stripes/north, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "pNN" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold/hidden/supply, @@ -90186,6 +90191,13 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/medical/medbay2) +"pTI" = ( +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkred" + }, +/area/security/securearmory) "pTS" = ( /obj/structure/table, /obj/machinery/light{ @@ -91113,20 +91125,6 @@ /obj/effect/decal/warning_stripes/yellow, /turf/simulated/floor/plasteel, /area/hallway/primary/central/ne) -"qdp" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "qdz" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -91516,6 +91514,18 @@ }, /turf/simulated/floor/plating, /area/security/visiting_room) +"qif" = ( +/obj/structure/closet/bombclosetsecurity, +/obj/effect/decal/warning_stripes/red, +/obj/effect/decal/warning_stripes/north, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 26 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "qih" = ( /turf/simulated/floor/plasteel{ dir = 6; @@ -91669,34 +91679,6 @@ /obj/structure/window/reinforced, /turf/simulated/floor/plating, /area/maintenance/consarea_virology) -"qjM" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/power/apc{ - pixel_y = -26 - }, -/obj/structure/cable, -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -6 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "qjN" = ( /obj/effect/landmark/ninja_teleport, /turf/simulated/floor/plating, @@ -91934,21 +91916,6 @@ "qni" = ( /turf/simulated/wall/r_wall, /area/turret_protected/aisat_interior) -"qnk" = ( -/obj/machinery/power/apc{ - dir = 1; - pixel_y = 26 - }, -/obj/structure/chair/barber{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "0-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "qnB" = ( /obj/structure/cable{ icon_state = "4-8" @@ -92067,23 +92034,6 @@ }, /turf/simulated/floor/plasteel, /area/crew_quarters/kitchen) -"qoy" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "qoG" = ( /obj/machinery/atmospherics/unary/portables_connector{ dir = 8 @@ -93226,14 +93176,6 @@ icon_state = "neutralcorner" }, /area/crew_quarters/locker) -"qAX" = ( -/obj/item/twohanded/required/kirbyplants, -/obj/structure/sign/barber{ - pixel_x = 22; - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/crew_quarters/serviceyard) "qAY" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 @@ -94346,6 +94288,34 @@ icon_state = "white" }, /area/assembly/robotics) +"qOn" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/ammo_box/shotgun/buck{ + pixel_x = 3 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/machinery/camera{ + c_tag = "Secure Armory East"; + dir = 8; + network = list("SS13","Security") + }, +/obj/machinery/newscaster/security_unit{ + pixel_x = 28 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "qOo" = ( /obj/machinery/atmospherics/pipe/simple/hidden/universal{ dir = 4 @@ -95214,6 +95184,24 @@ icon_state = "redyellowfull" }, /area/engineering/break_room) +"qZA" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Security SMG's" + }, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/automatic/wt550, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "qZO" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -96676,17 +96664,6 @@ }, /turf/simulated/floor/wood/fancy/light, /area/crew_quarters/courtroom) -"rrl" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/light{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "vault" - }, -/area/security/securearmory) "rrr" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -96923,6 +96900,16 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint) +"rui" = ( +/obj/structure/mirror{ + pixel_x = 28 + }, +/obj/effect/decal/cleanable/dirt, +/obj/structure/chair/barber{ + dir = 4 + }, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "rum" = ( /obj/structure/cable{ icon_state = "1-2" @@ -97304,13 +97291,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/brig) -"rzo" = ( -/obj/item/aiModule/quarantine, -/obj/structure/table/glass, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "rzq" = ( /obj/structure/cable{ icon_state = "1-2" @@ -97690,14 +97670,6 @@ icon_state = "red" }, /area/security/customs) -"rDM" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "vault" - }, -/area/security/securearmory) "rDP" = ( /obj/machinery/light{ dir = 4 @@ -97946,17 +97918,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/starboardsolar) -"rGK" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "vault" - }, -/area/security/securearmory) "rGM" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -97967,17 +97928,6 @@ icon_state = "purplefull" }, /area/toxins/xenobiology) -"rGO" = ( -/obj/effect/decal/warning_stripes/red, -/obj/machinery/portable_atmospherics/canister/oxygen, -/obj/item/radio/intercom{ - pixel_y = 28 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "vault" - }, -/area/security/securearmory) "rHl" = ( /obj/structure/table/reinforced, /obj/item/deck/cards/syndicate/black, @@ -98064,13 +98014,6 @@ icon_state = "red" }, /area/security/reception) -"rHW" = ( -/obj/structure/dispenser/oxygen, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "rIt" = ( /obj/structure/chair/office/light{ dir = 8 @@ -98151,16 +98094,6 @@ icon_state = "whitepurple" }, /area/medical/research/nhallway) -"rJG" = ( -/obj/machinery/suit_storage_unit/security, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "rJJ" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/structure/closet/radiation, @@ -98282,16 +98215,6 @@ }, /turf/simulated/floor/plasteel, /area/toxins/lab) -"rLl" = ( -/obj/machinery/suit_storage_unit/security, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/light{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "rLo" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small{ @@ -98354,14 +98277,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint4) -"rLF" = ( -/obj/effect/decal/warning_stripes/east, -/obj/machinery/suit_storage_unit/security, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "rLL" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable{ @@ -98846,6 +98761,18 @@ icon_state = "neutralcorner" }, /area/crew_quarters/locker) +"rRY" = ( +/obj/machinery/firealarm{ + dir = 8; + pixel_x = -26 + }, +/obj/structure/table/wood, +/obj/item/reagent_containers/food/drinks/bottle/whiskey, +/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass, +/obj/item/reagent_containers/food/drinks/flask/detflask, +/obj/item/lighter/zippo/detective, +/turf/simulated/floor/wood, +/area/security/detectives_office) "rSa" = ( /obj/effect/decal/warning_stripes/north, /obj/machinery/light{ @@ -98936,15 +98863,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/wall, /area/maintenance/asmaint) -"rTE" = ( -/obj/machinery/computer/borgupload, -/obj/item/radio/intercom/private{ - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "rTF" = ( /obj/machinery/photocopier, /turf/simulated/floor/plasteel{ @@ -99288,6 +99206,11 @@ icon_state = "caution" }, /area/engineering/break_room) +"rZj" = ( +/obj/effect/decal/cleanable/dirt, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "rZo" = ( /obj/item/twohanded/required/kirbyplants, /obj/machinery/alarm{ @@ -101016,21 +100939,6 @@ icon_state = "whiteblue" }, /area/medical/sleeper) -"stT" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/structure/rack/gunrack, -/obj/item/gun/energy/gun{ - pixel_x = -7 - }, -/obj/item/gun/energy/gun{ - pixel_x = 9 - }, -/obj/structure/window/reinforced, -/obj/item/gun/energy/gun, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "stU" = ( /obj/item/stack/ore/bananium, /turf/space, @@ -102302,6 +102210,25 @@ /obj/item/flag/nt, /turf/simulated/floor/carpet/royalblue, /area/crew_quarters/captain/bedroom) +"sKi" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = -6; + pixel_y = 6 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "sKv" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 @@ -102484,12 +102411,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint2) -"sMP" = ( -/obj/machinery/hologram/holopad, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "sMS" = ( /obj/structure/cable{ icon_state = "1-2" @@ -102992,6 +102913,44 @@ /obj/effect/spawner/random_spawners/rodent, /turf/simulated/floor/plating, /area/maintenance/trading) +"sSc" = ( +/obj/structure/rack/gunrack, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -7 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 7 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) +"sSe" = ( +/obj/structure/rack/gunrack, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/gun/energy/laser{ + pixel_x = -8 + }, +/obj/item/gun/energy/laser{ + pixel_x = 10 + }, +/obj/item/gun/energy/laser{ + pixel_x = 1 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "sSo" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 @@ -103828,20 +103787,6 @@ }, /turf/simulated/floor/plating, /area/assembly/robotics) -"tdC" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/glass{ - name = "Barber Shop" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "tdH" = ( /turf/simulated/floor/plasteel{ dir = 8; @@ -103977,18 +103922,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/kitchen) -"tfh" = ( -/obj/structure/closet/bombclosetsecurity, -/obj/effect/decal/warning_stripes/red, -/obj/effect/decal/warning_stripes/north, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 26 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "tfk" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable{ @@ -105652,15 +105585,6 @@ icon_state = "neutralfull" }, /area/engineering/engine) -"tzR" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/effect/landmark/start/barber, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "tzV" = ( /obj/machinery/door/airlock/medical{ glass = 1; @@ -106406,6 +106330,25 @@ icon_state = "white" }, /area/medical/genetics) +"tIp" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/item/razor, +/obj/structure/rack, +/obj/item/razor{ + pixel_x = -4; + pixel_y = 2 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fpmaint) "tIu" = ( /obj/machinery/power/apc{ dir = 1; @@ -107493,6 +107436,14 @@ "tSQ" = ( /turf/simulated/wall/r_wall, /area/maintenance/electrical) +"tSY" = ( +/obj/effect/decal/warning_stripes/east, +/obj/machinery/suit_storage_unit/security, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tTu" = ( /obj/machinery/atmospherics/pipe/simple/visible{ desc = "Труба служит для подачу горючей смеси в турбину для её работы"; @@ -107786,6 +107737,32 @@ icon_state = "whitehall" }, /area/maintenance/tourist) +"tWJ" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/door/window{ + dir = 2; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tWK" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -109383,6 +109360,22 @@ icon_state = "whiteyellow" }, /area/medical/chemistry) +"uqh" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/box/teargas, +/obj/item/storage/box/teargas{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uqq" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/machinery/alarm{ @@ -109406,29 +109399,6 @@ icon_state = "darkred" }, /area/security/securearmory) -"uqM" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "uqO" = ( /obj/structure/cable{ icon_state = "0-2" @@ -109441,34 +109411,6 @@ icon_state = "tranquillite" }, /area/maintenance/kitchen) -"urn" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/light{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SP-91-RC"; - req_access = list(1) - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 8 - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 4 - }, -/obj/item/ammo_box/magazine/sp91rc, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = -4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "urA" = ( /obj/structure/window/reinforced{ dir = 4 @@ -110430,6 +110372,29 @@ icon_state = "dark" }, /area/medical/virology/lab) +"uDw" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uDx" = ( /obj/effect/spawner/window/reinforced/polarized{ id = "qm" @@ -110491,6 +110456,32 @@ /obj/item/radio/beacon, /turf/simulated/floor/plasteel, /area/security/main) +"uEW" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/door/window{ + dir = 2; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uEX" = ( /obj/machinery/door/firedoor, /obj/effect/decal/warning_stripes/yellow, @@ -111863,6 +111854,23 @@ /obj/effect/decal/warning_stripes/south, /turf/simulated/floor/plasteel, /area/medical/research/nhallway) +"uUX" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uVh" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -112056,6 +112064,32 @@ icon_state = "neutralcorner" }, /area/hallway/primary/central/ne) +"uXB" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/door/window{ + dir = 2; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uXU" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table/wood, @@ -113439,34 +113473,6 @@ icon_state = "darkblue" }, /area/medical/surgery/south) -"vpi" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/ammo_box/shotgun/buck{ - pixel_x = 3 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/machinery/camera{ - c_tag = "Secure Armory East"; - dir = 8; - network = list("SS13","Security") - }, -/obj/machinery/newscaster/security_unit{ - pixel_x = 28 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "vpu" = ( /obj/structure/cable{ icon_state = "1-2" @@ -113622,31 +113628,6 @@ }, /turf/simulated/floor/plating, /area/medical/surgery/south) -"vqM" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/door/window{ - dir = 2; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "vqY" = ( /turf/simulated/floor/plasteel{ dir = 9; @@ -113939,25 +113920,6 @@ icon_state = "neutralfull" }, /area/assembly/robotics) -"vuE" = ( -/obj/structure/rack/gunrack, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -7 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 7 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "vvh" = ( /obj/structure/bed, /obj/item/radio/intercom/locked/prison{ @@ -115848,31 +115810,6 @@ icon_state = "whiteblue" }, /area/medical/surgery/south) -"vSt" = ( -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/door/window{ - dir = 2; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "vSA" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -116473,17 +116410,6 @@ /obj/machinery/papershredder, /turf/simulated/floor/carpet, /area/magistrateoffice) -"vYk" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "4-8" - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkredfull" - }, -/area/security/securearmory) "vYr" = ( /obj/machinery/light{ dir = 8 @@ -116517,35 +116443,6 @@ icon_state = "grimy" }, /area/chapel/office) -"vYL" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/lockbox/mindshield, -/obj/item/storage/box/trackimp, -/obj/item/storage/box/chemimp{ - pixel_x = 4; - pixel_y = 3 - }, -/obj/item/lock_buster, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "vYT" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -120499,32 +120396,6 @@ icon_state = "neutralfull" }, /area/quartermaster/storage) -"wXI" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, -/obj/structure/table/glass, -/obj/machinery/door/window{ - base_state = "right"; - dir = 2; - icon_state = "right"; - name = "Core Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "wXY" = ( /obj/structure/window/reinforced{ dir = 4 @@ -120628,17 +120499,6 @@ }, /turf/simulated/floor/plasteel, /area/engineering/controlroom) -"xas" = ( -/obj/machinery/firealarm{ - dir = 8; - pixel_x = -26 - }, -/obj/structure/table/wood, -/obj/item/reagent_containers/food/drinks/bottle/whiskey, -/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass, -/obj/item/reagent_containers/food/drinks/flask/detflask, -/turf/simulated/floor/wood, -/area/security/detectives_office) "xat" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -120705,13 +120565,6 @@ "xbn" = ( /turf/simulated/wall/r_wall/coated, /area/toxins/mixing) -"xbr" = ( -/obj/item/aiModule/freeform, -/obj/structure/table/glass, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "xbB" = ( /obj/machinery/door/window/brigdoor{ base_state = "rightsecure"; @@ -121646,6 +121499,16 @@ icon_state = "darkblue" }, /area/medical/morgue) +"xnc" = ( +/obj/machinery/suit_storage_unit/security, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "xne" = ( /obj/machinery/light{ dir = 4 @@ -122013,13 +121876,6 @@ "xqu" = ( /turf/simulated/wall/r_wall, /area/security/range) -"xqE" = ( -/obj/vehicle/ridden/secway, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "xqJ" = ( /obj/structure/grille, /turf/simulated/floor/plating, @@ -122455,6 +122311,36 @@ /obj/effect/landmark/tiles/damageturf, /turf/simulated/floor/plating, /area/maintenance/asmaint4) +"xuU" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SMG" + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 4; + pixel_y = -4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 6; + pixel_y = -6 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "xuX" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable{ @@ -125151,21 +125037,6 @@ }, /turf/simulated/floor/plasteel, /area/bridge/checkpoint/south) -"xWn" = ( -/obj/structure/table/reinforced, -/obj/structure/mirror{ - pixel_x = 28 - }, -/obj/machinery/camera{ - c_tag = "Barber Shop" - }, -/obj/item/radio/intercom{ - pixel_y = 28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "xWx" = ( /obj/effect/decal/warning_stripes/south, /obj/structure/morgue{ @@ -125867,6 +125738,10 @@ icon_state = "tranquillite" }, /area/maintenance/kitchen) +"ycW" = ( +/obj/machinery/portable_atmospherics/canister/nitrogen, +/turf/simulated/floor/wood/fancy/oak, +/area/maintenance/trading) "ydg" = ( /obj/machinery/door/poddoor/preopen{ id_tag = "BridgeLockdown"; @@ -126541,6 +126416,17 @@ icon_state = "dark" }, /area/medical/morgue) +"ykv" = ( +/obj/effect/decal/warning_stripes/red, +/obj/machinery/portable_atmospherics/canister/oxygen, +/obj/item/radio/intercom{ + pixel_y = 28 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "vault" + }, +/area/security/securearmory) "ykx" = ( /turf/simulated/wall/r_wall, /area/assembly/robotics) @@ -155399,7 +155285,7 @@ oNx kcq bxh bxh -kGQ +gWT tdH tdH tdH @@ -156391,7 +156277,7 @@ wAK cmh bTv cNg -cAt +bTv cAK xQW cjn @@ -157418,12 +157304,12 @@ aTA aoe cnf btQ -cwa -cwa -cwa -cHP -puk -cwa +peo +ycW +cAz +cbs +eMa +iTZ eYX bev iTZ @@ -157459,7 +157345,7 @@ rfV scx mIQ nVL -oqv +gml uQy vGw wpP @@ -157503,7 +157389,7 @@ mAn ipb suf wAv -duH +byX ddi apg oUh @@ -157675,12 +157561,12 @@ cAz cAz cAz dDf -cwa -lko -hSo -bZY -cAy -dFe +cAz +cAz +cAz +cbs +tIp +iTZ dFW dFW iTZ @@ -157932,12 +157818,12 @@ cno jHA cno lEk -cwa -qnk -tzR -prr -cmL -tdC +gbI +cbs +aVS +aVS +fIl +scT eSU eSU bGy @@ -158189,13 +158075,13 @@ aVS bWL cbs crN -cwa -xWn -oBA -dBN -krc -dFe -qAX +cku +cbs +rui +rZj +frC +iTZ +oNr dFW dFW dFW @@ -162338,7 +162224,7 @@ aaq bwf ydg bwf -jKY +fWv mYa byW tEy @@ -163115,10 +163001,10 @@ rlm tIL ugf bJh -ciR +jlr ddG -bOG -rzo +ett +eAF bMV bJh aaq @@ -164147,7 +164033,7 @@ vQn soL bKN bMY -rTE +bJo bJh aaq bIn @@ -164657,10 +164543,10 @@ rlm bCp cMY bJh -wXI +iOo hpE -cWa -xbr +agb +jjF bMV bJh aaq @@ -170544,7 +170430,7 @@ dWL bGm aWx duw -kgy +hzg dDb aZp kMn @@ -172625,7 +172511,7 @@ rYY eMZ vlU cOt -xas +rRY sTG xux vUF @@ -179043,14 +178929,14 @@ pBU pLm qhn lhS -rDM -rrl -dtj +pGD +hIR +mCS tzh lYj tzh -ciT -igH +xuU +qZA lhS lhS kNt @@ -179300,15 +179186,15 @@ nRT tLv tMU lhS -rGK +lBt szM aMD -ixm +kwW uqu sei vXH -iOV -hWb +pTI +gXi lhS mpW tLv @@ -179557,15 +179443,15 @@ hdu pLB bzC lhS -rGO +ykv tHS dIj -oPR -uqM -stT -vYk +sSe +uDw +jkH +gMJ wCH -bVZ +hJt lhS mzW lqG @@ -179814,15 +179700,15 @@ oIn qug mml lhS -rHW +eXZ tHS iTk -jPK +uUX vNX -vSt +uEW aau nhY -qjM +jgj lhS vcX nHu @@ -180071,15 +179957,15 @@ pbM mqz iUI lhS -rJG +ffh tHS -sMP -qdp +jHR +naT vNX -pyk +uXB ePU wCH -dZt +uqh lhS kih tNf @@ -180328,15 +180214,15 @@ pcc qug mfd lhS -rLl +xnc tHS toP -qdp +naT vNX -pyk +uXB ePU wCH -lSA +niy lhS vcX iHp @@ -180585,15 +180471,15 @@ pdt onq ioU lhS -rLF +tSY rAs toS -qoy +hip vNX -vqM +tWJ ePU wCH -pNI +jta xXC vcX nHu @@ -180845,9 +180731,9 @@ lhS lhS xmw tsW -vuE -urn -xqE +sSc +dRD +jFZ vYt wCH myZ @@ -181107,7 +180993,7 @@ vdJ uTS uTS mbY -jBm +jPT eEb vcX nHu @@ -181358,11 +181244,11 @@ mml rbU lhS sBd -tfh -nGa -vpi -kaB -vYL +qif +mwN +qOn +sKi +klt jDw lhS lhS diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_legion.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_legion.dmm index 300a5a4983e..1d18bd27fa6 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_legion.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_legion.dmm @@ -92,7 +92,7 @@ /obj/item/shield/riot/roman{ name = "battered shield" }, -/obj/item/claymore{ +/obj/item/melee/claymore{ block_chance = 40; desc = "A cracked and blunted sword, clearly weathered over the ages."; force = 30; diff --git a/_maps/map_files/celestation/celestation.dmm b/_maps/map_files/celestation/celestation.dmm index 93674e986d9..6b451b344ae 100644 --- a/_maps/map_files/celestation/celestation.dmm +++ b/_maps/map_files/celestation/celestation.dmm @@ -178,23 +178,6 @@ icon_state = "whiteblue" }, /area/medical/surgery/theatre) -"acd" = ( -/obj/structure/chair/barber{ - dir = 8 - }, -/obj/effect/landmark/start/barber, -/obj/structure/cable/orange{ - icon_state = "1-4" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 5 - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "acj" = ( /obj/structure/holohoop{ dir = 8 @@ -314,6 +297,13 @@ icon_state = "greenfull" }, /area/security/permabrig) +"acQ" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/port) "acR" = ( /obj/item/radio/intercom, /turf/simulated/wall/r_wall, @@ -1311,14 +1301,6 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/carpet/green, /area/library) -"alF" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkredfull" - }, -/area/security/armory) "alK" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -1844,6 +1826,23 @@ }, /turf/simulated/floor/glass, /area/hallway/primary/fore) +"apL" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 6 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 6 + }, +/obj/structure/cable/orange{ + icon_state = "2-4" + }, +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/armory) "apM" = ( /obj/machinery/chem_dispenser, /obj/structure/reagent_dispensers/fueltank/chem{ @@ -4671,16 +4670,6 @@ "aMS" = ( /turf/simulated/wall/r_wall, /area/crew_quarters/cabin1) -"aNl" = ( -/obj/structure/table, -/obj/item/aiModule/reset, -/obj/machinery/firealarm{ - pixel_y = 26 - }, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "aNo" = ( /turf/simulated/floor/plasteel{ dir = 8; @@ -4861,17 +4850,6 @@ /obj/machinery/camera/autoname, /turf/simulated/floor/glass, /area/security/prison/cell_block/A) -"aOM" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/radio/intercom{ - pixel_y = 28 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "aON" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -5786,27 +5764,6 @@ }, /turf/simulated/floor/carpet/cyan, /area/crew_quarters/fitness) -"aVT" = ( -/obj/structure/rack, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/shield/riot, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - dir = 2; - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "aVZ" = ( /obj/machinery/disposal, /obj/structure/disposalpipe/trunk{ @@ -5952,17 +5909,6 @@ color = "gray" }, /area/crew_quarters/bar/atrium) -"aWL" = ( -/obj/structure/table, -/obj/item/aiModule/crewsimov, -/obj/machinery/flasher{ - id = "AI"; - pixel_y = 21 - }, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "aWM" = ( /obj/machinery/door/airlock/public/glass{ name = "Central Access" @@ -6940,12 +6886,6 @@ "bcC" = ( /turf/simulated/openspace, /area/hallway/primary/starboard/south) -"bcK" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/aiModule/reset, -/turf/simulated/floor/plating, -/area/storage/tech) "bcL" = ( /obj/structure/cable/yellow{ icon_state = "1-2" @@ -7206,22 +7146,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/engineering) -"beN" = ( -/obj/machinery/light, -/obj/machinery/alarm{ - dir = 1; - pixel_y = -26 - }, -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 1 - }, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "beQ" = ( /obj/structure/table/wood, /obj/item/eftpos, @@ -8152,31 +8076,6 @@ icon_state = "navybluealt" }, /area/turret_protected/ai) -"bkW" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, -/obj/structure/table/glass, -/obj/machinery/door/window{ - dir = 1; - name = "High-Risk Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/ai_status_display{ - pixel_y = -32 - }, -/obj/machinery/light, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "bkX" = ( /obj/structure/table/wood, /obj/structure/railing{ @@ -9019,9 +8918,6 @@ icon_state = "whiteblue" }, /area/medical/reception) -"bpQ" = ( -/turf/simulated/wall, -/area/civilian/barber) "bpV" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ @@ -9774,6 +9670,29 @@ icon_state = "darkred" }, /area/security/podbay) +"buI" = ( +/obj/item/gun/energy/laser{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/laser, +/obj/item/gun/energy/laser{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/rack/gunrack, +/obj/item/gun/energy/laser, +/obj/machinery/status_display{ + pixel_x = 32 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "buK" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -10965,12 +10884,6 @@ }, /turf/simulated/floor/plating/asteroid, /area/maintenance/cele/medbay) -"bAW" = ( -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "darkredaltstrip" - }, -/area/security/armory) "bAX" = ( /obj/machinery/light{ dir = 8 @@ -11627,19 +11540,6 @@ icon_state = "darkyellow" }, /area/engineering/hardsuitstorage) -"bFm" = ( -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 26 - }, -/obj/structure/dresser, -/obj/machinery/camera/autoname{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bFp" = ( /obj/machinery/computer/security/engineering{ dir = 8 @@ -12107,6 +12007,31 @@ icon_state = "whitepurplecorner" }, /area/medical/research/shallway) +"bHz" = ( +/obj/structure/rack, +/obj/item/storage/box/seccarts{ + pixel_x = 3; + pixel_y = 2 + }, +/obj/item/storage/box/handcuffs, +/obj/item/storage/box/flashbangs{ + pixel_x = -2; + pixel_y = -2 + }, +/obj/item/storage/box/handcuffs, +/obj/item/storage/box/teargas{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "bHB" = ( /obj/structure/flora/rock/jungle, /turf/simulated/floor/glass, @@ -12485,22 +12410,21 @@ icon_state = "whitebluefull" }, /area/medical/cmostore) -"bKc" = ( -/obj/machinery/portable_atmospherics/canister/oxygen, -/obj/structure/railing/corner{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "darkred" - }, -/area/security/armory) "bKd" = ( /obj/structure/dispenser/oxygen, /turf/simulated/floor/plasteel{ icon_state = "dark" }, /area/storage/eva) +"bKf" = ( +/obj/machinery/turretid/stun{ + name = "AI Satellite Turret Control"; + pixel_x = 0; + pixel_y = 28; + req_access = list(75) + }, +/turf/simulated/floor/plating, +/area/turret_protected/aisat) "bKg" = ( /obj/machinery/door/firedoor, /obj/machinery/camera{ @@ -13206,27 +13130,6 @@ }, /turf/simulated/floor/engine/n2, /area/atmos) -"bOj" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/turretid/stun{ - control_area = "AI Satellite Secondary Antechamber"; - name = "AI Satellite Secondary Antechamber Turret Control"; - req_access = list(75); - pixel_x = -30; - pixel_y = -24 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/aisat_interior) "bOk" = ( /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plasteel, @@ -13589,13 +13492,6 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/wood/fancy/light, /area/security/hos) -"bQd" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 9 - }, -/obj/effect/decal/cleanable/dirt, -/turf/simulated/floor/plating, -/area/security/main) "bQg" = ( /obj/effect/decal/warning_stripes/southwest, /obj/effect/decal/warning_stripes/yellow/hollow, @@ -15903,18 +15799,6 @@ icon_state = "darkblue" }, /area/bridge) -"cdq" = ( -/obj/machinery/cryopod/robot, -/obj/effect/landmark/join_late_cyborg, -/obj/item/radio/intercom{ - pixel_y = 22; - pixel_x = 0 - }, -/turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "navyblue" - }, -/area/aisat/maintenance) "cdu" = ( /obj/machinery/firealarm{ dir = 8; @@ -18884,30 +18768,6 @@ }, /turf/simulated/floor/glass, /area/hallway/secondary/entry/south) -"cwM" = ( -/obj/machinery/power/smes{ - charge = 5e+006; - output_level = 150000 - }, -/obj/structure/cable, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/light{ - dir = 4 - }, -/obj/machinery/camera/autoname{ - dir = 8 - }, -/obj/machinery/turretid/lethal{ - check_synth = 1; - name = "AI Chamber Turret Control"; - req_access = list(75); - pixel_x = 32; - pixel_y = 0 - }, -/turf/simulated/floor/glass/reinforced, -/area/turret_protected/ai) "cwP" = ( /obj/structure/chair/office/dark{ dir = 4 @@ -19102,12 +18962,6 @@ icon_state = "darkred" }, /area/security/permabrig) -"cyd" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/armory) "cyi" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/engine, @@ -19698,6 +19552,34 @@ /obj/machinery/constructable_frame/machine_frame, /turf/simulated/floor/plating, /area/maintenance/cele/medbay) +"cCG" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + dir = 2; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/rack, +/obj/item/clothing/suit/armor/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/shield/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "cCI" = ( /obj/machinery/light/small{ dir = 4 @@ -19999,26 +19881,6 @@ icon_state = "darkred" }, /area/security/brig) -"cFr" = ( -/obj/structure/rack, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = -3; - pixel_y = 3 - }, -/turf/simulated/floor/plasteel{ - dir = 6; - icon_state = "darkred" - }, -/area/security/armory) "cFu" = ( /obj/structure/table/glass, /obj/item/storage/box/masks, @@ -21639,23 +21501,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/port) -"cQU" = ( -/obj/machinery/computer/borgupload{ - dir = 1 - }, -/obj/machinery/door/window/eastright{ - dir = 8; - name = "Console Access"; - req_access = list(16) - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "cQV" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/portable_atmospherics/canister/nitrogen, @@ -22076,20 +21921,6 @@ icon_state = "dark" }, /area/turret_protected/ai_upload) -"cUl" = ( -/obj/structure/table/wood, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/item/clipboard, -/obj/item/mining_voucher, -/obj/structure/disposalpipe/segment, -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/bridge/meeting_room) "cUm" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 6 @@ -22393,16 +22224,6 @@ icon_state = "darkbluecorners" }, /area/bridge) -"cVr" = ( -/obj/structure/table/reinforced, -/obj/item/eftpos, -/obj/structure/mirror{ - pixel_x = -26 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "cVs" = ( /obj/structure/bed, /obj/item/bedsheet/clown, @@ -27137,6 +26958,29 @@ icon_state = "whiteyellow" }, /area/assembly/chargebay) +"dLN" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/rack, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "dLQ" = ( /obj/structure/disposalpipe/trunk{ dir = 8 @@ -27547,15 +27391,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/cele/servise) -"dOS" = ( -/obj/machinery/turretid/stun{ - name = "AI Satellite Turret Control"; - req_access = list(75); - pixel_x = 0; - pixel_y = -26 - }, -/turf/simulated/floor/plating, -/area/turret_protected/aisat) "dOW" = ( /obj/effect/spawner/random_spawners/rock_50, /turf/simulated/floor/plating{ @@ -27756,6 +27591,17 @@ /obj/item/target, /turf/simulated/floor/plasteel/airless, /area/toxins/test_area) +"dQV" = ( +/obj/structure/table, +/obj/item/ai_module/crewsimov, +/obj/machinery/flasher{ + id = "AI"; + pixel_y = 21 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "dQY" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -28042,15 +27888,6 @@ icon_state = "white" }, /area/medical/genetics) -"dUh" = ( -/obj/machinery/newscaster/security_unit{ - pixel_x = 32 - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "dUw" = ( /obj/machinery/power/apc{ dir = 1; @@ -28445,6 +28282,30 @@ icon_state = "asteroidplating" }, /area/maintenance/fsmaint3) +"dZr" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/ammo_box/magazine/enforcer/lethal, +/obj/item/ammo_box/magazine/enforcer/lethal, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "dZs" = ( /turf/simulated/wall, /area/storage/tech) @@ -28930,23 +28791,6 @@ /obj/machinery/chem_dispenser, /turf/simulated/floor/engine, /area/toxins/misc_lab) -"ega" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 6 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 6 - }, -/obj/structure/cable/orange{ - icon_state = "2-4" - }, -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/armory) "egg" = ( /obj/machinery/light/small{ dir = 8 @@ -29457,25 +29301,6 @@ icon_state = "neutralcorner" }, /area/toxins/explab_chamber) -"ekN" = ( -/obj/structure/rack, -/obj/item/clothing/suit/armor/laserproof, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/item/gun/energy/ionrifle, -/obj/structure/sign/poster/official/ion_rifle{ - pixel_y = -32 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "ekP" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/warning_stripes/yellow/hollow, @@ -29833,6 +29658,13 @@ icon_state = "whiteyellow" }, /area/medical/medbay) +"epV" = ( +/obj/machinery/suit_storage_unit/security, +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkred" + }, +/area/security/armory) "eqe" = ( /obj/structure/chair/office/light{ dir = 1 @@ -31078,30 +30910,6 @@ color = "gray" }, /area/crew_quarters/bar/atrium) -"eBQ" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/ammo_box/magazine/enforcer/lethal, -/obj/item/ammo_box/magazine/enforcer/lethal, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "eCg" = ( /obj/machinery/computer/monitor{ name = "Sience Power Monitoring Computer" @@ -31267,14 +31075,6 @@ icon_state = "whiteyellow" }, /area/hallway/primary/central) -"eEt" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "eEx" = ( /obj/machinery/door/window{ dir = 2 @@ -32270,6 +32070,27 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/port) +"eOO" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/turretid/stun{ + control_area = "AI Satellite Secondary Antechamber"; + name = "AI Satellite Secondary Antechamber Turret Control"; + pixel_x = -30; + pixel_y = -24; + req_access = list(75) + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/aisat_interior) "eOQ" = ( /obj/machinery/computer/shuttle/mining, /obj/machinery/light{ @@ -32663,6 +32484,12 @@ icon_state = "neutralfull" }, /area/hallway/secondary/entry/south) +"eRY" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "eSd" = ( /obj/effect/spawner/window/reinforced, /obj/structure/transit_tube/horizontal, @@ -35084,6 +34911,40 @@ icon_state = "dark" }, /area/turret_protected/ai) +"frY" = ( +/obj/item/flash, +/obj/item/extraction_pack, +/obj/item/cartridge/quartermaster{ + pixel_x = -3 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -1; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 5; + pixel_y = 3 + }, +/obj/item/megaphone, +/obj/item/fulton_core, +/obj/structure/closet/secure_closet/quartermaster, +/obj/item/clipboard, +/obj/item/stamp/qm{ + pixel_y = 7 + }, +/obj/machinery/power/apc{ + dir = 4; + pixel_x = 28 + }, +/obj/structure/cable/orange{ + icon_state = "0-8" + }, +/obj/item/mining_voucher, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "brown" + }, +/area/quartermaster/qm) "fsi" = ( /obj/structure/dispenser, /obj/machinery/firealarm{ @@ -36154,13 +36015,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/hallway/spacebridge/dockmed) -"fCO" = ( -/obj/machinery/suit_storage_unit/security, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "fCR" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -37963,17 +37817,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/cele/medbay) -"fRS" = ( -/obj/structure/table, -/obj/item/aiModule/nanotrasen, -/obj/structure/cable/orange, -/obj/machinery/power/apc{ - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "fRU" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/external{ @@ -38077,6 +37920,20 @@ icon_state = "fancy-wood-cherry-broken2" }, /area/maintenance/starboard) +"fSB" = ( +/obj/machinery/alarm{ + dir = 1; + pixel_y = -26 + }, +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 1 + }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/port) "fSG" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -38139,6 +37996,16 @@ }, /turf/simulated/floor/plating, /area/medical/virology/lab) +"fTs" = ( +/obj/machinery/portable_atmospherics/canister/oxygen, +/obj/structure/railing/corner{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkred" + }, +/area/security/armory) "fTB" = ( /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -38431,6 +38298,14 @@ icon_state = "neutral" }, /area/crew_quarters/fitness) +"fWf" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkred" + }, +/area/security/armory) "fWi" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -38976,17 +38851,6 @@ }, /turf/simulated/floor/glass/reinforced, /area/security/checkpoint/south) -"gbk" = ( -/obj/structure/cable/orange{ - icon_state = "2-8" - }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "gbp" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -39675,30 +39539,6 @@ }, /turf/simulated/floor/greengrid, /area/engineering/engine/monitor) -"gic" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - dir = 2; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/rack, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/item/shield/riot, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "gie" = ( /obj/machinery/door/airlock/security/glass{ name = "Armory"; @@ -40038,6 +39878,12 @@ icon_state = "dark" }, /area/chapel/office) +"gkV" = ( +/obj/machinery/suit_storage_unit/security, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "gla" = ( /obj/machinery/computer/med_data, /obj/machinery/camera{ @@ -42392,6 +42238,24 @@ }, /turf/simulated/floor/plasteel, /area/crew_quarters/arcade) +"gIu" = ( +/obj/item/gun/energy/gun{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/rack/gunrack, +/obj/item/gun/energy/gun, +/obj/structure/window/reinforced, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "gIv" = ( /obj/machinery/atmospherics/pipe/simple/visible/cyan{ desc = "Труба содержит дыхательную смесь для подачи на станцию"; @@ -44688,6 +44552,13 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/glass, /area/atmos/control) +"hgh" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 9 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/security/main) "hgm" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -44981,6 +44852,12 @@ icon_state = "neutral" }, /area/hallway/secondary/exit) +"hiL" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/ai_module/reset, +/turf/simulated/floor/plating, +/area/storage/tech) "hiU" = ( /obj/structure/reagent_dispensers/water_cooler, /turf/simulated/floor/plasteel{ @@ -46510,31 +46387,6 @@ icon_state = "neutral" }, /area/security/lobby) -"hxu" = ( -/obj/structure/rack, -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "hxE" = ( /obj/machinery/light{ dir = 1 @@ -47189,43 +47041,6 @@ icon_state = "darkredfull" }, /area/security/execution) -"hDF" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/ammo_box/shotgun/buck{ - pixel_x = 3 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/machinery/ai_status_display{ - pixel_y = -32 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "hDG" = ( /obj/structure/holosign/barrier/atmos, /turf/simulated/floor/plating, @@ -48351,12 +48166,6 @@ icon_state = "darkred" }, /area/security/permabrig) -"hPZ" = ( -/obj/machinery/dye_generator, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "hQg" = ( /turf/simulated/floor/carpet/black, /area/chapel/office) @@ -49357,6 +49166,32 @@ icon_state = "darkredfull" }, /area/security/prison/cell_block/A) +"iaU" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/lockbox/mindshield, +/obj/item/storage/box/trackimp, +/obj/item/storage/box/chemimp{ + pixel_x = 4; + pixel_y = 3 + }, +/obj/item/lock_buster, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkred" + }, +/area/security/armory) "iaV" = ( /obj/structure/table/wood, /obj/item/grown/log, @@ -51549,42 +51384,6 @@ icon_state = "darkblue" }, /area/medical/morgue) -"iwn" = ( -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, -/obj/structure/table/glass, -/obj/machinery/door/window{ - dir = 2; - name = "Core Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/ai_status_display{ - pixel_y = 32 - }, -/obj/machinery/light{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) -"iwo" = ( -/obj/machinery/status_display{ - pixel_y = 32 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "iws" = ( /obj/machinery/hologram/holopad, /obj/structure/disposalpipe/junction/reversed, @@ -53631,11 +53430,6 @@ icon_state = "darkpurple" }, /area/teleporter/research) -"iRq" = ( -/turf/simulated/floor/plasteel{ - icon_state = "darkredaltstrip" - }, -/area/security/armory) "iRs" = ( /obj/structure/table, /obj/item/book/manual/security_space_law, @@ -54103,6 +53897,16 @@ icon_state = "whitepurple" }, /area/toxins/test_chamber) +"iVK" = ( +/obj/machinery/alarm{ + dir = 4; + pixel_x = -26 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/armory) "iVW" = ( /obj/machinery/light{ dir = 8 @@ -55535,18 +55339,6 @@ icon_state = "neutralcorner" }, /area/crew_quarters/serviceyard) -"jjf" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkredaltstrip" - }, -/area/security/armory) "jjg" = ( /obj/structure/closet/crate, /obj/effect/spawner/lootdrop/maintenance/tripple, @@ -55881,16 +55673,6 @@ icon_state = "darkyellow" }, /area/engineering/mechanic_workshop/hangar) -"jnc" = ( -/obj/machinery/alarm{ - dir = 4; - pixel_x = -26 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/armory) "jng" = ( /obj/machinery/door/poddoor/shutters/preopen{ dir = 2; @@ -59377,17 +59159,6 @@ icon_state = "caution" }, /area/atmos/distribution) -"jXF" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 9 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkredfull" - }, -/area/security/armory) "jXK" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 10 @@ -60050,29 +59821,6 @@ icon_state = "neutral" }, /area/medical/research) -"kfn" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/light, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "kfo" = ( /obj/machinery/atmospherics/pipe/simple/visible/yellow{ desc = "Труба хранит в себе набор газов для смешивания"; @@ -60518,15 +60266,6 @@ icon_state = "dark" }, /area/toxins/lab) -"kiQ" = ( -/obj/machinery/turretid/stun{ - name = "AI Satellite Turret Control"; - req_access = list(75); - pixel_x = 0; - pixel_y = 28 - }, -/turf/simulated/floor/plating, -/area/turret_protected/aisat) "kiY" = ( /turf/simulated/floor/plasteel{ dir = 9; @@ -61299,20 +61038,6 @@ /obj/effect/spawner/random_spawners/rock_50, /turf/simulated/floor/plating, /area/maintenance/apmaint2) -"kqo" = ( -/obj/structure/sign/directions/floor/alt{ - dir = 8; - pixel_y = 32 - }, -/obj/structure/stairs{ - dir = 4 - }, -/obj/machinery/door/firedoor, -/turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "darkred" - }, -/area/security/armory) "kqy" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 @@ -61736,6 +61461,26 @@ icon_state = "darkyellow" }, /area/bridge) +"kut" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = -3; + pixel_y = 3 + }, +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkred" + }, +/area/security/armory) "kuA" = ( /obj/effect/spawner/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -62912,29 +62657,6 @@ /obj/effect/landmark/event/lightsout, /turf/simulated/floor/wood/fancy/oak, /area/hallway/primary/port) -"kFc" = ( -/obj/item/gun/energy/laser{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/energy/laser, -/obj/item/gun/energy/laser{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/rack/gunrack, -/obj/item/gun/energy/laser, -/obj/machinery/status_display{ - pixel_x = 32 - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "kFk" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, @@ -63571,6 +63293,25 @@ /obj/machinery/seed_extractor, /turf/simulated/floor/grass, /area/hallway/secondary/garden) +"kMt" = ( +/obj/structure/rack, +/obj/item/clothing/suit/armor/laserproof, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/gun/energy/ionrifle, +/obj/structure/sign/poster/official/ion_rifle{ + pixel_y = -32 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "kMx" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating/asteroid, @@ -63751,14 +63492,6 @@ icon_state = "dark" }, /area/engineering/engine) -"kOI" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/universal{ - dir = 4 - }, -/obj/structure/table, -/obj/item/flashlight, -/turf/simulated/floor/plasteel, -/area/storage/primary) "kOL" = ( /obj/structure/grille/broken, /obj/item/stack/rods, @@ -64803,24 +64536,6 @@ icon_state = "whiteblue" }, /area/medical/medbay2) -"kYX" = ( -/obj/structure/rack, -/obj/structure/window/reinforced, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/alt, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "kZb" = ( /obj/structure/table/glass, /obj/item/defibrillator/loaded, @@ -67884,6 +67599,17 @@ icon_state = "navybluealt" }, /area/turret_protected/aisat) +"lFV" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "lGa" = ( /obj/structure/disposalpipe/trunk, /obj/machinery/disposal, @@ -68046,21 +67772,6 @@ /obj/item/reagent_containers/food/snacks/cheesiehonkers, /turf/simulated/floor/grass, /area/hallway/secondary/garden) -"lHu" = ( -/obj/machinery/power/apc{ - dir = 1; - pixel_y = 28 - }, -/obj/structure/cable/orange{ - icon_state = "0-2" - }, -/obj/machinery/camera/autoname, -/obj/structure/dispenser/oxygen, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "lHw" = ( /obj/machinery/light/small{ dir = 1 @@ -68435,6 +68146,29 @@ icon_state = "neutral" }, /area/hallway/primary/central) +"lLa" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "lLe" = ( /obj/machinery/status_display{ pixel_x = 32 @@ -69278,6 +69012,21 @@ icon_state = "neutralfull" }, /area/hallway/primary/central) +"lUQ" = ( +/obj/machinery/power/apc{ + dir = 1; + pixel_y = 28 + }, +/obj/structure/cable/orange{ + icon_state = "0-2" + }, +/obj/machinery/camera/autoname, +/obj/structure/dispenser/oxygen, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "lVc" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -69503,14 +69252,6 @@ icon_state = "dark" }, /area/turret_protected/aisat) -"lXa" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 9; - icon_state = "darkred" - }, -/area/security/armory) "lXd" = ( /turf/simulated/wall/r_wall, /area/toxins/server_coldroom) @@ -70573,27 +70314,6 @@ icon_state = "dark" }, /area/chapel/office) -"mhb" = ( -/obj/structure/rack, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/alt, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "mhc" = ( /obj/structure/girder, /turf/simulated/floor/plating{ @@ -71142,6 +70862,19 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/medical/virology/lab) +"mnq" = ( +/obj/structure/sign/poster/official/random{ + pixel_x = 32 + }, +/obj/machinery/suit_storage_unit/security, +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "mnD" = ( /obj/machinery/door/airlock/public/glass{ name = "Barber Shop" @@ -71996,6 +71729,31 @@ icon_state = "asteroidplating" }, /area/maintenance/genetics) +"mxh" = ( +/obj/structure/rack, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/shield/riot, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + dir = 2; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "mxl" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable/orange{ @@ -73029,6 +72787,25 @@ icon_state = "whiteblue" }, /area/medical/cmostore) +"mFZ" = ( +/obj/machinery/camera/autoname, +/obj/machinery/light{ + dir = 1 + }, +/obj/item/radio/intercom{ + pixel_y = 28 + }, +/obj/structure/railing{ + dir = 4 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "mGb" = ( /obj/structure/ladder, /obj/structure/sign/directions/floor/alt{ @@ -73316,6 +73093,31 @@ icon_state = "whitebluefull" }, /area/maintenance/cele/cargo) +"mIL" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, +/obj/structure/table/glass, +/obj/machinery/door/window{ + dir = 1; + name = "High-Risk Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/ai_status_display{ + pixel_y = -32 + }, +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "mIN" = ( /obj/item/bedsheet/yellow, /obj/structure/bed, @@ -73961,6 +73763,13 @@ icon_state = "neutralfull" }, /area/hallway/primary/fore) +"mOy" = ( +/obj/machinery/dye_generator, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "mOz" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/unary/cold_sink/freezer{ @@ -76232,6 +76041,18 @@ icon_state = "dark" }, /area/quartermaster/storage) +"nmU" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/power/apc{ + pixel_y = -28 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "nmY" = ( /obj/structure/sink{ dir = 1 @@ -76721,6 +76542,14 @@ icon_state = "navybluealt" }, /area/teleporter/quantum/service) +"nsn" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "nsp" = ( /obj/structure/girder, /turf/simulated/floor/plating{ @@ -76839,6 +76668,12 @@ }, /turf/simulated/floor/plasteel, /area/security/prison/cell_block/A) +"ntC" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkredaltstrip" + }, +/area/security/armory) "ntF" = ( /turf/simulated/openspace, /area/engineering/mechanic_workshop) @@ -76846,25 +76681,6 @@ /obj/structure/cable/orange, /turf/simulated/floor/plating, /area/engineering/engine) -"ntJ" = ( -/obj/machinery/camera/autoname, -/obj/machinery/light{ - dir = 1 - }, -/obj/item/radio/intercom{ - pixel_y = 28 - }, -/obj/structure/railing{ - dir = 4 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "ntL" = ( /obj/machinery/light{ dir = 8 @@ -77765,6 +77581,12 @@ icon_state = "white" }, /area/medical/virology/lab) +"nBX" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkredalt" + }, +/area/security/armory) "nCg" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -78310,6 +78132,17 @@ }, /turf/simulated/floor/plasteel/white, /area/medical/research/restroom) +"nIQ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 9 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkredfull" + }, +/area/security/armory) "nIT" = ( /obj/machinery/light/small{ dir = 1 @@ -78891,6 +78724,28 @@ icon_state = "neutralfull" }, /area/shuttle/arrival/station) +"nOA" = ( +/obj/machinery/light{ + dir = 8 + }, +/obj/machinery/camera/autoname{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "2-4" + }, +/obj/machinery/turretid/stun{ + control_area = "AI Satellite Antechamber"; + name = "AI Satellite Antechamber Turret Control"; + pixel_x = -28; + pixel_y = 0; + req_access = list(75) + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "navyblue" + }, +/area/turret_protected/aisat) "nOF" = ( /obj/structure/table/wood, /obj/item/storage/toolbox/mechanical, @@ -78960,13 +78815,6 @@ icon_state = "whitepurple" }, /area/medical/genetics) -"nPL" = ( -/obj/machinery/suit_storage_unit/security, -/turf/simulated/floor/plasteel{ - dir = 6; - icon_state = "darkred" - }, -/area/security/armory) "nPQ" = ( /obj/structure/railing/wooden{ dir = 8 @@ -80582,6 +80430,17 @@ icon_state = "neutral" }, /area/hallway/primary/starboard/north) +"ohm" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/radio/intercom{ + pixel_y = 28 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "ohn" = ( /obj/machinery/light{ dir = 1 @@ -80592,6 +80451,15 @@ icon_state = "whiteblue" }, /area/medical/paramedic) +"ohs" = ( +/obj/machinery/newscaster/security_unit{ + pixel_x = 32 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "ohH" = ( /turf/simulated/mineral/ancient, /area/maintenance/cele/arrival) @@ -80716,12 +80584,6 @@ icon_state = "whitegreen" }, /area/medical/medbay2) -"oiP" = ( -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "ojf" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -80903,6 +80765,21 @@ }, /turf/simulated/floor/carpet, /area/crew_quarters/theatre) +"olx" = ( +/obj/structure/rack, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = -3; + pixel_y = 3 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "oly" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -81872,6 +81749,26 @@ icon_state = "neutralfull" }, /area/medical/research) +"owK" = ( +/obj/structure/rack, +/obj/structure/window/reinforced, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/obj/item/clothing/gloves/color/black/ballistic, +/obj/item/clothing/shoes/jackboots/armored, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "owN" = ( /obj/machinery/embedded_controller/radio/airlock/access_controller{ frequency = 1449; @@ -81962,6 +81859,22 @@ }, /turf/simulated/floor/carpet/red, /area/chapel/main) +"oxt" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/universal{ + dir = 4 + }, +/obj/structure/table, +/obj/item/vending_refill/custom{ + pixel_x = -6; + pixel_y = 5 + }, +/obj/item/vending_refill/custom{ + pixel_x = 3; + pixel_y = 3 + }, +/obj/item/flashlight, +/turf/simulated/floor/plasteel, +/area/storage/primary) "oxI" = ( /obj/structure/window/reinforced, /obj/machinery/disposal, @@ -82462,6 +82375,16 @@ /obj/item/stack/sheet/glass/fifty, /turf/simulated/floor/plating, /area/maintenance/atmospherics) +"oBQ" = ( +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 26 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "oBY" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 @@ -83682,6 +83605,44 @@ icon_state = "whitepurplecorner" }, /area/medical/research/shallway) +"oNb" = ( +/obj/structure/rack, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/head/helmet/riot, +/obj/item/clothing/head/helmet/riot, +/obj/structure/window/reinforced, +/obj/item/clothing/suit/armor/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/shield/riot, +/obj/item/shield/riot, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "oNq" = ( /obj/machinery/alarm{ dir = 4; @@ -83732,10 +83693,6 @@ icon_state = "darkyellow" }, /area/engineering/engine/smes) -"oNQ" = ( -/obj/effect/spawner/window/reinforced, -/turf/simulated/floor/plating, -/area/civilian/barber) "oOd" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -86248,21 +86205,6 @@ icon_state = "whiteblue" }, /area/medical/cloning) -"pkA" = ( -/obj/structure/rack, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = -3; - pixel_y = 3 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "pkP" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -86939,6 +86881,11 @@ icon_state = "whitepurple" }, /area/medical/genetics) +"pqC" = ( +/turf/simulated/floor/plasteel{ + icon_state = "darkredaltstrip" + }, +/area/security/armory) "pqJ" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 4 @@ -87190,6 +87137,31 @@ }, /turf/simulated/floor/plasteel, /area/engineering/chiefs_office) +"psi" = ( +/obj/structure/rack, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "psl" = ( /obj/structure/cable/yellow{ icon_state = "2-8" @@ -87222,31 +87194,6 @@ icon_state = "neutralfull" }, /area/security/lobby) -"psw" = ( -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/shotgun/riot, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/rack/gunrack, -/obj/item/gun/projectile/shotgun/riot, -/obj/structure/window/reinforced, -/obj/machinery/status_display{ - pixel_x = -32 - }, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/armory) "psx" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold/hidden/supply, @@ -87963,39 +87910,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/wood, /area/crew_quarters/cabin4) -"pzy" = ( -/obj/item/flash, -/obj/item/extraction_pack, -/obj/item/cartridge/quartermaster{ - pixel_x = -3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -1; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = 5; - pixel_y = 3 - }, -/obj/item/megaphone, -/obj/item/fulton_core, -/obj/structure/closet/secure_closet/quartermaster, -/obj/item/clipboard, -/obj/item/stamp/qm{ - pixel_y = 7 - }, -/obj/machinery/power/apc{ - dir = 4; - pixel_x = 28 - }, -/obj/structure/cable/orange{ - icon_state = "0-8" - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "brown" - }, -/area/quartermaster/qm) "pzA" = ( /turf/simulated/floor/plasteel{ dir = 4; @@ -89640,24 +89554,6 @@ icon_state = "neutral" }, /area/toxins/misc_lab) -"pQf" = ( -/obj/item/gun/energy/gun{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/energy/gun, -/obj/item/gun/energy/gun{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/rack/gunrack, -/obj/item/gun/energy/gun, -/obj/structure/window/reinforced, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "pQi" = ( /obj/structure/railing, /obj/machinery/door/firedoor/border_only, @@ -90642,9 +90538,33 @@ icon_state = "navyblue" }, /area/turret_protected/ai) +"qaX" = ( +/obj/machinery/turretid/stun{ + name = "AI Satellite Turret Control"; + pixel_x = 0; + pixel_y = -26; + req_access = list(75) + }, +/turf/simulated/floor/plating, +/area/turret_protected/aisat) "qbb" = ( /turf/simulated/floor/carpet/royalblack, /area/crew_quarters/captain/bedroom) +"qbd" = ( +/obj/structure/chair/barber{ + dir = 8 + }, +/obj/structure/cable/orange{ + icon_state = "1-4" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 5 + }, +/obj/structure/disposalpipe/segment, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/port) "qbf" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -91820,6 +91740,13 @@ icon_state = "whiteblue" }, /area/medical/cmostore) +"qmX" = ( +/obj/structure/table/reinforced, +/obj/structure/mirror{ + pixel_x = -26 + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "qmZ" = ( /obj/structure/table/wood, /obj/item/storage/fancy/donut_box, @@ -92220,17 +92147,6 @@ icon_state = "whitegreen" }, /area/medical/virology/lab) -"qqW" = ( -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "qqZ" = ( /obj/effect/spawner/lootdrop/maintenance/double, /turf/simulated/floor/mech_bay_recharge_floor, @@ -92951,12 +92867,6 @@ icon_state = "whitepurplecorner" }, /area/toxins/mixing) -"qyg" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkredalt" - }, -/area/security/armory) "qyj" = ( /obj/structure/stairs{ dir = 4 @@ -92989,32 +92899,6 @@ icon_state = "bar" }, /area/clownoffice) -"qyI" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/lockbox/mindshield, -/obj/item/storage/box/trackimp, -/obj/item/storage/box/chemimp{ - pixel_x = 4; - pixel_y = 3 - }, -/obj/item/lock_buster, -/turf/simulated/floor/plasteel{ - dir = 9; - icon_state = "darkred" - }, -/area/security/armory) "qyW" = ( /obj/structure/railing{ dir = 4 @@ -93324,6 +93208,31 @@ icon_state = "whiteblue" }, /area/medical/patients_rooms) +"qDi" = ( +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/rack/gunrack, +/obj/item/gun/projectile/shotgun/riot, +/obj/structure/window/reinforced, +/obj/machinery/status_display{ + pixel_x = -32 + }, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/armory) "qDv" = ( /obj/machinery/camera/autoname{ dir = 8 @@ -93535,6 +93444,15 @@ icon_state = "blue" }, /area/hallway/primary/starboard/south) +"qEK" = ( +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkred" + }, +/area/security/armory) "qEQ" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 @@ -94687,6 +94605,16 @@ /obj/effect/spawner/airlock/e_to_w, /turf/simulated/wall, /area/maintenance/asmaint2) +"qRm" = ( +/obj/structure/table, +/obj/item/ai_module/reset, +/obj/machinery/firealarm{ + pixel_y = 26 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "qRn" = ( /obj/structure/cable/orange{ icon_state = "1-2" @@ -95363,6 +95291,30 @@ icon_state = "redfull" }, /area/crew_quarters/kitchen) +"qZs" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Security SMG's" + }, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/automatic/wt550, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/gun/projectile/automatic/wt550, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/armory) "qZu" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -95541,6 +95493,16 @@ icon_state = "white" }, /area/medical/genetics) +"raJ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/airlock/maintenance{ + locked = 1; + req_access = list(12) + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "raQ" = ( /obj/machinery/hydroponics/constructable, /obj/machinery/firealarm{ @@ -96811,27 +96773,6 @@ icon_state = "darkbrown" }, /area/maintenance/disposal) -"rnQ" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/rack, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/head/helmet/alt, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "rnX" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -98970,13 +98911,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/carpet/royalblack, /area/crew_quarters/captain/bedroom) -"rIl" = ( -/obj/item/razor, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "rIo" = ( /obj/structure/girder, /turf/simulated/floor/plating, @@ -102080,41 +102014,6 @@ icon_state = "whitepurple" }, /area/toxins/xenobiology) -"sps" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SMG" - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 2; - pixel_y = -2 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 4; - pixel_y = -4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9, -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "darkred" - }, -/area/security/armory) "spB" = ( /turf/simulated/wall/r_wall, /area/storage/tech) @@ -102178,6 +102077,13 @@ icon_state = "dark" }, /area/engineering/engine) +"sqz" = ( +/obj/machinery/suit_storage_unit/security, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "sqB" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 @@ -102894,16 +102800,6 @@ icon_state = "neutralfull" }, /area/storage/primary) -"sxN" = ( -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 26 - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "syc" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -103499,20 +103395,6 @@ }, /turf/simulated/floor/carpet/royalblack, /area/crew_quarters/bar/atrium) -"sDC" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp/green{ - on = 0; - pixel_x = -6; - pixel_y = 14 - }, -/obj/item/storage/fancy/cigarettes/dromedaryco, -/obj/item/clothing/glasses/sunglasses, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/turf/simulated/floor/carpet, -/area/security/detectives_office) "sDP" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -104051,13 +103933,6 @@ }, /turf/simulated/floor/plasteel, /area/security/checkpoint/south) -"sIL" = ( -/obj/structure/table/reinforced, -/obj/item/razor, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "sIT" = ( /obj/structure/window/reinforced{ dir = 4 @@ -104510,16 +104385,6 @@ icon_state = "redfull" }, /area/security/lobby) -"sMY" = ( -/obj/structure/table, -/obj/item/aiModule/corp, -/obj/item/radio/intercom{ - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "sNe" = ( /turf/simulated/floor/plasteel{ dir = 5; @@ -104729,6 +104594,20 @@ color = "gray" }, /area/crew_quarters/bar/atrium) +"sOM" = ( +/obj/structure/sign/directions/floor/alt{ + dir = 8; + pixel_y = 32 + }, +/obj/structure/stairs{ + dir = 4 + }, +/obj/machinery/door/firedoor, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkred" + }, +/area/security/armory) "sOP" = ( /obj/structure/closet/crate, /obj/effect/spawner/lootdrop/maintenance/double, @@ -105367,13 +105246,6 @@ /obj/structure/chair/stool, /turf/simulated/floor/wood/fancy/oak, /area/maintenance/gambling_den2) -"sUZ" = ( -/obj/machinery/suit_storage_unit/security, -/obj/machinery/light, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "sVe" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -105500,6 +105372,12 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/storage/primary) +"sWs" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkred" + }, +/area/security/armory) "sWC" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -105744,21 +105622,6 @@ icon_state = "dark" }, /area/chapel/office) -"sZm" = ( -/obj/machinery/door/airlock/public/glass{ - name = "Barber Shop" - }, -/obj/machinery/door/firedoor, -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "sZp" = ( /obj/structure/cable/orange{ icon_state = "1-2" @@ -106039,17 +105902,6 @@ icon_state = "fancy-wood-oak-broken4" }, /area/civilian/vacantoffice) -"tct" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/light{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "tcu" = ( /obj/structure/closet/secure_closet/personal/patient, /turf/simulated/floor/plasteel{ @@ -106678,11 +106530,28 @@ }, /turf/simulated/floor/plasteel, /area/engineering/chiefs_office) +"tih" = ( +/obj/machinery/hologram/holopad, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkred" + }, +/area/security/armory) "tii" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small, /turf/simulated/floor/plating, /area/maintenance/genetics) +"tis" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 5 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/security/main) "tiy" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -107126,6 +106995,16 @@ icon_state = "darkred" }, /area/security/permabrig) +"tmp" = ( +/obj/structure/table, +/obj/item/ai_module/corp, +/obj/item/radio/intercom{ + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "tmq" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -110146,6 +110025,29 @@ "tOs" = ( /turf/simulated/floor/plating, /area/storage/tech) +"tOt" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "tOx" = ( /obj/machinery/atmospherics/pipe/simple/visible/cyan{ dir = 10 @@ -110164,6 +110066,14 @@ icon_state = "whitered" }, /area/crew_quarters/kitchen) +"tOA" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkredfull" + }, +/area/security/armory) "tOB" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 8 @@ -110239,6 +110149,43 @@ icon_state = "freezerfloor" }, /area/medical/virology/lab) +"tPd" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/ammo_box/shotgun/buck{ + pixel_x = 3 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/machinery/ai_status_display{ + pixel_y = -32 + }, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "tPh" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 8 @@ -113266,6 +113213,11 @@ }, /turf/simulated/floor/plating, /area/maintenance/cele/engineering) +"utC" = ( +/obj/structure/table/reinforced, +/obj/item/razor, +/turf/simulated/floor/plating, +/area/maintenance/port) "utD" = ( /obj/effect/turf_decal/stripes/corner, /obj/effect/turf_decal/stripes/red/corner, @@ -114730,12 +114682,6 @@ icon_state = "whiteblue" }, /area/medical/cmostore) -"uGo" = ( -/obj/machinery/suit_storage_unit/security, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "uGw" = ( /obj/machinery/door/firedoor, /obj/structure/cable/orange{ @@ -115607,6 +115553,34 @@ "uQh" = ( /turf/simulated/floor/wood/fancy/light, /area/security/hos) +"uQm" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, +/obj/structure/table/glass, +/obj/machinery/door/window{ + dir = 2; + name = "Core Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/ai_status_display{ + pixel_y = 32 + }, +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "uQE" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -117679,6 +117653,18 @@ "vlC" = ( /turf/simulated/floor/plating, /area/security/permabrig) +"vlT" = ( +/obj/machinery/cryopod/robot, +/obj/effect/landmark/join_late_cyborg, +/obj/item/radio/intercom{ + pixel_x = 0; + pixel_y = 22 + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "navyblue" + }, +/area/aisat/maintenance) "vlU" = ( /obj/machinery/atmospherics/pipe/simple/visible/yellow{ desc = "Труба хранит в себе набор газов для смешивания"; @@ -117809,36 +117795,6 @@ icon_state = "bluefull" }, /area/hallway/primary/fore) -"vnx" = ( -/obj/structure/rack, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/shield/riot, -/obj/item/shield/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "darkred" - }, -/area/security/armory) "vnN" = ( /obj/machinery/vending/clothing/departament/law, /obj/machinery/light{ @@ -118339,28 +118295,6 @@ }, /turf/simulated/floor/plating, /area/quartermaster/storage) -"vtT" = ( -/obj/machinery/light{ - dir = 8 - }, -/obj/machinery/camera/autoname{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/machinery/turretid/stun{ - control_area = "AI Satellite Antechamber"; - name = "AI Satellite Antechamber Turret Control"; - req_access = list(75); - pixel_x = -28; - pixel_y = 0 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "navyblue" - }, -/area/turret_protected/aisat) "vtZ" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/stairs{ @@ -119959,6 +119893,21 @@ }, /turf/simulated/floor/plating, /area/assembly/chargebay) +"vIK" = ( +/obj/structure/table/wood, +/obj/item/flashlight/lamp/green{ + on = 0; + pixel_x = -6; + pixel_y = 14 + }, +/obj/item/storage/fancy/cigarettes/dromedaryco, +/obj/item/clothing/glasses/sunglasses, +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/obj/item/lighter/zippo/detective, +/turf/simulated/floor/carpet, +/area/security/detectives_office) "vIR" = ( /turf/simulated/floor/plating, /area/maintenance/genetics) @@ -121746,6 +121695,23 @@ "wbx" = ( /turf/simulated/floor/grass, /area/hallway/secondary/garden) +"wbB" = ( +/obj/machinery/camera{ + c_tag = "Mini Satellite Teleporter"; + dir = 1; + network = list("SS13","MiniSat") + }, +/obj/machinery/turretid/stun{ + control_area = "AI Satellite Antechamber"; + name = "AI Satellite Antechamber Turret Control"; + pixel_x = 0; + pixel_y = -26; + req_access = list(75) + }, +/turf/simulated/floor/plasteel{ + icon_state = "navyblue" + }, +/area/aisat/maintenance) "wbI" = ( /obj/structure/chair/sofa/corp{ dir = 1 @@ -122217,6 +122183,15 @@ }, /turf/simulated/floor/carpet/blue, /area/crew_quarters/theatre) +"wgg" = ( +/obj/structure/cable/orange{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plating, +/area/maintenance/port) "wgh" = ( /obj/machinery/portable_atmospherics/scrubber/huge/stationary, /turf/simulated/floor/plating, @@ -122239,15 +122214,6 @@ }, /turf/simulated/floor/plating, /area/bridge/meeting_room) -"wgj" = ( -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "wgm" = ( /obj/structure/cable{ icon_state = "1-2" @@ -122806,18 +122772,16 @@ }, /turf/simulated/floor/plating/airless, /area/solar/west) -"wlC" = ( -/obj/structure/cable/orange, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/power/apc{ - pixel_y = -28 +"wlE" = ( +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 26 }, /turf/simulated/floor/plasteel{ - icon_state = "barber" + dir = 4; + icon_state = "darkred" }, -/area/civilian/barber) +/area/security/armory) "wlI" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -122887,31 +122851,6 @@ icon_state = "darkgreenfull" }, /area/hydroponics) -"wmu" = ( -/obj/structure/sign/poster/official/random{ - pixel_x = 32 - }, -/obj/machinery/suit_storage_unit/security, -/obj/machinery/light{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) -"wmA" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/obj/item/radio/intercom{ - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "wmC" = ( /obj/machinery/door/airlock/research{ name = "Research Division Access"; @@ -123397,16 +123336,6 @@ icon_state = "neutral" }, /area/hallway/secondary/entry/north) -"wrc" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 5 - }, -/obj/effect/decal/cleanable/dirt, -/turf/simulated/floor/plating, -/area/security/main) "wrg" = ( /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -123922,6 +123851,41 @@ icon_state = "navyblue" }, /area/aisat/maintenance) +"wuY" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SMG" + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 4; + pixel_y = -4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkred" + }, +/area/security/armory) "wve" = ( /obj/machinery/chem_heater, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -124317,13 +124281,6 @@ /obj/effect/spawner/lootdrop/maintenance/tripple, /turf/simulated/floor/plating, /area/maintenance/fpmaint) -"wAq" = ( -/obj/machinery/hologram/holopad, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "darkred" - }, -/area/security/armory) "wAt" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 @@ -127425,6 +127382,18 @@ "xeX" = ( /turf/simulated/floor/carpet/purple, /area/crew_quarters/hor) +"xff" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkredaltstrip" + }, +/area/security/armory) "xfl" = ( /obj/machinery/portable_atmospherics/canister/toxins, /obj/effect/decal/cleanable/dirt, @@ -128152,6 +128121,13 @@ }, /turf/simulated/floor/plating, /area/security/prison/cell_block/A) +"xmN" = ( +/obj/machinery/suit_storage_unit/security, +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + icon_state = "darkred" + }, +/area/security/armory) "xmT" = ( /obj/structure/railing{ dir = 4 @@ -130197,30 +130173,6 @@ icon_state = "neutralfull" }, /area/crew_quarters/serviceyard) -"xFV" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Security SMG's" - }, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/automatic/wt550, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/gun/projectile/automatic/wt550, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "darkred" - }, -/area/security/armory) "xFW" = ( /obj/structure/cable/orange{ icon_state = "1-2" @@ -130482,6 +130434,17 @@ icon_state = "navyblue" }, /area/turret_protected/ai) +"xIi" = ( +/obj/structure/table, +/obj/item/ai_module/nanotrasen, +/obj/structure/cable/orange, +/obj/machinery/power/apc{ + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "xIj" = ( /obj/structure/sign/directions/floor/alt{ dir = 6; @@ -130688,31 +130651,6 @@ icon_state = "redcorner" }, /area/security/processing) -"xKk" = ( -/obj/structure/rack, -/obj/item/storage/box/seccarts{ - pixel_x = 3; - pixel_y = 2 - }, -/obj/item/storage/box/handcuffs, -/obj/item/storage/box/flashbangs{ - pixel_x = -2; - pixel_y = -2 - }, -/obj/item/storage/box/handcuffs, -/obj/item/storage/box/teargas{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "darkred" - }, -/area/security/armory) "xKm" = ( /obj/effect/decal/cleanable/dirt, /obj/item/radio/intercom{ @@ -130927,6 +130865,19 @@ }, /turf/simulated/floor/glass/reinforced, /area/turret_protected/ai) +"xMk" = ( +/obj/structure/table/wood, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/item/clipboard, +/obj/structure/disposalpipe/segment, +/obj/structure/cable/orange{ + icon_state = "1-2" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/bridge/meeting_room) "xMp" = ( /obj/effect/spawner/random_spawners/grille_13, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -131569,6 +131520,19 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/brig) +"xSy" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/obj/item/radio/intercom{ + pixel_y = -28 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "xSA" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -131604,6 +131568,30 @@ icon_state = "yellow" }, /area/hallway/secondary/entry/north) +"xSN" = ( +/obj/machinery/power/smes{ + charge = 5e+006; + output_level = 150000 + }, +/obj/structure/cable, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/machinery/camera/autoname{ + dir = 8 + }, +/obj/machinery/turretid/lethal{ + check_synth = 1; + name = "AI Chamber Turret Control"; + pixel_x = 32; + pixel_y = 0; + req_access = list(75) + }, +/turf/simulated/floor/glass/reinforced, +/area/turret_protected/ai) "xSR" = ( /turf/simulated/wall, /area/engineering/hardsuitstorage) @@ -131678,23 +131666,6 @@ icon_state = "neutralcorner" }, /area/hallway/secondary/entry/south) -"xTu" = ( -/obj/machinery/camera{ - c_tag = "Mini Satellite Teleporter"; - dir = 1; - network = list("SS13","MiniSat") - }, -/obj/machinery/turretid/stun{ - control_area = "AI Satellite Antechamber"; - name = "AI Satellite Antechamber Turret Control"; - req_access = list(75); - pixel_x = 0; - pixel_y = -26 - }, -/turf/simulated/floor/plasteel{ - icon_state = "navyblue" - }, -/area/aisat/maintenance) "xTA" = ( /obj/structure/ladder, /obj/structure/cable/orange{ @@ -132899,6 +132870,23 @@ /obj/effect/turf_decal/stripes/line, /turf/simulated/floor/engine, /area/medical/chemistry) +"ygz" = ( +/obj/machinery/computer/aiupload/cyborg{ + dir = 1 + }, +/obj/machinery/door/window/eastright{ + dir = 8; + name = "Console Access"; + req_access = list(16) + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "ygB" = ( /obj/effect/spawner/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -208897,7 +208885,7 @@ bBS yib anc aiZ -kOI +oxt fRn yib gBF @@ -213511,11 +213499,11 @@ nJS bWb ren qwS -bpQ -oNQ -bpQ -oNQ -bpQ +kwS +kwS +kwS +kwS +kwS oYJ oYJ sXn @@ -213768,11 +213756,11 @@ jgv tnS rsa tnS -bpQ -rIl -cVr -sIL -bpQ +kwS +wXU +qmX +utC +kwS isY pXT oqO @@ -214025,11 +214013,11 @@ dWJ tnS lHC uRx -sZm -qqW -acd -beN -bpQ +raJ +wgg +qbd +fSB +kwS mfh vxY eBu @@ -214282,11 +214270,11 @@ uuf mJW oEM vfV -bpQ -iwo -gbk -wlC -bpQ +kwS +uUy +acQ +nmU +kwS bkY woK cix @@ -214539,11 +214527,11 @@ fDL tnS urC nAn -bpQ -hPZ -bFm -wmA -bpQ +kwS +mOy +oBQ +xSy +kwS rSN mzx caO @@ -214796,11 +214784,11 @@ kMG tnS oYt xRO -bpQ -bpQ -bpQ -bpQ -bpQ +kwS +kwS +kwS +kwS +kwS oYJ oYJ hGj @@ -217055,7 +217043,7 @@ dmS wDw dsy wDw -sDC +vIK odS wfN eyQ @@ -218314,14 +218302,14 @@ vfO hau szZ pNG -lXa -ega +fWf +apL pUS doz fOk qAf pxd -ekN +kMt pNG mWc pNG @@ -218571,14 +218559,14 @@ bsG lxy wDe pNG -eEt +nsn qJm vpy -qyg -qyg +nBX +nBX ybD jkR -vnx +oNb pNG mWc pNG @@ -218828,14 +218816,14 @@ mpg jdw rEc pNG -aOM +ohm jgB iWY -hxu -xKk -wAq +psi +bHz +tih eGv -uGo +gkV pNG mWc pNG @@ -219085,14 +219073,14 @@ suP pNG pNG pNG -tct +lFV jgB -cyd -mhb -aVT -cyd +sWs +tOt +mxh +sWs eGv -sUZ +xmN pNG mWc pNG @@ -219342,14 +219330,14 @@ buS pNG mWc pNG -lHu +lUQ fQm iWY -kYX -aVT +owK +mxh iWY eGv -uGo +gkV pNG mWc pNG @@ -219599,14 +219587,14 @@ buS pNG mWc pNG -wgj +qEK jXb -cyd -rnQ -gic -cyd +sWs +dLN +cCG +sWs eGv -uGo +gkV pNG mWc pNG @@ -219856,14 +219844,14 @@ qQo pNG mWc pNG -kqo +sOM mfT fbR ttu ttu vmo tbX -uGo +gkV pNG mWc pNG @@ -220114,13 +220102,13 @@ pNG mWc pNG pNG -bKc -fCO -wmu -sxN +fTs +sqz +mnq +wlE iWY iWY -nPL +epV pNG mWc pNG @@ -226065,7 +226053,7 @@ uXj rOO cRs wwA -bOj +eOO wwA jSS rOO @@ -227346,7 +227334,7 @@ eyp bqK jsv uUx -xTu +wbB kCa gPP vWu @@ -227601,7 +227589,7 @@ miQ hGg wuV kCa -cdq +vlT gms bWX kCa @@ -228115,19 +228103,19 @@ ntZ clk gKY oAd -dOS +qaX gKY vWt roh jvL vzz -vtT +nOA jHo ktZ roh dMW gKY -kiQ +bKf oAd gKY umH @@ -234301,7 +234289,7 @@ aeM aeM kkS dZs -bcK +hiL tOs tOs sdv @@ -236824,7 +236812,7 @@ gFo dIP uod gEz -cUl +xMk oKi oSm qjl @@ -239391,11 +239379,11 @@ aeM aeM qcV qBC -aNl +qRm tUs ayL tUs -sMY +tmp qBC bmg cRH @@ -239648,11 +239636,11 @@ aeM aeM dFy qBC -iwn +uQm uJW mYs cam -bkW +mIL qBC tHA tHA @@ -239905,11 +239893,11 @@ aeM aeM dFy qBC -aWL +dQV tUs avo vrf -fRS +xIi qBC qcV aeM @@ -240163,7 +240151,7 @@ aeM dFy qBC uIC -cQU +ygz axp cUj uIC @@ -251222,7 +251210,7 @@ rLB tLh uyz aRC -pzy +frY aTm rLB ipa @@ -283853,8 +283841,8 @@ sPj sPj lBY sPj -wrc -bQd +tis +hgh ffK aeM aeM @@ -284364,12 +284352,12 @@ aeM oOE hVW pNG -qyI -xFV -psw +iaU +qZs +qDi xBi -jnc -sps +iVK +wuY pNG aeM gUQ @@ -284621,12 +284609,12 @@ aeM ffK ffK pNG -oiP +eRY xJl jRb -jjf -bAW -eBQ +xff +ntC +dZr pNG aeM aeM @@ -284878,12 +284866,12 @@ aeM aeM aeM pNG -ntJ +mFZ ngV kkI -jXF -iRq -hDF +nIQ +pqC +tPd pNG aeM aeM @@ -285138,9 +285126,9 @@ pNG xjn aym mZK -alF -iRq -kfn +tOA +pqC +lLa pNG aeM aeM @@ -285397,7 +285385,7 @@ iUH ucp ucp aFu -pkA +olx pNG aeM aeM @@ -285651,10 +285639,10 @@ aeM pNG rmd puS -dUh -kFc -pQf -cFr +ohs +buI +gIu +kut pNG aeM aeM @@ -287489,7 +287477,7 @@ oVi oVi pbY ppr -cwM +xSN sIb oVi oVi diff --git a/_maps/map_files/cerestation/cerestation.dmm b/_maps/map_files/cerestation/cerestation.dmm index dd37d32655f..6c1aacb38df 100644 --- a/_maps/map_files/cerestation/cerestation.dmm +++ b/_maps/map_files/cerestation/cerestation.dmm @@ -702,6 +702,16 @@ }, /turf/simulated/floor/glass/reinforced, /area/maintenance/fore2) +"agz" = ( +/obj/machinery/light, +/obj/structure/extinguisher_cabinet{ + pixel_y = -28 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "agA" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable/orange{ @@ -1544,6 +1554,31 @@ }, /turf/simulated/floor/plating, /area/turret_protected/aisat_interior/secondary) +"amz" = ( +/obj/structure/rack, +/obj/item/storage/box/seccarts{ + pixel_x = 3; + pixel_y = 2 + }, +/obj/item/storage/box/handcuffs, +/obj/item/storage/box/flashbangs{ + pixel_x = -2; + pixel_y = -2 + }, +/obj/item/storage/box/handcuffs, +/obj/item/storage/box/teargas{ + pixel_x = -3; + pixel_y = -3 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "amA" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -1720,6 +1755,35 @@ icon_state = "darkred" }, /area/bridge) +"aoq" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/lockbox/mindshield, +/obj/item/storage/box/trackimp, +/obj/item/storage/box/chemimp{ + pixel_x = 4; + pixel_y = 3 + }, +/obj/item/lock_buster, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "aou" = ( /obj/machinery/atmospherics/pipe/simple/visible{ dir = 4 @@ -3630,17 +3694,6 @@ icon_state = "dark" }, /area/security/evidence) -"aFA" = ( -/obj/structure/table/reinforced, -/obj/item/razor, -/obj/item/eftpos, -/obj/structure/mirror{ - pixel_x = -27 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aFE" = ( /obj/effect/turf_decal/stripes/red/full, /turf/simulated/floor/engine, @@ -4704,13 +4757,6 @@ icon_state = "darkred" }, /area/security/warden) -"aOM" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "aOS" = ( /obj/machinery/power/terminal{ dir = 4 @@ -5298,32 +5344,6 @@ icon_state = "brown" }, /area/quartermaster/qm) -"aTm" = ( -/obj/item/flash, -/obj/item/extraction_pack, -/obj/item/cartridge/quartermaster{ - pixel_x = -3 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = -1; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = 5; - pixel_y = 3 - }, -/obj/item/megaphone, -/obj/item/fulton_core, -/obj/structure/closet/secure_closet/quartermaster, -/obj/item/clipboard, -/obj/item/stamp/qm{ - pixel_y = 7 - }, -/turf/simulated/floor/plasteel{ - dir = 6; - icon_state = "brown" - }, -/area/quartermaster/qm) "aTn" = ( /obj/structure/closet, /turf/simulated/floor/plating, @@ -5530,15 +5550,6 @@ icon_state = "asteroidplating" }, /area/maintenance/starboard) -"aUW" = ( -/obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Armory_South_East"; - location = "Armory_North" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "aUY" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -5693,15 +5704,6 @@ "aVS" = ( /turf/simulated/wall/r_wall, /area/bridge) -"aVT" = ( -/obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Armory_South"; - location = "Armory_South_East" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "aVW" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -7095,15 +7097,6 @@ /obj/structure/cable/orange, /turf/simulated/floor/plating, /area/security/range) -"beN" = ( -/obj/machinery/light, -/obj/structure/extinguisher_cabinet{ - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "beQ" = ( /obj/structure/table/wood, /obj/item/eftpos, @@ -7114,28 +7107,6 @@ /obj/structure/flora/grass/jungle, /turf/simulated/floor/grass, /area/hallway/secondary/garden) -"beX" = ( -/obj/structure/rack/gunrack, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -7 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 7 - }, -/obj/structure/sign/poster/official/random{ - pixel_x = 32 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "beY" = ( /obj/machinery/hologram/holopad, /obj/structure/cable{ @@ -7975,19 +7946,6 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/crew_quarters/bar) -"bjo" = ( -/obj/machinery/dye_generator, -/obj/machinery/alarm{ - dir = 8; - pixel_x = 24 - }, -/obj/item/radio/intercom{ - pixel_y = 28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bjq" = ( /obj/structure/cable{ icon_state = "4-8" @@ -8274,17 +8232,6 @@ icon_state = "dark" }, /area/quartermaster/storage) -"bkY" = ( -/obj/effect/landmark/start/barber, -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bla" = ( /obj/structure/cable{ icon_state = "4-8" @@ -11171,35 +11118,6 @@ }, /turf/simulated/wall/r_wall, /area/engineering/break_room) -"bBX" = ( -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/shotgun/riot, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/obj/machinery/camera{ - c_tag = "Brig Secure Armory" - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Riot shotguns" - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "bCa" = ( /obj/structure/table/reinforced, /obj/item/paper_bin, @@ -12060,14 +11978,6 @@ color = "gray" }, /area/crew_quarters/bar) -"bGA" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "bGB" = ( /obj/effect/spawner/window/reinforced/polarized{ id = "privateroom" @@ -15127,14 +15037,6 @@ icon_state = "asteroidplating" }, /area/chapel/office) -"bVm" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "bVn" = ( /obj/effect/landmark/ninja_teleport, /turf/simulated/floor/plating{ @@ -17233,31 +17135,6 @@ icon_state = "neutral" }, /area/hallway/primary/port/east) -"cgG" = ( -/obj/structure/rack, -/obj/item/storage/box/seccarts{ - pixel_x = 3; - pixel_y = 2 - }, -/obj/item/storage/box/handcuffs, -/obj/item/storage/box/flashbangs{ - pixel_x = -2; - pixel_y = -2 - }, -/obj/item/storage/box/handcuffs, -/obj/item/storage/box/teargas{ - pixel_x = -3; - pixel_y = -3 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "cgJ" = ( /obj/structure/cable{ icon_state = "4-8" @@ -17279,52 +17156,6 @@ icon_state = "purple" }, /area/hallway/primary/aft/west) -"cgL" = ( -/obj/item/gun/energy/laser{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/energy/laser, -/obj/item/gun/energy/laser{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/obj/structure/rack/gunrack, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) -"cgR" = ( -/obj/item/gun/energy/gun{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/energy/gun, -/obj/item/gun/energy/gun{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/obj/structure/rack/gunrack, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "chc" = ( /obj/structure/cable{ icon_state = "1-2" @@ -17563,14 +17394,6 @@ icon_state = "darkred" }, /area/security/warden) -"cjk" = ( -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "cjm" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -17616,12 +17439,6 @@ icon_state = "darkred" }, /area/security/checkpoint2) -"cjA" = ( -/mob/living/simple_animal/bot/secbot/armsky, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "cjC" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/door/airlock{ @@ -17928,6 +17745,15 @@ }, /turf/simulated/floor/plating, /area/hallway/primary/fore) +"cmz" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/hologram/holopad, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "cmB" = ( /obj/structure/cable/orange{ icon_state = "1-4" @@ -18183,20 +18009,6 @@ /obj/structure/cable/orange, /turf/simulated/floor/plating, /area/security/armory) -"cov" = ( -/obj/machinery/light{ - dir = 4 - }, -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 24 - }, -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "coy" = ( /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, @@ -22445,6 +22257,30 @@ /obj/item/megaphone, /turf/simulated/floor/carpet, /area/magistrateoffice) +"cTj" = ( +/obj/machinery/door/window{ + base_state = "right"; + icon_state = "right"; + name = "Core Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, +/obj/structure/table/glass, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "cTr" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold4w/hidden/supply, @@ -23739,13 +23575,6 @@ "dcw" = ( /turf/simulated/wall/r_wall, /area/hallway/spacebridge/serveng) -"dcy" = ( -/obj/item/razor, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "dcz" = ( /obj/machinery/door/airlock/maintenance/external{ name = "External Airlock Access"; @@ -26174,12 +26003,6 @@ icon_state = "whitepurple" }, /area/toxins/misc_lab) -"dyM" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/aiModule/reset, -/turf/simulated/floor/plating, -/area/storage/tech) "dyN" = ( /obj/machinery/hologram/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -28806,6 +28629,12 @@ /obj/effect/landmark/join_late_cyborg, /turf/simulated/floor/shuttle, /area/shuttle/arrival/station) +"egH" = ( +/obj/structure/chair/barber{ + dir = 8 + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "egM" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 5 @@ -29409,6 +29238,29 @@ /obj/machinery/photocopier, /turf/simulated/floor/wood/fancy/light, /area/library) +"esW" = ( +/obj/item/gun/energy/laser{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/laser, +/obj/item/gun/energy/laser{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/rack/gunrack, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "etf" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -29614,6 +29466,20 @@ slowdown = -0.3 }, /area/hallway/spacebridge/sercom) +"eyp" = ( +/obj/machinery/light{ + dir = 4 + }, +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 24 + }, +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "eyt" = ( /obj/machinery/light/small{ dir = 4 @@ -29640,6 +29506,17 @@ icon_state = "whitebluefull" }, /area/medical/medbay) +"eyC" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 9 + }, +/obj/structure/cable/orange{ + icon_state = "1-2" + }, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plating, +/area/maintenance/port) "eyO" = ( /obj/structure/cable{ icon_state = "1-2" @@ -30426,6 +30303,28 @@ /obj/effect/landmark/event/blobstart, /turf/simulated/floor/wood/fancy/oak, /area/maintenance/gambling_den2) +"eLA" = ( +/obj/item/radio/intercom{ + pixel_y = 23 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Security SMG's" + }, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/automatic/wt550, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = 3; + pixel_y = -3 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "eLI" = ( /obj/machinery/disposal, /obj/structure/disposalpipe/trunk{ @@ -31075,6 +30974,31 @@ /obj/machinery/portable_atmospherics/canister/air, /turf/simulated/floor/plating, /area/maintenance/starboardsolar) +"eWa" = ( +/obj/structure/rack, +/obj/item/storage/backpack/duffel/security/bulletproof_armory{ + pixel_x = -6; + pixel_y = -3 + }, +/obj/item/storage/backpack/duffel/security/bulletproof_armory{ + pixel_x = -3 + }, +/obj/item/storage/backpack/duffel/security/bulletproof_armory{ + pixel_y = 3 + }, +/obj/item/storage/backpack/duffel/security/bulletproof_armory{ + pixel_x = 3; + pixel_y = 6 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "eWj" = ( /obj/structure/closet/secure_closet/brig{ id = "Cell 4"; @@ -33063,6 +32987,33 @@ icon_state = "yellow" }, /area/engineering/mechanic_workshop) +"fEb" = ( +/obj/item/flash, +/obj/item/extraction_pack, +/obj/item/cartridge/quartermaster{ + pixel_x = -3 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = -1; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 5; + pixel_y = 3 + }, +/obj/item/megaphone, +/obj/item/fulton_core, +/obj/structure/closet/secure_closet/quartermaster, +/obj/item/clipboard, +/obj/item/stamp/qm{ + pixel_y = 7 + }, +/obj/item/mining_voucher, +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "brown" + }, +/area/quartermaster/qm) "fEh" = ( /obj/structure/table/wood, /obj/item/reagent_containers/food/drinks/drinkingglass{ @@ -33281,28 +33232,6 @@ "fGN" = ( /turf/simulated/wall, /area/crew_quarters/sleep) -"fGV" = ( -/obj/machinery/door/window{ - dir = 8; - name = "High-Risk Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, -/obj/structure/table/glass, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "fHe" = ( /obj/structure/cable/orange{ icon_state = "2-4" @@ -34113,15 +34042,6 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/hallway/primary/fore) -"fWa" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/effect/landmark/event/lightsout, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "fWe" = ( /obj/structure/cable/orange{ icon_state = "2-8" @@ -35054,6 +34974,17 @@ icon_state = "dark" }, /area/medical/morgue) +"giY" = ( +/obj/machinery/ai_status_display{ + pixel_y = 32 + }, +/obj/structure/table, +/obj/item/ai_module/reset, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "gji" = ( /turf/simulated/floor/wood/fancy/oak{ icon_state = "fancy-wood-oak-broken5" @@ -35691,17 +35622,6 @@ slowdown = -0.3 }, /area/hallway/spacebridge/scidock) -"gvY" = ( -/obj/machinery/ai_status_display{ - pixel_y = 32 - }, -/obj/structure/table, -/obj/item/aiModule/nanotrasen, -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "gwk" = ( /obj/effect/landmark/start/brig_physician, /turf/simulated/floor/plasteel{ @@ -35873,23 +35793,6 @@ }, /turf/simulated/floor/plating, /area/civilian/vacantoffice) -"gyW" = ( -/obj/structure/sign/barber{ - pixel_y = -30 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "neutral" - }, -/area/hallway/primary/port/east) "gza" = ( /obj/machinery/portable_atmospherics/canister/oxygen, /obj/effect/decal/warning_stripes/yellow/hollow, @@ -36017,6 +35920,29 @@ /obj/effect/spawner/random_spawners/grille_50, /turf/simulated/floor/plating/asteroid/ancient, /area/maintenance/fsmaint4) +"gBx" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -6; + pixel_y = 6 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "gBz" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -37135,6 +37061,15 @@ }, /turf/simulated/floor/engine, /area/toxins/mixing) +"gQl" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "gQs" = ( /obj/machinery/vending/coffee, /turf/simulated/floor/plating, @@ -37853,29 +37788,6 @@ icon_state = "rampbottom" }, /area/gateway) -"hbk" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -6; - pixel_y = 6 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "hbo" = ( /obj/structure/rack, /obj/item/pickaxe, @@ -38782,6 +38694,10 @@ icon_state = "dark" }, /area/security/execution) +"hpo" = ( +/obj/structure/table/reinforced, +/turf/simulated/floor/plating, +/area/maintenance/port) "hpr" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 4 @@ -38797,6 +38713,13 @@ }, /turf/simulated/floor/carpet/green, /area/library/game_zone) +"hpJ" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "hpL" = ( /obj/structure/table, /turf/simulated/floor/plasteel{ @@ -39753,6 +39676,18 @@ }, /turf/simulated/floor/plating, /area/maintenance/fpmaint) +"hDQ" = ( +/obj/structure/table, +/obj/item/ai_module/crewsimov, +/obj/machinery/camera{ + c_tag = "AI Upload Chamber"; + dir = 4; + network = list("SS13","RD") + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "hEi" = ( /obj/structure/fence, /obj/structure/cable/orange{ @@ -40331,6 +40266,35 @@ icon_state = "whitepurple" }, /area/toxins/hallway) +"hLS" = ( +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/machinery/camera{ + c_tag = "Brig Secure Armory" + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Riot shotguns" + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "hMs" = ( /obj/structure/table/wood, /obj/item/deck/cards, @@ -41222,33 +41186,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/security/processing) -"hXX" = ( -/obj/structure/rack, -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "hYg" = ( /obj/structure/extinguisher_cabinet{ pixel_y = 28 @@ -41266,15 +41203,6 @@ "hYW" = ( /turf/simulated/mineral/ancient, /area/medical/paramedic) -"hZa" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/hologram/holopad, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "hZh" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -42426,12 +42354,6 @@ icon_state = "redfull" }, /area/security/seceqstorage) -"isY" = ( -/obj/structure/table/reinforced, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "itf" = ( /turf/simulated/mineral/ancient, /area/maintenance/starboard) @@ -42763,6 +42685,30 @@ }, /turf/simulated/floor/plating, /area/hallway/spacebridge/serveng) +"iwD" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/structure/rack, +/obj/item/gun/energy/ionrifle, +/obj/item/clothing/suit/armor/laserproof, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + dir = 1; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/shield/riot, +/obj/item/shield/riot, +/obj/item/shield/riot, +/obj/item/shield/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "iwW" = ( /obj/structure/chair/stool, /obj/effect/landmark/start/janitor, @@ -43743,17 +43689,6 @@ icon_state = "asteroidplating" }, /area/maintenance/starboard) -"iNq" = ( -/obj/machinery/ai_status_display{ - pixel_y = 32 - }, -/obj/structure/table, -/obj/item/aiModule/reset, -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "bcircuit" - }, -/area/turret_protected/ai_upload) "iNt" = ( /obj/machinery/firealarm{ dir = 8; @@ -44107,6 +44042,16 @@ }, /turf/simulated/floor/carpet/royalblack, /area/ntrep) +"iRG" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 5 + }, +/obj/structure/disposalpipe/segment, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "iRM" = ( /obj/machinery/hologram/holopad, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -44153,6 +44098,31 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/wood/fancy/light, /area/security/hos) +"iSu" = ( +/obj/structure/rack, +/obj/item/storage/backpack/duffel/security/riot_armory{ + pixel_x = -6; + pixel_y = -3 + }, +/obj/item/storage/backpack/duffel/security/riot_armory{ + pixel_x = -3 + }, +/obj/item/storage/backpack/duffel/security/riot_armory{ + pixel_y = 3 + }, +/obj/item/storage/backpack/duffel/security/riot_armory{ + pixel_x = 3; + pixel_y = 6 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "iSG" = ( /obj/structure/noticeboard{ dir = 8; @@ -46073,6 +46043,15 @@ icon_state = "dark" }, /area/atmos) +"juz" = ( +/obj/machinery/navbeacon{ + codes_txt = "patrol;next_patrol=Armory_South"; + location = "Armory_South_East" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "juB" = ( /obj/structure/cable{ icon_state = "1-2" @@ -49262,35 +49241,6 @@ icon_state = "dark" }, /area/toxins/lab) -"kqo" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/lockbox/mindshield, -/obj/item/storage/box/trackimp, -/obj/item/storage/box/chemimp{ - pixel_x = 4; - pixel_y = 3 - }, -/obj/item/lock_buster, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "kqs" = ( /turf/simulated/floor/plasteel{ icon_state = "darkbluecorners" @@ -50995,10 +50945,6 @@ icon_state = "neutral" }, /area/hallway/primary/port/south) -"kOk" = ( -/obj/effect/spawner/window/reinforced, -/turf/simulated/floor/plating, -/area/civilian/barber) "kOr" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -54021,40 +53967,6 @@ icon_state = "neutral" }, /area/hallway/primary/port/south) -"lHu" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SMG" - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 2; - pixel_y = -2 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 4; - pixel_y = -4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/machinery/alarm{ - pixel_y = 26 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "lHw" = ( /obj/machinery/light/small{ dir = 1 @@ -55421,6 +55333,13 @@ }, /turf/simulated/floor/plating, /area/security/permabrig) +"mar" = ( +/obj/machinery/suit_storage_unit/security, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "mau" = ( /obj/machinery/firealarm{ dir = 1; @@ -55806,11 +55725,6 @@ icon_state = "whitepurplecorner" }, /area/toxins/xenobiology) -"mfh" = ( -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "mfA" = ( /obj/structure/cable{ icon_state = "1-2" @@ -55939,36 +55853,6 @@ icon_state = "red" }, /area/security/lobby) -"mhb" = ( -/obj/structure/rack, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/item/clothing/gloves/color/black/ballistic, -/obj/item/clothing/gloves/color/black/ballistic, -/obj/item/clothing/gloves/color/black/ballistic, -/obj/item/clothing/gloves/color/black/ballistic, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/shoes/jackboots/armored, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "mhs" = ( /obj/machinery/door/airlock/public/glass{ name = "Central Access" @@ -58538,6 +58422,9 @@ }, /turf/simulated/floor/plating/asteroid/ancient, /area/mine/unexplored/cere/orbiting) +"mUz" = ( +/turf/space, +/area/security/armory) "mUI" = ( /obj/structure/extinguisher_cabinet{ pixel_y = 32 @@ -60249,18 +60136,6 @@ icon_state = "asteroidplating" }, /area/maintenance/apmaint2) -"ntb" = ( -/obj/structure/table, -/obj/item/aiModule/crewsimov, -/obj/machinery/camera{ - c_tag = "AI Upload Chamber"; - dir = 4; - network = list("SS13","RD") - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "ntg" = ( /obj/machinery/atm{ pixel_y = -32 @@ -60847,6 +60722,16 @@ /obj/structure/closet/wardrobe/xenos, /turf/simulated/floor/shuttle, /area/shuttle/arrival/station) +"nBh" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/airlock/maintenance{ + locked = 1; + req_access = list(12) + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "nBn" = ( /obj/machinery/firealarm{ dir = 4; @@ -61079,6 +60964,14 @@ }, /turf/simulated/floor/plating, /area/storage/tech) +"nEO" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "nEU" = ( /obj/machinery/keycard_auth{ pixel_x = -24 @@ -61963,18 +61856,6 @@ icon_state = "neutral" }, /area/hallway/primary/port/south) -"nQC" = ( -/obj/machinery/newscaster{ - dir = 8; - pixel_x = 28 - }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "nQM" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/extinguisher_cabinet{ @@ -62761,6 +62642,28 @@ icon_state = "whiteblue" }, /area/medical/surgery/theatre) +"obe" = ( +/obj/machinery/door/window{ + dir = 8; + name = "High-Risk Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, +/obj/structure/table/glass, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "obh" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -63463,6 +63366,15 @@ }, /turf/simulated/floor/carpet/cyan, /area/crew_quarters/fitness) +"old" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/effect/landmark/event/lightsout, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "olg" = ( /obj/machinery/door/airlock/maintenance{ name = "Docking Asteroid SMES Access"; @@ -63613,6 +63525,28 @@ }, /turf/simulated/floor/plasteel, /area/security/permabrig) +"ooy" = ( +/obj/structure/rack/gunrack, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -7 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 7 + }, +/obj/structure/sign/poster/official/random{ + pixel_x = 32 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "ooA" = ( /turf/simulated/floor/grass, /area/hallway/secondary/garden) @@ -64344,6 +64278,17 @@ /obj/effect/decal/cleanable/blood/old, /turf/simulated/floor/plasteel/freezer, /area/crew_quarters/kitchen) +"oAt" = ( +/obj/machinery/ai_status_display{ + pixel_y = 32 + }, +/obj/structure/table, +/obj/item/ai_module/nanotrasen, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "bcircuit" + }, +/area/turret_protected/ai_upload) "oAv" = ( /obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ @@ -65234,9 +65179,6 @@ icon_state = "white" }, /area/medical/medbreak) -"oOg" = ( -/turf/simulated/wall, -/area/civilian/barber) "oOu" = ( /obj/structure/morgue{ dir = 8 @@ -65246,6 +65188,12 @@ icon_state = "dark" }, /area/medical/morgue) +"oOv" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 4 + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "oOD" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/effect/turf_decal/stripes/line, @@ -66860,25 +66808,6 @@ icon_state = "neutralcorner" }, /area/hallway/primary/port/south) -"pkO" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 6 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10 - }, -/obj/machinery/computer/borgupload{ - dir = 1 - }, -/obj/machinery/door/window/eastright{ - dir = 1; - name = "Console Access"; - req_access = list(16) - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "pkP" = ( /obj/structure/cable{ icon_state = "0-8" @@ -66906,6 +66835,29 @@ "pls" = ( /turf/simulated/mineral/ancient, /area/maintenance/fore) +"plA" = ( +/obj/item/gun/energy/gun{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/rack/gunrack, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "pma" = ( /obj/structure/extinguisher_cabinet{ pixel_y = -27 @@ -68301,6 +68253,14 @@ /obj/structure/girder, /turf/simulated/floor/plating/asteroid/ancient, /area/maintenance/atmospherics) +"pEI" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "pEP" = ( /obj/structure/extinguisher_cabinet{ pixel_y = 30 @@ -69686,6 +69646,31 @@ }, /turf/simulated/floor/plating, /area/maintenance/port) +"pZE" = ( +/obj/structure/table, +/obj/item/vending_refill/custom{ + pixel_x = 4; + pixel_y = 5 + }, +/obj/item/vending_refill/custom{ + pixel_x = -2; + pixel_y = 3 + }, +/obj/structure/cable/orange{ + icon_state = "0-4" + }, +/obj/item/stack/cable_coil, +/obj/item/stack/cable_coil, +/obj/machinery/power/apc{ + dir = 8; + pixel_x = -24 + }, +/obj/item/multitool, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutral" + }, +/area/storage/primary) "pZK" = ( /obj/machinery/computer/scan_consolenew{ dir = 8 @@ -69806,6 +69791,16 @@ icon_state = "neutral" }, /area/hallway/primary/port/east) +"qbW" = ( +/obj/structure/table, +/obj/structure/sign/kiddieplaque{ + pixel_x = 32 + }, +/obj/item/ai_module/corp, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "qbZ" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 8 @@ -70311,28 +70306,6 @@ icon_state = "darkyellow" }, /area/engineering/engine/smes) -"qhx" = ( -/obj/item/radio/intercom{ - pixel_y = 23 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Security SMG's" - }, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/automatic/wt550, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = 3; - pixel_y = -3 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "qhP" = ( /obj/machinery/alarm{ dir = 1; @@ -72010,6 +71983,18 @@ icon_state = "darkyellow" }, /area/engineering/chiefs_office) +"qGo" = ( +/obj/structure/table/wood, +/obj/item/flashlight/lamp/green{ + on = 0; + pixel_x = -6; + pixel_y = 14 + }, +/obj/item/storage/fancy/cigarettes/dromedaryco, +/obj/item/clothing/glasses/sunglasses, +/obj/item/lighter/zippo/detective, +/turf/simulated/floor/carpet, +/area/security/detectives_office) "qGz" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/cyan{ dir = 1; @@ -72358,6 +72343,12 @@ icon_state = "darkred" }, /area/security/reception) +"qLP" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/ai_module/reset, +/turf/simulated/floor/plating, +/area/storage/tech) "qMi" = ( /obj/structure/closet, /obj/effect/spawner/lootdrop/maintenance, @@ -74041,14 +74032,6 @@ icon_state = "dark" }, /area/security/prison/cell_block/A) -"rnQ" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "rnT" = ( /obj/structure/chair/stool/bar{ dir = 4 @@ -74508,41 +74491,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/fsmaint3) -"rud" = ( -/obj/structure/rack, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1; - layer = 2.9 - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun/buck, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "rug" = ( /obj/structure/disposalpipe/junction/reversed{ dir = 8 @@ -76277,16 +76225,6 @@ }, /turf/simulated/floor/plating, /area/magistrateoffice) -"rSN" = ( -/obj/machinery/firealarm{ - dir = 4; - pixel_x = 24 - }, -/obj/structure/dresser, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "rTo" = ( /obj/machinery/portable_atmospherics/canister/carbon_dioxide, /obj/machinery/light{ @@ -77050,6 +76988,17 @@ icon_state = "darkbrown" }, /area/quartermaster/office) +"scK" = ( +/obj/machinery/dye_generator, +/obj/machinery/alarm{ + dir = 8; + pixel_x = 24 + }, +/obj/item/radio/intercom{ + pixel_y = 28 + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "scU" = ( /obj/structure/cable{ icon_state = "1-2" @@ -78190,6 +78139,41 @@ slowdown = -0.3 }, /area/hallway/spacebridge/engmed) +"suG" = ( +/obj/structure/rack, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun/buck, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "suP" = ( /obj/structure/sign/poster/official/space_a{ pixel_y = 32 @@ -78370,16 +78354,6 @@ icon_state = "dark" }, /area/toxins/lab) -"sxv" = ( -/obj/structure/table, -/obj/structure/sign/kiddieplaque{ - pixel_x = 32 - }, -/obj/item/aiModule/corp, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "sxw" = ( /obj/effect/decal/cleanable/blood/tracks, /turf/simulated/floor/plating/asteroid/ancient, @@ -78669,17 +78643,6 @@ icon_state = "blue" }, /area/hallway/primary/fore) -"sDC" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp/green{ - on = 0; - pixel_x = -6; - pixel_y = 14 - }, -/obj/item/storage/fancy/cigarettes/dromedaryco, -/obj/item/clothing/glasses/sunglasses, -/turf/simulated/floor/carpet, -/area/security/detectives_office) "sDE" = ( /obj/machinery/conveyor{ dir = 8; @@ -78975,16 +78938,6 @@ icon_state = "whitegreen" }, /area/medical/virology/lab) -"sIL" = ( -/obj/structure/table/reinforced, -/obj/machinery/light_switch{ - dir = 1; - pixel_y = -24 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "sJe" = ( /obj/structure/cable{ icon_state = "1-2" @@ -79404,6 +79357,16 @@ /obj/effect/spawner/random_spawners/blood_5, /turf/simulated/floor/plating, /area/turret_protected/aisat_interior/secondary) +"sOt" = ( +/obj/machinery/firealarm{ + dir = 4; + pixel_x = 24 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "sOu" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ @@ -79840,6 +79803,27 @@ icon_state = "fancy-wood-oak-broken5" }, /area/maintenance/gambling_den2) +"sUJ" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SP-91-RC"; + req_access = list(1) + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 8 + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 4 + }, +/obj/item/ammo_box/magazine/sp91rc, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = -4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "sUQ" = ( /obj/effect/turf_decal/stripes/line, /obj/machinery/atmospherics/pipe/simple/visible/cyan{ @@ -80367,27 +80351,6 @@ icon_state = "neutralfull" }, /area/hallway/primary/aft/east) -"teI" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SP-91-RC"; - req_access = list(1) - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 8 - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 4 - }, -/obj/item/ammo_box/magazine/sp91rc, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = -4 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "teL" = ( /obj/machinery/hologram/holopad, /obj/structure/cable{ @@ -81159,6 +81122,12 @@ }, /turf/simulated/floor/plating, /area/maintenance/disposal/west) +"tom" = ( +/mob/living/simple_animal/bot/secbot/armsky, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "tow" = ( /obj/machinery/camera{ c_tag = "Medbay Asteroid Hallway 4"; @@ -84658,21 +84627,6 @@ "uqz" = ( /turf/simulated/wall, /area/security/main) -"uqB" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/door/airlock/public/glass{ - name = "Barber Shop" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/machinery/door/firedoor, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "uqM" = ( /obj/structure/sign/security{ pixel_x = -32 @@ -85521,6 +85475,40 @@ icon_state = "neutral" }, /area/hallway/primary/fore) +"uDb" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SMG" + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 4; + pixel_y = -4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/machinery/alarm{ + pixel_y = 26 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "uDv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -85668,13 +85656,6 @@ icon_state = "whitepurple" }, /area/toxins/lab) -"uGo" = ( -/obj/machinery/suit_storage_unit/security, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "uGA" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -86092,23 +86073,6 @@ /obj/effect/landmark/start/civilian, /turf/simulated/floor/carpet, /area/crew_quarters/locker) -"uMQ" = ( -/obj/structure/table, -/obj/structure/cable/orange{ - icon_state = "0-4" - }, -/obj/item/stack/cable_coil, -/obj/item/stack/cable_coil, -/obj/machinery/power/apc{ - dir = 8; - pixel_x = -24 - }, -/obj/item/multitool, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "neutral" - }, -/area/storage/primary) "uNh" = ( /obj/machinery/shower{ dir = 4 @@ -86128,14 +86092,6 @@ "uNn" = ( /turf/simulated/floor/plating, /area/maintenance/disposal/east) -"uNw" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "uNx" = ( /obj/structure/extinguisher_cabinet{ pixel_x = 24 @@ -87540,41 +87496,6 @@ icon_state = "brown" }, /area/quartermaster/qm) -"vnx" = ( -/obj/structure/rack, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/shield/riot, -/obj/item/shield/riot, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/armory) "vnM" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -87836,6 +87757,16 @@ /obj/effect/landmark/event/xeno_spawn, /turf/simulated/floor/plating/asteroid/ancient, /area/maintenance/apmaint2) +"vrC" = ( +/obj/machinery/light{ + dir = 4 + }, +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "vrQ" = ( /turf/simulated/floor/plating, /area/maintenance/fore2) @@ -87854,6 +87785,19 @@ icon_state = "whitegreen" }, /area/medical/virology/lab) +"vsa" = ( +/obj/machinery/power/apc{ + pixel_y = -24 + }, +/obj/structure/cable/orange, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "vsg" = ( /obj/machinery/atmospherics/pipe/simple/heat_exchanging, /obj/machinery/atmospherics/air_sensor{ @@ -88247,15 +88191,6 @@ }, /turf/simulated/floor/carpet/red, /area/crew_quarters/courtroom) -"vxY" = ( -/obj/structure/chair/barber{ - dir = 8 - }, -/obj/effect/landmark/start/barber, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "vyy" = ( /obj/machinery/newscaster{ dir = 4; @@ -88357,18 +88292,6 @@ icon_state = "neutralcorner" }, /area/hallway/primary/central) -"vzG" = ( -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 5 - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "vzX" = ( /obj/effect/spawner/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -89114,19 +89037,6 @@ icon_state = "red" }, /area/hallway/secondary/entry) -"vLS" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 9 - }, -/obj/structure/cable/orange{ - icon_state = "1-2" - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "vLV" = ( /obj/effect/spawner/airlock/e_to_w, /turf/simulated/wall, @@ -91010,18 +90920,6 @@ color = "gray" }, /area/crew_quarters/bar) -"wlC" = ( -/obj/machinery/power/apc{ - pixel_y = -24 - }, -/obj/structure/cable/orange, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "wlV" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 4 @@ -91060,15 +90958,6 @@ /obj/item/toy/figure/captain, /turf/simulated/floor/carpet/black, /area/crew_quarters/captain) -"wmA" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "wmE" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -91658,6 +91547,14 @@ icon_state = "dark" }, /area/security/execution) +"wuA" = ( +/obj/structure/table/reinforced, +/obj/item/razor, +/obj/structure/mirror{ + pixel_x = -27 + }, +/turf/simulated/floor/plating, +/area/maintenance/port) "wuC" = ( /obj/structure/chair/office/dark{ dir = 1 @@ -91679,6 +91576,33 @@ icon_state = "white" }, /area/medical/cmostore) +"wvj" = ( +/obj/structure/rack, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/structure/window/reinforced{ + dir = 1; + layer = 2.9 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/armory) "wvp" = ( /turf/simulated/floor/plasteel{ icon_state = "white" @@ -92767,6 +92691,25 @@ /obj/structure/girder, /turf/simulated/floor/plating, /area/maintenance/fsmaint4) +"wKZ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 6 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 10 + }, +/obj/machinery/computer/aiupload/cyborg{ + dir = 1 + }, +/obj/machinery/door/window/eastright{ + dir = 1; + name = "Console Access"; + req_access = list(16) + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "wLe" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -94126,16 +94069,6 @@ /obj/structure/grille/broken, /turf/simulated/floor/plating/asteroid/ancient, /area/maintenance/engineering) -"xgR" = ( -/obj/machinery/light{ - dir = 4 - }, -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "xhc" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -94687,6 +94620,14 @@ icon_state = "dark" }, /area/security/podbay) +"xpH" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "xpO" = ( /obj/machinery/atmospherics/binary/pump{ dir = 4; @@ -94698,30 +94639,6 @@ /obj/machinery/ai_status_display, /turf/simulated/wall/r_wall, /area/toxins/rdoffice) -"xpT" = ( -/obj/machinery/door/window{ - base_state = "right"; - icon_state = "right"; - name = "Core Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, -/obj/structure/table/glass, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "xqm" = ( /obj/machinery/vending/cola, /turf/simulated/floor/plasteel, @@ -94960,6 +94877,15 @@ /obj/item/coin/clown, /turf/simulated/floor/plating, /area/clownoffice/secret) +"xuf" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/port) "xut" = ( /turf/simulated/floor/carpet/blue, /area/hallway/secondary/garden) @@ -95108,6 +95034,14 @@ }, /turf/simulated/floor/plating, /area/medical/paramedic) +"xwA" = ( +/obj/structure/cable/orange{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plating, +/area/maintenance/port) "xwI" = ( /turf/simulated/floor/engine{ slowdown = -0.3 @@ -95243,6 +95177,14 @@ slowdown = -0.3 }, /area/hallway/spacebridge/serveng) +"xyk" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "xyo" = ( /obj/structure/rack, /obj/item/tank/jetpack/carbondioxide{ @@ -95996,6 +95938,15 @@ icon_state = "asteroidplating" }, /area/security/permabrig) +"xHu" = ( +/obj/machinery/navbeacon{ + codes_txt = "patrol;next_patrol=Armory_South_East"; + location = "Armory_North" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/armory) "xHB" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -96092,26 +96043,6 @@ }, /turf/simulated/floor/engine, /area/engineering/engine) -"xJl" = ( -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/structure/rack, -/obj/item/gun/energy/ionrifle, -/obj/item/clothing/suit/armor/laserproof, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/door/window{ - dir = 1; - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/armory) "xJn" = ( /obj/effect/turf_decal/stripes/line{ dir = 10; @@ -97056,9 +96987,6 @@ icon_state = "darkyellow" }, /area/engineering/mechanic_workshop/hangar) -"xYM" = ( -/turf/space, -/area/security/armory) "xYT" = ( /obj/structure/cable/orange{ icon_state = "4-8" @@ -112603,7 +112531,7 @@ boG bqe brA aYV -uMQ +pZE vka brA xIM @@ -114915,12 +114843,12 @@ fGN xsr kzm nXl -oOg -oOg -kOk -oOg -kOk -oOg +pXy +pXy +pXy +pXy +pXy +pXy aZZ oGW aZZ @@ -115172,12 +115100,12 @@ fGN tmw jBM txu -kOk -dcy -isY -aFA -sIL -oOg +pXy +jee +hpo +wuA +hpo +pXy dNF wlu liD @@ -115428,13 +115356,13 @@ pYw rVw tmw jBM -gyW -oOg -uNw -mfh -vxY -beN -oOg +txu +pXy +oOv +jee +egH +agz +pXy gCy bTu cpP @@ -115686,12 +115614,12 @@ rVw cSb aTi pRd -uqB -vLS -bkY -vzG -wlC -oOg +nBh +eyC +xwA +iRG +vsa +pXy xUc ogY xLH @@ -115943,12 +115871,12 @@ rVw tmw jBM kwE -oOg -bjo -rSN -nQC -wmA -oOg +pXy +scK +sOt +gQl +xuf +pXy dHb iio kSA @@ -117706,7 +117634,7 @@ dmS cEU dib glb -sDC +qGo odS wfN tPa @@ -119738,14 +119666,14 @@ qYY hri rIP bBH -qhx +eLA ioE -fWa -hXX -cgG -bGA +old +wvj +amz +pEI ioE -xJl +iwD bBH eXi ljF @@ -119995,14 +119923,14 @@ bgr euE rFa bBH -lHu +uDb ioE -bVm -mhb -cgL -hZa +xpH +eWa +esW +cmz ioE -vnx +gBx bBH lii vmO @@ -120252,14 +120180,14 @@ wxl wOD nQM bBH -bBX +hLS ioE -rnQ -hbk -cgR -cjk +xyk +iSu +plA +nEO ioE -uGo +mar bBH kMh nuI @@ -120509,14 +120437,14 @@ bDn psy wOD dkF -rud +suG ioE -aUW +xHu ioE -aVT -cjA +juz +tom ioE -uGo +mar bBH lkp ljr @@ -120766,14 +120694,14 @@ brk psy aBy bBH -aOM -xgR -kqo -beX -teI -uGo -cov -aOM +hpJ +vrC +aoq +ooy +sUJ +mar +eyp +hpJ bBH lii rfX @@ -121280,14 +121208,14 @@ myx wOD tSH bBH -xYM -xYM -xYM -xYM -xYM -xYM -xYM -xYM +mUz +mUz +mUz +mUz +mUz +mUz +mUz +mUz bBH jLQ udM @@ -122937,8 +122865,8 @@ ccW ful ful bHT -ntb -xpT +hDQ +cTj knG ful ful @@ -123193,7 +123121,7 @@ rWw ccW ful ful -iNq +giY cfd pKY cfd @@ -123709,7 +123637,7 @@ ful ful joJ wpc -pkO +wKZ qVA vCk nHm @@ -124221,7 +124149,7 @@ ccW ccW ful ful -gvY +oAt cfd mMI cfd @@ -124479,8 +124407,8 @@ ccW ful ful qNN -sxv -fGV +qbW +obe knG ful ful @@ -134683,7 +134611,7 @@ xbO eDx cSf dqI -dyM +qLP baR xfN vQx @@ -147249,7 +147177,7 @@ tLh aRC pzy uyz -aTm +fEb iIK fZf rMQ diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 9342f86ef13..15134615a45 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -132,28 +132,6 @@ icon_state = "dark" }, /area/security/securearmory) -"acj" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/camera{ - c_tag = "Brig Security Equipment Lockers"; - dir = 4 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/obj/machinery/status_display{ - layer = 4; - pixel_x = -32 - }, -/obj/machinery/light{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "aco" = ( /obj/structure/target_stake, /obj/effect/decal/warning_stripes/northwest, @@ -177,20 +155,6 @@ /obj/effect/decal/warning_stripes/northeast, /turf/simulated/floor/plasteel, /area/security/range) -"acv" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "acw" = ( /obj/structure/cable{ icon_state = "1-2" @@ -227,33 +191,6 @@ icon_state = "darkredfull" }, /area/security/main) -"acB" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/grenade/barrier{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/grenade/barrier, -/obj/item/grenade/barrier{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/grenade/barrier{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "acD" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/simulated/floor/plasteel{ @@ -331,58 +268,12 @@ }, /turf/simulated/floor/plating, /area/maintenance/apmaint) -"acO" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/lockbox/spy_kit, -/obj/item/storage/lockbox/mindshield, -/obj/item/storage/box/trackimp, -/obj/item/storage/box/chemimp{ - pixel_x = 4; - pixel_y = 3 - }, -/obj/item/lock_buster, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "acP" = ( /obj/effect/decal/warning_stripes/red/hollow, /turf/simulated/floor/plasteel{ icon_state = "redfull" }, /area/security/range) -"acR" = ( -/obj/machinery/flasher/portable, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "vault" - }, -/area/security/securearmory) -"acS" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/flasher/portable, -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "vault" - }, -/area/security/securearmory) "acV" = ( /obj/structure/table/reinforced, /obj/item/folder/red{ @@ -395,13 +286,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel, /area/security/main) -"acW" = ( -/obj/structure/dispenser/oxygen, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "acX" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/emcloset, @@ -410,12 +294,6 @@ }, /turf/simulated/floor/plasteel, /area/security/prisonershuttle) -"acY" = ( -/obj/machinery/suit_storage_unit/security, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "acZ" = ( /obj/structure/rack, /obj/machinery/light{ @@ -431,15 +309,6 @@ icon_state = "whiteblue" }, /area/security/medbay) -"ada" = ( -/obj/machinery/suit_storage_unit/security, -/obj/machinery/light{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "adb" = ( /obj/structure/cable{ d2 = 2; @@ -453,23 +322,6 @@ }, /turf/simulated/floor/plating, /area/security/hos) -"adg" = ( -/obj/machinery/portable_atmospherics/canister/oxygen, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) -"adh" = ( -/obj/machinery/computer/mech_bay_power_console, -/obj/effect/decal/warning_stripes/yellow/hollow, -/obj/machinery/firealarm{ - name = "north fire alarm"; - pixel_y = 24 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "adi" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ @@ -477,30 +329,6 @@ }, /turf/simulated/floor/carpet, /area/security/hos) -"adj" = ( -/obj/machinery/light{ - dir = 1 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/alarm{ - pixel_y = 24 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) -"adk" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "adl" = ( /obj/structure/table, /obj/item/taperecorder, @@ -511,15 +339,6 @@ }, /turf/simulated/floor/plasteel, /area/security/main) -"adm" = ( -/obj/machinery/mech_bay_recharge_port{ - dir = 8 - }, -/obj/effect/decal/warning_stripes/yellow/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "adn" = ( /obj/structure/curtain/open/shower/security{ anchored = 1 @@ -575,28 +394,6 @@ icon_state = "whiteblue" }, /area/security/medbay) -"adt" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Security SMG's" - }, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/projectile/automatic/wt550, -/obj/item/gun/projectile/automatic/wt550{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "adu" = ( /obj/structure/closet/secure_closet/brigdoc, /obj/machinery/camera{ @@ -644,20 +441,6 @@ }, /turf/simulated/floor/engine, /area/security/podbay) -"adx" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/machinery/light{ - dir = 4 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "adD" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ @@ -722,18 +505,6 @@ "adO" = ( /turf/simulated/wall/r_wall, /area/security/medbay) -"adQ" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "adR" = ( /obj/structure/chair/comfy/red{ dir = 1 @@ -861,12 +632,6 @@ icon_state = "whiteblue" }, /area/security/medbay) -"aeo" = ( -/turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "vault" - }, -/area/security/securearmory) "aep" = ( /obj/machinery/status_display{ layer = 4; @@ -900,16 +665,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel, /area/security/main) -"aev" = ( -/obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Armory_South"; - location = "Armory_sleva" - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "aew" = ( /obj/structure/cable{ icon_state = "4-8" @@ -1021,17 +776,6 @@ icon_state = "darkred" }, /area/security/warden) -"aeR" = ( -/obj/item/radio/intercom{ - name = "custom station intercom (General)"; - pixel_x = 28; - pixel_y = 28 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "aeT" = ( /obj/structure/chair/office/dark, /obj/structure/cable{ @@ -1620,34 +1364,6 @@ icon_state = "whiteblue" }, /area/security/medbay) -"ahD" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/machinery/light{ - dir = 4 - }, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "ahE" = ( /obj/structure/cable{ icon_state = "4-8" @@ -1768,19 +1484,6 @@ icon_state = "darkred" }, /area/security/brig) -"ahY" = ( -/obj/structure/table/reinforced, -/obj/item/restraints/handcuffs, -/obj/item/restraints/handcuffs{ - pixel_y = -4 - }, -/obj/item/crowbar, -/obj/item/radio/sec, -/turf/simulated/floor/plasteel{ - dir = 10; - icon_state = "darkredfull" - }, -/area/security/main) "ahZ" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel, @@ -1849,33 +1552,6 @@ "aih" = ( /turf/simulated/wall/r_wall, /area/security/hos) -"aik" = ( -/obj/structure/rack/gunrack, -/obj/item/gun/energy/laser{ - pixel_x = -2; - pixel_y = 3 - }, -/obj/item/gun/energy/laser, -/obj/item/gun/energy/laser{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/camera{ - c_tag = "Secure Armory East"; - dir = 8; - network = list("SS13","Security") - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "ail" = ( /obj/structure/table/reinforced, /obj/item/gun/energy/laser/practice, @@ -2358,33 +2034,6 @@ /obj/structure/sign/nosmoking_2, /turf/simulated/wall/r_wall, /area/security/podbay) -"akf" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/obj/item/storage/box/flashbangs, -/obj/item/storage/box/flashbangs{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/storage/box/teargas{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/storage/box/teargas, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "aki" = ( /obj/structure/cable{ icon_state = "1-2" @@ -2397,31 +2046,6 @@ icon_state = "darkredcorners" }, /area/security/prison/cell_block/A) -"akl" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/ammo_box/shotgun/beanbag, -/obj/item/ammo_box/shotgun/beanbag{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun/tranquilizer{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "akm" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ @@ -2435,28 +2059,6 @@ icon_state = "darkredcorners" }, /area/security/brig) -"akn" = ( -/obj/item/ammo_box/shotgun/buck{ - pixel_x = 3 - }, -/obj/item/ammo_box/shotgun/buck{ - pixel_y = 3 - }, -/obj/item/ammo_box/shotgun{ - pixel_x = -3; - pixel_y = 6 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal Bullets" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "ako" = ( /obj/effect/decal/warning_stripes/east, /turf/simulated/floor/engine/vacuum, @@ -2475,32 +2077,6 @@ icon_state = "darkredcorners" }, /area/security/brig) -"akt" = ( -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -1; - pixel_y = 3 - }, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -1 - }, -/obj/item/gun/projectile/shotgun/riot{ - pixel_x = -1; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Riot shotguns" - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "akv" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ @@ -2748,37 +2324,6 @@ /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/simulated/floor/plasteel, /area/security/prisonlockers) -"ale" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SMG" - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 6; - pixel_y = -6 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 4; - pixel_y = -4 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = 2; - pixel_y = -2 - }, -/obj/item/ammo_box/magazine/wt550m9, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -2; - pixel_y = 2 - }, -/obj/item/ammo_box/magazine/wt550m9{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/structure/window/reinforced, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "alh" = ( /obj/structure/table, /obj/item/reagent_containers/food/drinks/cans/beer{ @@ -2995,10 +2540,6 @@ icon_state = "dark" }, /area/security/prisonershuttle) -"alU" = ( -/obj/structure/sign/security, -/turf/simulated/wall/r_wall, -/area/security/securearmory) "alW" = ( /obj/machinery/autolathe/security, /obj/item/radio/intercom{ @@ -3009,53 +2550,6 @@ icon_state = "escape" }, /area/security/customs2) -"alZ" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) -"ama" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/machinery/camera{ - c_tag = "Secure Armory West"; - dir = 4; - network = list("SS13","Security") - }, -/obj/item/clothing/suit/armor/laserproof, -/obj/item/gun/energy/ionrifle, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) -"amb" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 6 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "amc" = ( /obj/machinery/status_display{ layer = 4; @@ -3096,17 +2590,6 @@ icon_state = "red" }, /area/security/main) -"amh" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 5 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "amm" = ( /obj/machinery/hologram/holopad, /obj/structure/cable{ @@ -3144,22 +2627,6 @@ /obj/effect/spawner/lootdrop/officetoys, /turf/simulated/floor/carpet, /area/security/hos) -"amv" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/item/radio/intercom{ - name = "south station intercom (General)"; - pixel_y = -28 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "amx" = ( /obj/machinery/access_button{ command = "cycle_interior"; @@ -3217,26 +2684,6 @@ icon_state = "vault" }, /area/security/main) -"amK" = ( -/obj/structure/rack/gunrack, -/obj/item/gun/energy/gun{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/gun/energy/gun, -/obj/item/gun/energy/gun{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "amL" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on, /turf/simulated/floor/plasteel, @@ -3545,6 +2992,35 @@ }, /turf/simulated/floor/wood, /area/lawoffice) +"any" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/lockbox/mindshield, +/obj/item/storage/box/trackimp, +/obj/item/storage/box/chemimp{ + pixel_x = 4; + pixel_y = 3 + }, +/obj/item/lock_buster, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "anz" = ( /obj/machinery/computer/secure_data, /turf/simulated/floor/plasteel{ @@ -4161,30 +3637,6 @@ }, /turf/simulated/floor/plasteel/dark, /area/tcommsat/chamber) -"apy" = ( -/obj/structure/window/reinforced, -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Magazines for SP-91-RC"; - req_access = list(1) - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 8 - }, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = 4 - }, -/obj/item/ammo_box/magazine/sp91rc, -/obj/item/ammo_box/magazine/sp91rc{ - pixel_x = -4 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "apE" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable{ @@ -4979,33 +4431,6 @@ icon_state = "dark" }, /area/security/evidence) -"arG" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/obj/machinery/light{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "arH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -5017,31 +4442,6 @@ icon_state = "darkred" }, /area/security/permabrig) -"arI" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "arJ" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 @@ -7664,31 +7064,6 @@ icon_state = "darkred" }, /area/security/permabrig) -"ayE" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "ayH" = ( /obj/structure/table/reinforced, /obj/item/flashlight/lamp, @@ -9258,16 +8633,6 @@ "aDN" = ( /turf/simulated/wall, /area/crew_quarters/mrchangs) -"aDP" = ( -/obj/machinery/dye_generator, -/obj/machinery/alarm{ - dir = 8; - pixel_x = 24 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aDQ" = ( /obj/structure/table, /obj/item/folder/blue{ @@ -9372,23 +8737,6 @@ "aEl" = ( /turf/simulated/wall, /area/crew_quarters/courtroom) -"aEm" = ( -/obj/structure/table/wood, -/obj/item/flashlight/lamp/green{ - on = 0; - pixel_x = -3; - pixel_y = 8 - }, -/obj/item/storage/fancy/cigarettes/dromedaryco, -/obj/item/radio/intercom/department/security{ - name = "east station intercom (Security)"; - pixel_x = 28 - }, -/obj/item/reagent_containers/food/drinks/flask/detflask, -/turf/simulated/floor/plasteel{ - icon_state = "grimy" - }, -/area/security/detectives_office) "aEn" = ( /obj/effect/spawner/window/reinforced, /obj/structure/cable, @@ -10073,15 +9421,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/fsmaint) -"aGG" = ( -/obj/structure/table/reinforced, -/obj/machinery/computer/security/telescreen/entertainment{ - pixel_x = -32 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aGI" = ( /obj/structure/weightmachine/stacklifter, /obj/effect/decal/cleanable/dirt, @@ -10089,20 +9428,6 @@ icon_state = "dark" }, /area/security/permabrig) -"aGK" = ( -/obj/machinery/power/apc{ - dir = 1; - name = "north bump"; - pixel_y = 24 - }, -/obj/structure/cable{ - d2 = 2; - icon_state = "0-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aGL" = ( /obj/structure/chair/comfy/black, /obj/effect/landmark/start/civilian, @@ -10506,19 +9831,6 @@ "aIc" = ( /turf/simulated/wall, /area/clownoffice) -"aId" = ( -/obj/structure/table/reinforced, -/obj/item/razor, -/obj/structure/mirror{ - pixel_x = -28 - }, -/obj/machinery/light{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aIe" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ dir = 1 @@ -10561,16 +9873,6 @@ icon_state = "redcorner" }, /area/security/main) -"aIi" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/effect/landmark/start/barber, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aIj" = ( /obj/structure/chair{ dir = 8 @@ -10985,19 +10287,6 @@ icon_state = "darkblue" }, /area/security/detectives_office) -"aJp" = ( -/obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_y = 5 - }, -/obj/item/pen, -/obj/machinery/computer/security/telescreen/entertainment{ - pixel_x = -32 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aJr" = ( /obj/structure/closet/secure_closet/brig, /obj/machinery/light_switch{ @@ -11385,27 +10674,6 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/plating, /area/maintenance/fsmaint) -"aKE" = ( -/obj/machinery/newscaster{ - name = "east newscaster"; - pixel_x = 32; - pixel_y = 1 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 10 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/light{ - dir = 4 - }, -/obj/machinery/hologram/holopad, -/obj/structure/cable{ - icon_state = "1-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "aKF" = ( /obj/structure/table, /turf/simulated/floor/carpet/arcade, @@ -12001,6 +11269,17 @@ }, /turf/simulated/floor/carpet/arcade, /area/crew_quarters/arcade) +"aMI" = ( +/obj/item/radio/intercom{ + name = "custom station intercom (General)"; + pixel_x = 28; + pixel_y = 28 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "aMJ" = ( /obj/machinery/light_switch{ name = "west light switch"; @@ -12886,18 +12165,6 @@ icon_state = "whitegreenfull" }, /area/crew_quarters/sleep) -"aPP" = ( -/obj/structure/sign/barber{ - pixel_y = 30 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "neutralcorner" - }, -/area/crew_quarters/dorms) "aPQ" = ( /obj/effect/decal/warning_stripes/south, /obj/structure/sign/poster/official/space_a{ @@ -22868,16 +22135,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/fsmaint) -"buF" = ( -/obj/structure/table/reinforced, -/obj/structure/mirror{ - pixel_x = -28 - }, -/obj/item/razor, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "buG" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -22925,21 +22182,6 @@ icon_state = "bluecorner" }, /area/hydroponics) -"buL" = ( -/obj/structure/chair/barber{ - dir = 8 - }, -/obj/machinery/status_display{ - layer = 4; - pixel_y = 32 - }, -/obj/machinery/camera{ - c_tag = "Barber Shop" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "buM" = ( /obj/machinery/vending/cart, /turf/simulated/floor/carpet/arcade, @@ -23660,13 +22902,6 @@ "bxl" = ( /turf/simulated/wall, /area/bridge/meeting_room) -"bxm" = ( -/obj/structure/table, -/obj/item/aiModule/reset, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "bxn" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -23721,28 +22956,6 @@ }, /turf/simulated/floor/wood, /area/crew_quarters/captain) -"bxt" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) -"bxu" = ( -/obj/structure/dresser, -/obj/machinery/firealarm{ - dir = 4; - name = "east fire alarm"; - pixel_x = 24 - }, -/obj/machinery/light{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bxv" = ( /turf/simulated/wall/r_wall, /area/crew_quarters/captain) @@ -24190,13 +23403,6 @@ /obj/machinery/vending/snack, /turf/simulated/floor/wood, /area/bridge/meeting_room) -"byK" = ( -/obj/structure/table, -/obj/item/aiModule/quarantine, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "byL" = ( /obj/structure/window/reinforced{ dir = 8 @@ -24354,12 +23560,6 @@ icon_state = "neutral" }, /area/hallway/secondary/exit) -"bzm" = ( -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bzo" = ( /obj/structure/chair/stool, /turf/simulated/floor/carpet/arcade, @@ -24809,14 +24009,6 @@ icon_state = "darkred" }, /area/hallway/primary/fore) -"bAH" = ( -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bAK" = ( /obj/structure/cable{ icon_state = "1-2" @@ -24992,28 +24184,6 @@ /obj/structure/falsewall, /turf/simulated/floor/plating, /area/maintenance/livingcomplex) -"bBw" = ( -/obj/structure/table, -/obj/machinery/door/window{ - base_state = "right"; - icon_state = "right"; - name = "Core Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, -/turf/simulated/floor/bluegrid, -/area/turret_protected/ai_upload) "bBx" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -25032,26 +24202,6 @@ }, /turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) -"bBz" = ( -/obj/structure/table, -/obj/machinery/door/window{ - dir = 8; - name = "High-Risk Modules"; - req_access = list(20) - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, -/turf/simulated/floor/bluegrid, -/area/turret_protected/ai_upload) "bBA" = ( /obj/machinery/light, /turf/simulated/floor/plasteel{ @@ -25307,14 +24457,6 @@ }, /turf/simulated/floor/bluegrid, /area/maintenance/engineering) -"bCy" = ( -/obj/structure/table, -/obj/item/aiModule/protectStation, -/obj/item/aiModule/nanotrasen, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "bCA" = ( /obj/machinery/status_display{ pixel_y = 32 @@ -25728,13 +24870,6 @@ icon_state = "dark" }, /area/turret_protected/ai_upload) -"bDW" = ( -/obj/structure/table, -/obj/item/aiModule/freeform, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/turret_protected/ai_upload) "bDX" = ( /turf/simulated/wall, /area/maintenance/maintcentral) @@ -26191,20 +25326,6 @@ /obj/structure/grille, /turf/simulated/floor/plating, /area/maintenance/fsmaint) -"bFL" = ( -/obj/item/radio/intercom{ - name = "east station intercom (General)"; - pixel_x = 28 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable{ - icon_state = "1-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bFM" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/oil/streak, @@ -27018,19 +26139,6 @@ icon_state = "whiteblue" }, /area/medical/reception) -"bIs" = ( -/obj/machinery/disposal, -/obj/machinery/light_switch{ - name = "west light switch"; - pixel_x = -24 - }, -/obj/structure/disposalpipe/trunk{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bIt" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -27055,17 +26163,6 @@ icon_state = "neutralcorner" }, /area/hallway/secondary/exit) -"bIz" = ( -/obj/machinery/atm{ - pixel_y = -32 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bIB" = ( /obj/machinery/atmospherics/pipe/simple/visible{ dir = 4 @@ -27095,19 +26192,6 @@ icon_state = "browncorner" }, /area/hallway/primary/central/west) -"bIL" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "bIM" = ( /obj/structure/table/wood, /obj/item/folder/blue, @@ -28097,6 +27181,22 @@ /obj/item/flash, /turf/simulated/floor/plasteel, /area/assembly/robotics) +"bLQ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/structure/disposalpipe/segment, +/obj/machinery/door/airlock/maintenance{ + locked = 1; + req_access = list(12) + }, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "bLR" = ( /obj/structure/cable{ icon_state = "2-4" @@ -29824,6 +28924,19 @@ icon_state = "white" }, /area/toxins/lab) +"bRR" = ( +/obj/machinery/navbeacon{ + codes_txt = "patrol;next_patrol=Armory_North"; + location = "Armory_sprava" + }, +/obj/machinery/light, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 9 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "bRT" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 5 @@ -34293,6 +33406,21 @@ icon_state = "arrival" }, /area/hallway/secondary/entry) +"cem" = ( +/obj/machinery/power/apc{ + dir = 1; + name = "north bump"; + pixel_y = 24 + }, +/obj/structure/cable{ + d2 = 2; + icon_state = "0-2" + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "ceo" = ( /obj/structure/cable{ icon_state = "1-2" @@ -36448,32 +35576,6 @@ }, /turf/simulated/floor/wood, /area/blueshield) -"ckq" = ( -/obj/structure/closet/secure_closet/quartermaster, -/obj/item/extraction_pack, -/obj/item/fulton_core, -/obj/item/flash, -/obj/item/clipboard, -/obj/item/cartridge/quartermaster{ - pixel_x = -4; - pixel_y = 7 - }, -/obj/item/cartridge/quartermaster{ - pixel_x = 6; - pixel_y = 5 - }, -/obj/item/cartridge/quartermaster, -/obj/item/megaphone{ - pixel_x = 7 - }, -/obj/item/stamp/qm{ - pixel_x = 7; - pixel_y = 3 - }, -/turf/simulated/floor/plasteel{ - icon_state = "brown" - }, -/area/quartermaster/qm) "ckr" = ( /obj/item/reagent_containers/food/snacks/grown/banana, /turf/simulated/floor/plating, @@ -40923,14 +40025,6 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/engineering/controlroom) -"czC" = ( -/obj/structure/table, -/obj/machinery/alarm{ - pixel_y = 24 - }, -/obj/item/t_scanner, -/turf/simulated/floor/plasteel, -/area/storage/primary) "czD" = ( /obj/structure/table, /obj/machinery/status_display{ @@ -42130,12 +41224,6 @@ icon_state = "vault" }, /area/security/nuke_storage) -"cCM" = ( -/obj/structure/table, -/obj/item/aicard, -/obj/item/aiModule/reset, -/turf/simulated/floor/plating, -/area/storage/tech) "cCN" = ( /obj/structure/rack{ dir = 8; @@ -46491,6 +45579,28 @@ icon_state = "freezerfloor" }, /area/crew_quarters/kitchen) +"cON" = ( +/obj/structure/rack/gunrack, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -7 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = -2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 2 + }, +/obj/item/gun/projectile/automatic/sp91rc{ + pixel_x = 7 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "cOQ" = ( /obj/machinery/alarm{ dir = 4; @@ -51010,18 +50120,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/escapepodbay) -"dbn" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "dbo" = ( /mob/living/simple_animal/hostile/killertomato{ desc = "Прирученный ботаниками томат-убийца. Не подпускать к Сане."; @@ -56518,6 +55616,31 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/quartermaster/office) +"dyO" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "dyZ" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ @@ -56726,6 +55849,15 @@ }, /turf/simulated/floor/plasteel, /area/hallway/primary/central/sw) +"dHi" = ( +/obj/machinery/mech_bay_recharge_port{ + dir = 8 + }, +/obj/effect/decal/warning_stripes/yellow/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "dHB" = ( /obj/structure/table, /obj/item/clothing/glasses/science, @@ -56956,6 +56088,32 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint) +"dOf" = ( +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -1; + pixel_y = 3 + }, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -1 + }, +/obj/item/gun/projectile/shotgun/riot{ + pixel_x = -1; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Riot shotguns" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "dOm" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 10 @@ -56992,6 +56150,28 @@ }, /turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) +"dOZ" = ( +/obj/item/ammo_box/shotgun/buck{ + pixel_x = 3 + }, +/obj/item/ammo_box/shotgun/buck{ + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun{ + pixel_x = -3; + pixel_y = 6 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal Bullets" + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "dPX" = ( /obj/structure/cable{ icon_state = "1-4" @@ -57878,6 +57058,13 @@ icon_state = "darkbluecorners" }, /area/medical/cmo) +"etU" = ( +/obj/structure/table, +/obj/item/ai_module/reset, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "eua" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/public/glass{ @@ -58014,6 +57201,17 @@ }, /turf/simulated/floor/engine, /area/toxins/mixing) +"eBc" = ( +/obj/structure/mirror{ + pixel_x = -28 + }, +/obj/effect/decal/cleanable/dirt, +/obj/item/paper_bin{ + pixel_y = 5 + }, +/obj/item/pen, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "eBd" = ( /obj/item/reagent_containers/glass/beaker/large, /obj/item/reagent_containers/dropper, @@ -58033,6 +57231,28 @@ icon_state = "freezerfloor" }, /area/crew_quarters/locker/locker_toilet) +"eBW" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/camera{ + c_tag = "Brig Security Equipment Lockers"; + dir = 4 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/obj/machinery/status_display{ + layer = 4; + pixel_x = -32 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "eCc" = ( /obj/machinery/alarm{ dir = 4; @@ -58359,6 +57579,33 @@ icon_state = "vault" }, /area/engineering/mechanic_workshop) +"eKz" = ( +/obj/structure/rack/gunrack, +/obj/item/gun/energy/laser{ + pixel_x = -2; + pixel_y = 3 + }, +/obj/item/gun/energy/laser, +/obj/item/gun/energy/laser{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/camera{ + c_tag = "Secure Armory East"; + dir = 8; + network = list("SS13","Security") + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "eKD" = ( /obj/effect/spawner/random_spawners/oil_20, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ @@ -58687,6 +57934,13 @@ }, /turf/simulated/floor/plasteel, /area/hallway/primary/starboard/west) +"eVr" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/flasher/portable, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "eVW" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock{ @@ -58829,6 +58083,28 @@ }, /turf/space, /area/space) +"faz" = ( +/obj/structure/table, +/obj/machinery/door/window{ + base_state = "right"; + icon_state = "right"; + name = "Core Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, +/turf/simulated/floor/bluegrid, +/area/turret_protected/ai_upload) "fbk" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -58867,17 +58143,6 @@ icon_state = "whitegreen" }, /area/medical/virology/lab) -"fbY" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10 - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "fcS" = ( /obj/structure/cable{ icon_state = "1-2" @@ -59279,6 +58544,18 @@ }, /turf/simulated/floor/plating, /area/aisat/atmospherics) +"fvE" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "fvW" = ( /obj/structure/cable{ icon_state = "1-4" @@ -59852,6 +59129,13 @@ icon_state = "neutral" }, /area/security/customs) +"fPD" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "fPG" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 10 @@ -59904,6 +59188,17 @@ icon_state = "whitegreencorner" }, /area/medical/virology) +"fQR" = ( +/obj/machinery/computer/mech_bay_power_console, +/obj/effect/decal/warning_stripes/yellow/hollow, +/obj/machinery/firealarm{ + name = "north fire alarm"; + pixel_y = 24 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "fRf" = ( /obj/structure/cable{ icon_state = "4-8" @@ -59954,13 +59249,6 @@ icon_state = "bluefull" }, /area/medical/cmostore) -"fSC" = ( -/obj/effect/decal/warning_stripes/red/hollow, -/obj/machinery/flasher/portable, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "fSG" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/generic, @@ -60236,6 +59524,12 @@ icon_state = "dark" }, /area/maintenance/livingcomplex) +"gbD" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "gbG" = ( /obj/structure/cable{ icon_state = "1-4" @@ -60293,9 +59587,6 @@ }, /turf/simulated/floor/plasteel, /area/hallway/primary/starboard/west) -"gel" = ( -/turf/simulated/wall, -/area/civilian/barber) "gff" = ( /obj/structure/closet/crate/trashcart{ desc = "A heavy, metal laundrycart with wheels."; @@ -60790,6 +60081,15 @@ /obj/effect/decal/cleanable/blood/xeno, /turf/simulated/floor/plasteel, /area/maintenance/xenozoo) +"grV" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "grY" = ( /obj/machinery/disposal, /obj/structure/disposalpipe/trunk{ @@ -60919,6 +60219,40 @@ }, /turf/simulated/floor/plasteel, /area/atmos/control) +"gwc" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Lethal bullets" + }, +/obj/item/ammo_box/c9mm, +/obj/item/ammo_box/c9mm{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/magazine/enforcer/lethal{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "gwj" = ( /obj/machinery/status_display{ layer = 4; @@ -60977,6 +60311,20 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/apmaint) +"gxq" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "gxr" = ( /obj/structure/table, /obj/effect/spawner/lootdrop/maintenance, @@ -61164,6 +60512,34 @@ icon_state = "wood-broken" }, /area/maintenance/asmaint) +"gFy" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "gFC" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/generic, @@ -61219,6 +60595,22 @@ /obj/effect/decal/cleanable/dust, /turf/simulated/floor/plasteel, /area/maintenance/apmaint) +"gGV" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/item/radio/intercom{ + name = "south station intercom (General)"; + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "gID" = ( /obj/machinery/light{ dir = 8 @@ -62014,6 +61406,24 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/assembly/assembly_line) +"hkL" = ( +/obj/structure/table/wood, +/obj/item/flashlight/lamp/green{ + on = 0; + pixel_x = -3; + pixel_y = 8 + }, +/obj/item/storage/fancy/cigarettes/dromedaryco, +/obj/item/radio/intercom/department/security{ + name = "east station intercom (Security)"; + pixel_x = 28 + }, +/obj/item/reagent_containers/food/drinks/flask/detflask, +/obj/item/lighter/zippo/detective, +/turf/simulated/floor/plasteel{ + icon_state = "grimy" + }, +/area/security/detectives_office) "hkT" = ( /obj/machinery/door/airlock/external{ id_tag = "specops_home"; @@ -62947,6 +62357,37 @@ /obj/machinery/door/firedoor, /turf/simulated/floor/plasteel, /area/hallway/primary/central/se) +"hRQ" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SMG" + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 4; + pixel_y = -4 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/item/ammo_box/magazine/wt550m9, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ammo_box/magazine/wt550m9{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/structure/window/reinforced, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "hSk" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table/wood, @@ -63290,28 +62731,6 @@ icon_state = "tranquillite" }, /area/mimeoffice) -"icF" = ( -/obj/structure/rack/gunrack, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -7 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = -2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 2 - }, -/obj/item/gun/projectile/automatic/sp91rc{ - pixel_x = 7 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "icM" = ( /obj/machinery/door/airlock/external{ frequency = 1379; @@ -63691,6 +63110,18 @@ icon_state = "dark" }, /area/turret_protected/ai) +"itT" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "iuC" = ( /obj/structure/table/reinforced, /obj/machinery/door_control{ @@ -64153,14 +63584,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/port) -"iHY" = ( -/obj/structure/chair/barber{ - dir = 8 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "iIh" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/hatch{ @@ -64439,6 +63862,18 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/engine, /area/maintenance/server) +"iOJ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "iOM" = ( /obj/structure/sink{ dir = 8; @@ -64775,6 +64210,31 @@ icon_state = "brown" }, /area/quartermaster/miningdock) +"jcP" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jdp" = ( /obj/structure/railing, /turf/space, @@ -64979,6 +64439,33 @@ icon_state = "white" }, /area/toxins/explab) +"jlu" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/machinery/camera{ + c_tag = "Secure Armory West"; + dir = 4; + network = list("SS13","Security") + }, +/obj/item/clothing/suit/armor/laserproof, +/obj/item/gun/energy/ionrifle, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "jlG" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 @@ -65855,6 +65342,19 @@ icon_state = "dark" }, /area/medical/morgue) +"jVz" = ( +/obj/structure/chair/barber{ + dir = 8 + }, +/obj/machinery/status_display{ + layer = 4; + pixel_y = 32 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "jVL" = ( /obj/effect/decal/warning_stripes/north, /obj/machinery/atmospherics/binary/pump{ @@ -66251,6 +65751,12 @@ icon_state = "floorgrime" }, /area/maintenance/asmaint2) +"kiJ" = ( +/obj/structure/table/reinforced, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "kiK" = ( /obj/structure/sign/biohazard{ pixel_y = 32 @@ -66449,6 +65955,26 @@ /obj/effect/decal/cleanable/dust, /turf/simulated/floor/plating, /area/maintenance/apmaint) +"kpg" = ( +/obj/structure/rack/gunrack, +/obj/item/gun/energy/gun{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/energy/gun, +/obj/item/gun/energy/gun{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "kpo" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -66516,6 +66042,21 @@ icon_state = "yellowcorner" }, /area/hallway/primary/aft) +"kqL" = ( +/obj/structure/table, +/obj/machinery/alarm{ + pixel_y = 24 + }, +/obj/item/vending_refill/custom{ + pixel_x = 5; + pixel_y = 5 + }, +/obj/item/vending_refill/custom{ + pixel_y = 3 + }, +/obj/item/t_scanner, +/turf/simulated/floor/plasteel, +/area/storage/primary) "kqU" = ( /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -66809,6 +66350,16 @@ /obj/effect/decal/cleanable/cobweb2, /turf/simulated/floor/plating, /area/maintenance/engineering) +"kAX" = ( +/obj/structure/table/reinforced, +/obj/structure/mirror{ + pixel_x = -28 + }, +/obj/item/razor, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "kBg" = ( /obj/machinery/vending/pai, /turf/simulated/floor/plasteel{ @@ -67307,6 +66858,13 @@ /obj/effect/decal/warning_stripes/northeast, /turf/simulated/floor/plasteel, /area/toxins/xenobiology) +"kSS" = ( +/obj/structure/table, +/obj/item/ai_module/quarantine, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "kTu" = ( /obj/structure/largecrate, /obj/effect/decal/cleanable/dust, @@ -67323,6 +66881,17 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plating, /area/maintenance/genetics) +"kUl" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "kUB" = ( /obj/structure/cable{ icon_state = "4-8" @@ -67422,6 +66991,15 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/simulated/floor/plating, /area/maintenance/asmaint) +"kXP" = ( +/obj/machinery/suit_storage_unit/security, +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "kXS" = ( /obj/effect/decal/warning_stripes/northwestsouth, /obj/structure/rack, @@ -67833,6 +67411,30 @@ icon_state = "black" }, /area/space) +"lqz" = ( +/obj/structure/window/reinforced, +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Magazines for SP-91-RC"; + req_access = list(1) + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 8 + }, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = 4 + }, +/obj/item/ammo_box/magazine/sp91rc, +/obj/item/ammo_box/magazine/sp91rc{ + pixel_x = -4 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "lqE" = ( /obj/machinery/light, /obj/structure/table, @@ -68777,6 +68379,12 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/port) +"lUn" = ( +/obj/structure/table, +/obj/item/aicard, +/obj/item/ai_module/reset, +/turf/simulated/floor/plating, +/area/storage/tech) "lUt" = ( /obj/structure/cable/yellow{ d1 = 1; @@ -68867,6 +68475,17 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/maintenance/turbine) +"lWI" = ( +/obj/machinery/dye_generator, +/obj/machinery/alarm{ + dir = 8; + pixel_x = 24 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "lWN" = ( /obj/structure/cable{ icon_state = "1-2" @@ -70250,24 +69869,6 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, /area/toxins/storage) -"mOm" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/public/glass{ - name = "Barber Shop" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "mOp" = ( /obj/effect/spawner/window/reinforced, /obj/structure/transit_tube/horizontal, @@ -70492,6 +70093,13 @@ }, /turf/simulated/floor/plasteel, /area/engineering/break_room) +"mYY" = ( +/obj/structure/table, +/obj/item/ai_module/freeform, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "mZa" = ( /obj/machinery/chem_master, /turf/simulated/floor/plating, @@ -70802,6 +70410,13 @@ /obj/effect/decal/warning_stripes/northwest, /turf/simulated/floor/plasteel, /area/hallway/secondary/entry) +"njU" = ( +/obj/structure/dispenser/oxygen, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "njX" = ( /obj/structure/table, /obj/item/reagent_containers/glass/beaker/large{ @@ -71331,6 +70946,14 @@ icon_state = "whiteblue" }, /area/security/medbay) +"nDs" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "nDy" = ( /obj/effect/decal/warning_stripes/east, /turf/simulated/floor/plating/airless, @@ -71687,6 +71310,16 @@ icon_state = "darkgrey" }, /area/toxins/mixing) +"nPM" = ( +/obj/machinery/atm{ + pixel_y = -32 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "nQm" = ( /obj/machinery/mineral/equipment_vendor, /turf/simulated/floor/plasteel{ @@ -72758,6 +72391,10 @@ icon_state = "dark" }, /area/security/warden) +"oJS" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "oKl" = ( /obj/effect/decal/warning_stripes/west, /obj/effect/decal/cleanable/dirt, @@ -73288,6 +72925,17 @@ }, /turf/simulated/floor/plasteel, /area/atmos) +"oWU" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "oXf" = ( /obj/structure/table, /obj/effect/decal/warning_stripes/north, @@ -73499,6 +73147,14 @@ }, /turf/simulated/floor/carpet, /area/crew_quarters/captain/bedroom) +"pdI" = ( +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/flasher/portable, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/security/securearmory) "pdR" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -74056,19 +73712,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/starboardsolar) -"pyg" = ( -/obj/machinery/navbeacon{ - codes_txt = "patrol;next_patrol=Armory_North"; - location = "Armory_sprava" - }, -/obj/machinery/light, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 9 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "pyQ" = ( /obj/machinery/light{ dir = 1 @@ -74535,6 +74178,16 @@ /obj/machinery/light/small, /turf/simulated/floor/plating, /area/maintenance/asmaint2) +"pLK" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "pMx" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -74702,30 +74355,6 @@ }, /turf/simulated/floor/plating, /area/aisat) -"pUD" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "pUG" = ( /obj/structure/cable/yellow{ d1 = 1; @@ -74951,31 +74580,6 @@ icon_state = "floorgrime" }, /area/maintenance/asmaint2) -"qcp" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/bulletproof, -/obj/item/clothing/head/helmet/alt, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - dir = 8; - name = "Secure Armory"; - req_access = list(1) - }, -/obj/item/clothing/shoes/jackboots/armored, -/obj/item/clothing/gloves/color/black/ballistic, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "qcJ" = ( /obj/structure/rack, /obj/item/storage/box/donkpockets{ @@ -75376,6 +74980,15 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint2) +"qmr" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "neutralcorner" + }, +/area/crew_quarters/dorms) "qmz" = ( /turf/simulated/floor/plasteel{ dir = 4; @@ -75652,6 +75265,13 @@ /obj/effect/decal/warning_stripes/northeast, /turf/simulated/floor/plating/airless, /area/space) +"qsM" = ( +/obj/machinery/computer/aiupload/cyborg, +/obj/item/radio/intercom/private{ + pixel_y = -28 + }, +/turf/simulated/floor/bluegrid, +/area/turret_protected/ai_upload) "qtm" = ( /obj/structure/girder, /turf/simulated/floor/plating, @@ -75711,6 +75331,12 @@ "qus" = ( /turf/simulated/wall/r_wall, /area/medical/research) +"qvQ" = ( +/obj/machinery/suit_storage_unit/security, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "qww" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 @@ -76630,6 +76256,20 @@ "qYv" = ( /turf/simulated/wall, /area/maintenance/cafeteria) +"qYH" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "qYU" = ( /turf/simulated/floor/plasteel{ icon_state = "grimy" @@ -76753,6 +76393,19 @@ icon_state = "cafeteria" }, /area/maintenance/cafeteria) +"rdR" = ( +/obj/structure/table/reinforced, +/obj/item/restraints/handcuffs, +/obj/item/restraints/handcuffs{ + pixel_y = -4 + }, +/obj/item/crowbar, +/obj/item/radio/sec, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkredfull" + }, +/area/security/main) "rdT" = ( /obj/structure/disposalpipe/trunk{ dir = 1 @@ -77154,6 +76807,28 @@ /obj/effect/spawner/random_spawners/cobweb_right_frequent, /turf/simulated/floor/plating, /area/maintenance/asmaint) +"rss" = ( +/obj/structure/closet/secure_closet/guncabinet{ + anchored = 1; + name = "Security SMG's" + }, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/gun/projectile/automatic/wt550, +/obj/item/gun/projectile/automatic/wt550{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "rsL" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/engineering/glass{ @@ -77345,6 +77020,14 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/simulated/floor/plating, /area/maintenance/genetics) +"rzG" = ( +/obj/machinery/flasher/portable, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/security/securearmory) "rzH" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ @@ -77603,30 +77286,6 @@ }, /turf/simulated/floor/engine/insulated, /area/maintenance/incinerator) -"rIF" = ( -/obj/structure/rack{ - dir = 8; - layer = 2.9 - }, -/obj/item/clothing/suit/armor/riot, -/obj/item/shield/riot, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/head/helmet/riot, -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/machinery/door/window{ - name = "Secure Armory"; - req_access = list(1) - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "rIH" = ( /obj/machinery/light_switch{ name = "south light switch"; @@ -78216,6 +77875,15 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/crew_quarters/courtroom) +"sbK" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 10 + }, +/obj/structure/cable{ + icon_state = "1-2" + }, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "scD" = ( /obj/structure/closet, /obj/effect/spawner/lootdrop/maintenance, @@ -78279,6 +77947,33 @@ icon_state = "neutral" }, /area/hallway/secondary/exit) +"seR" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/item/storage/box/flashbangs, +/obj/item/storage/box/flashbangs{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/storage/box/teargas{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/storage/box/teargas, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "seY" = ( /obj/effect/decal/warning_stripes/northeast, /obj/effect/decal/cleanable/blood/xeno, @@ -79005,16 +78700,6 @@ /obj/structure/sign/biohazard, /turf/simulated/wall/r_wall, /area/medical/research) -"sDN" = ( -/obj/structure/table/reinforced, -/obj/item/razor, -/obj/structure/mirror{ - pixel_x = -28 - }, -/turf/simulated/floor/plasteel{ - icon_state = "barber" - }, -/area/civilian/barber) "sDO" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -79780,6 +79465,34 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint2) +"sZQ" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/obj/machinery/light{ + dir = 8 + }, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tab" = ( /obj/structure/cable{ icon_state = "1-2" @@ -79916,17 +79629,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint) -"tdQ" = ( -/obj/structure/closet/secure_closet/security, -/obj/structure/window/reinforced{ - dir = 8 - }, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/main) "tel" = ( /turf/simulated/wall/r_wall, /area/engineering/gravitygenerator) @@ -80173,6 +79875,31 @@ /obj/effect/decal/warning_stripes/northeast, /turf/simulated/floor/plasteel/airless, /area/toxins/test_area) +"tmA" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/bulletproof, +/obj/item/clothing/head/helmet/alt, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/structure/window/reinforced, +/obj/machinery/door/window{ + dir = 8; + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/jackboots/armored, +/obj/item/clothing/gloves/color/black/ballistic, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tmJ" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ @@ -80265,6 +79992,26 @@ icon_state = "dark" }, /area/construction) +"tpZ" = ( +/obj/structure/table, +/obj/machinery/door/window{ + dir = 8; + name = "High-Risk Modules"; + req_access = list(20) + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, +/turf/simulated/floor/bluegrid, +/area/turret_protected/ai_upload) "tqG" = ( /obj/machinery/space_heater, /turf/simulated/floor/plating, @@ -80307,6 +80054,31 @@ }, /turf/simulated/floor/plating, /area/quartermaster/storage) +"tsV" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tui" = ( /obj/machinery/door/poddoor{ density = 0; @@ -80411,6 +80183,19 @@ icon_state = "cafeteria" }, /area/medical/medbreak) +"twB" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/obj/machinery/alarm{ + pixel_y = 24 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "twI" = ( /obj/item/screwdriver, /turf/simulated/floor/plasteel, @@ -80598,6 +80383,10 @@ }, /turf/simulated/floor/wood, /area/medical/psych) +"tEg" = ( +/obj/machinery/arcade/minesweeper, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "tEL" = ( /obj/machinery/light_switch{ name = "south light switch"; @@ -80702,6 +80491,12 @@ /obj/effect/decal/warning_stripes/east, /turf/simulated/floor/plating, /area/storage/secure) +"tHu" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plating, +/area/maintenance/fsmaint) "tHw" = ( /obj/machinery/suit_storage_unit/engine, /obj/machinery/light{ @@ -80810,6 +80605,10 @@ /obj/structure/lattice/catwalk, /turf/space, /area/solar/port) +"tMq" = ( +/obj/structure/sign/security, +/turf/simulated/wall/r_wall, +/area/security/securearmory) "tMD" = ( /obj/machinery/photocopier, /obj/structure/sign/poster/official/random{ @@ -80863,6 +80662,31 @@ icon_state = "whiteblue" }, /area/medical/surgery/north) +"tNJ" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/clothing/suit/armor/riot, +/obj/item/shield/riot, +/obj/item/clothing/head/helmet/riot, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/door/window{ + name = "Secure Armory"; + req_access = list(1) + }, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "tNO" = ( /obj/item/radio/intercom{ name = "north station intercom (General)"; @@ -81211,6 +81035,18 @@ icon_state = "cafeteria" }, /area/medical/research/restroom) +"tWe" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "tWU" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -81973,6 +81809,12 @@ }, /turf/simulated/floor/plating, /area/maintenance/atmospherics) +"uBf" = ( +/obj/machinery/portable_atmospherics/canister/oxygen, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "uBA" = ( /obj/effect/decal/cleanable/dust, /obj/machinery/atmospherics/unary/vent_scrubber/on{ @@ -83086,6 +82928,31 @@ }, /turf/simulated/floor/plasteel, /area/hallway/primary/central/se) +"vpk" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/ammo_box/shotgun/beanbag, +/obj/item/ammo_box/shotgun/beanbag{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/ammo_box/shotgun/tranquilizer{ + pixel_x = -6; + pixel_y = 6 + }, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "vpO" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel, @@ -83593,6 +83460,17 @@ icon_state = "floorgrime" }, /area/maintenance/incinerator) +"vHg" = ( +/obj/structure/closet/secure_closet/security, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/main) "vHh" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -83824,13 +83702,6 @@ icon_state = "darkgrey" }, /area/toxins/mixing) -"vNE" = ( -/obj/machinery/computer/borgupload, -/obj/item/radio/intercom/private{ - pixel_y = -28 - }, -/turf/simulated/floor/bluegrid, -/area/turret_protected/ai_upload) "vOi" = ( /obj/structure/cable{ icon_state = "4-8" @@ -84345,6 +84216,12 @@ icon_state = "whiteblue" }, /area/medical/reception) +"wbo" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "vault" + }, +/area/security/securearmory) "wbr" = ( /obj/structure/cable{ icon_state = "2-4" @@ -84367,6 +84244,16 @@ icon_state = "bar" }, /area/maintenance/cafeteria) +"wcj" = ( +/obj/machinery/navbeacon{ + codes_txt = "patrol;next_patrol=Armory_South"; + location = "Armory_sleva" + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "vault" + }, +/area/security/securearmory) "wcS" = ( /obj/machinery/door/airlock/security/glass{ name = "Shooting Range"; @@ -84941,6 +84828,14 @@ icon_state = "grimy" }, /area/crew_quarters/heads) +"wqH" = ( +/obj/structure/table, +/obj/item/ai_module/protect_station, +/obj/item/ai_module/nanotrasen, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/turret_protected/ai_upload) "wqK" = ( /obj/structure/sign/securearea{ desc = "A warning sign which reads 'HIGH VOLTAGE'"; @@ -84949,6 +84844,33 @@ }, /turf/simulated/wall/r_wall, /area/storage/tech) +"wri" = ( +/obj/structure/closet/secure_closet/quartermaster, +/obj/item/extraction_pack, +/obj/item/fulton_core, +/obj/item/flash, +/obj/item/clipboard, +/obj/item/cartridge/quartermaster{ + pixel_x = -4; + pixel_y = 7 + }, +/obj/item/cartridge/quartermaster{ + pixel_x = 6; + pixel_y = 5 + }, +/obj/item/cartridge/quartermaster, +/obj/item/megaphone{ + pixel_x = 7 + }, +/obj/item/stamp/qm{ + pixel_x = 7; + pixel_y = 3 + }, +/obj/item/mining_voucher, +/turf/simulated/floor/plasteel{ + icon_state = "brown" + }, +/area/quartermaster/qm) "wrj" = ( /obj/machinery/light, /obj/effect/decal/warning_stripes/southeastcorner, @@ -86535,40 +86457,6 @@ /obj/machinery/atmospherics/meter, /turf/simulated/floor/plasteel, /area/atmos) -"xnw" = ( -/obj/structure/closet/secure_closet/guncabinet{ - anchored = 1; - name = "Lethal bullets" - }, -/obj/item/ammo_box/c9mm, -/obj/item/ammo_box/c9mm{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = 3; - pixel_y = -3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -3; - pixel_y = 3 - }, -/obj/item/ammo_box/magazine/enforcer/lethal{ - pixel_x = -6; - pixel_y = 6 - }, -/obj/machinery/light{ - dir = 4 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/effect/decal/warning_stripes/red/hollow, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/security/securearmory) "xnH" = ( /obj/effect/decal/cleanable/generic, /turf/simulated/floor/plating, @@ -86778,6 +86666,33 @@ icon_state = "whitebluecorner" }, /area/medical/medbay2) +"xvk" = ( +/obj/structure/rack{ + dir = 8; + layer = 2.9 + }, +/obj/item/grenade/barrier{ + pixel_x = -3; + pixel_y = 3 + }, +/obj/item/grenade/barrier, +/obj/item/grenade/barrier{ + pixel_x = 3; + pixel_y = -3 + }, +/obj/item/grenade/barrier{ + pixel_x = 6; + pixel_y = -6 + }, +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/effect/decal/warning_stripes/red/hollow, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "xvu" = ( /obj/machinery/light, /obj/item/radio/intercom{ @@ -87395,6 +87310,14 @@ }, /turf/simulated/floor/plating, /area/maintenance/asmaint2) +"xPV" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 6 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/security/securearmory) "xQb" = ( /obj/machinery/alarm{ dir = 4; @@ -87812,6 +87735,20 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/engine, /area/engineering/mechanic_workshop) +"yfO" = ( +/obj/machinery/firealarm{ + dir = 4; + name = "east fire alarm"; + pixel_x = 24 + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel{ + icon_state = "barber" + }, +/area/maintenance/fsmaint) "ygO" = ( /obj/machinery/light/small{ dir = 8 @@ -87859,12 +87796,6 @@ "yig" = ( /turf/simulated/wall/rust, /area/maintenance/fsmaint2) -"yjg" = ( -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "vault" - }, -/area/security/securearmory) "yjl" = ( /mob/living/simple_animal/pet/dog/security/warden, /obj/structure/bed/dogbed, @@ -104782,7 +104713,7 @@ aPi aMA aSa bdd -czC +kqL aWl aXQ aXQ @@ -110475,7 +110406,7 @@ cas cbK cls cis -ckq +wri bYM jiK eKX @@ -113826,7 +113757,7 @@ cgW cgW gth dsp -cCM +lUn cyr vVH map @@ -114757,15 +114688,15 @@ aiv abO aiv aiv -acR -aeo -akf -ama -ama -arG -pUD -pUD -rIF +rzG +wbo +seR +jlu +jlu +sZQ +tsV +tsV +tNJ vUG aep oJy @@ -114822,10 +114753,10 @@ bqO bsv bvX bvX -bxm -byK +etU +kSS fRL -bBw +faz bvX uTI uTI @@ -115013,16 +114944,16 @@ uTI aiv abO aiv -acR -aeo -yjg -yjg -yjg -yjg -yjg -yjg -yjg -yjg +rzG +wbo +gbD +gbD +gbD +gbD +gbD +gbD +gbD +gbD aAl aqv oJy @@ -115270,11 +115201,11 @@ lTl aiv abO aiv -acS -aev +pdI +wcj qzT qzT -amb +xPV alk alk atp @@ -115527,16 +115458,16 @@ uTI aiv abO aiv -acW +njU qzT ahU akv -amh +oWU qzT -yjg -yjg -yjg -yjg +gbD +gbD +gbD +gbD aAr aBT oJy @@ -115784,16 +115715,16 @@ lTl aiv abO aiv -acY -yjg -acB -akl -dbn -icF -arI -ahD -qcp -ayE +qvQ +gbD +xvk +vpk +itT +cON +tmA +gFy +jcP +dyO vUG aBZ orW @@ -116041,12 +115972,12 @@ uTI aiv abO aiv -ada -yjg -ale -akn -dbn -apy +kXP +gbD +hRQ +dOZ +itT +lqz aiv aiv aiv @@ -116110,7 +116041,7 @@ dpx bxo bDV bFb -vNE +qsM bvX jck bFC @@ -116298,13 +116229,13 @@ lTl aiv abO aiv -acY -yjg -adt -akt -amv +qvQ +gbD +rss +dOf +gGV aiv -alU +tMq ats aiz aiv @@ -116555,10 +116486,10 @@ uTI aiv abO aiv -adg +uBf qzT -fSC -fSC +eVr +eVr brW mKg arJ @@ -116812,11 +116743,11 @@ lTl aiv abO aiv -adh +fQR acH ahV alk -pyg +bRR gqF fhJ dqg @@ -116878,10 +116809,10 @@ dkJ bsz bvX bvX -bCy -bDW +wqH +mYY bzW -bBz +tpZ bvX uTI uTI @@ -117069,7 +117000,7 @@ uTI aiv abO aiv -adj +twB qzT qzT qzT @@ -117326,11 +117257,11 @@ lTl aiv abO aiv -adm -aeR -yjg -yjg -yjg +dHi +aMI +gbD +gbD +gbD qzT qzT qzT @@ -117584,10 +117515,10 @@ aiv abO aiv aiv -acO -aik -xnw -amK +any +eKz +gwc +kpg aiv ajr aci @@ -117616,7 +117547,7 @@ azN nKY aCq boO -aEm +hkL aFA bxh byV @@ -118105,10 +118036,10 @@ abO abO abO aiv -acv -tdQ -acj -alZ +qYH +kUl +eBW +fvE jYT eDL anJ @@ -118369,7 +118300,7 @@ pHt aEC tJR aHU -ahY +rdR xXJ aqC ajm @@ -119133,10 +119064,10 @@ abM apP arP nEQ -adk -adk -adx -adQ +vHg +vHg +gxq +tWe aek eDL vjh @@ -120186,14 +120117,14 @@ azW tjI aCw aIq -gel -gel -gel -gel -gel -gel -gel -gel +awl +awl +awl +awl +aRF +awl +awl +awl bUC aQn aSx @@ -120443,15 +120374,15 @@ aCw aCw aCw aIq -gel -buF -aGG -aId -aJp -sDN -bIs -gel -aPP +awl +kAX +kiJ +eBc +axW +axW +nDs +awl +qmr bbo aZt aZt @@ -120700,14 +120631,14 @@ azY avq wCH aIq -gel -buL -bxt -iHY -bAH -iHY -bIz -gel +awl +jVz +grV +avq +fPD +axW +nPM +awl bUD aQp aZv @@ -120957,14 +120888,14 @@ azX aKK aKK bpg -gel -aGK -fbY -aIi -aKE -bFL -bIL -mOm +awl +cem +sbK +oJS +pLK +tHu +iOJ +bLQ bUN aQo cbn @@ -121214,10 +121145,10 @@ avq avq aBq avq -gel -aDP -bxu -bzm +awl +lWI +yfO +tEg aFI aFI aFI diff --git a/_maps/map_files/event/Station/delta_old.dmm b/_maps/map_files/event/Station/delta_old.dmm index 23a34ab390d..5981bbb8556 100644 --- a/_maps/map_files/event/Station/delta_old.dmm +++ b/_maps/map_files/event/Station/delta_old.dmm @@ -12764,6 +12764,9 @@ /obj/structure/cable{ icon_state = "1-8" }, +/obj/item/qm_quest_tablet{ + pixel_y = -3 + }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -13906,7 +13909,7 @@ /turf/simulated/floor/plasteel, /area/engineering/controlroom) "bOG" = ( -/obj/item/aiModule/reset, +/obj/item/ai_module/reset, /obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -16339,7 +16342,6 @@ scrub_N2O = 1; scrub_Toxins = 1 }, -/obj/effect/landmark/start/barber, /turf/simulated/floor/plasteel{ icon_state = "barber" }, @@ -27319,11 +27321,11 @@ }, /area/medical/research/shallway) "cWa" = ( -/obj/item/aiModule/protectStation{ +/obj/item/ai_module/protect_station{ pixel_x = -2; pixel_y = 2 }, -/obj/item/aiModule/nanotrasen{ +/obj/item/ai_module/nanotrasen{ pixel_x = 2; pixel_y = -2 }, @@ -30423,7 +30425,7 @@ }, /area/construction/hallway) "diX" = ( -/obj/machinery/computer/borgupload, +/obj/machinery/computer/aiupload/cyborg, /obj/item/radio/intercom/private{ pixel_y = -28 }, @@ -50769,10 +50771,10 @@ /obj/structure/window/reinforced{ dir = 4 }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, /obj/structure/table/glass, /turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) @@ -65408,7 +65410,7 @@ /area/chapel/office) "kGQ" = ( /obj/structure/table/reinforced, -/obj/item/aiModule/reset, +/obj/item/ai_module/reset, /obj/item/flash, /obj/item/flash, /obj/effect/decal/warning_stripes/yellow, @@ -90972,7 +90974,7 @@ /turf/simulated/wall, /area/security/processing) "rzo" = ( -/obj/item/aiModule/quarantine, +/obj/item/ai_module/quarantine, /obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -111779,7 +111781,7 @@ /turf/simulated/wall/r_wall/coated, /area/toxins/mixing) "xbr" = ( -/obj/item/aiModule/freeform, +/obj/item/ai_module/freeform, /obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -113214,11 +113216,11 @@ /obj/structure/window/reinforced{ dir = 8 }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, /obj/structure/table/glass, /turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) diff --git a/_maps/map_files/event/Station/towerstation.dmm b/_maps/map_files/event/Station/towerstation.dmm index dca0de25e48..c737bee27ea 100644 --- a/_maps/map_files/event/Station/towerstation.dmm +++ b/_maps/map_files/event/Station/towerstation.dmm @@ -232,10 +232,10 @@ /obj/structure/window/reinforced{ dir = 4 }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, /obj/structure/table/reinforced, /obj/machinery/door/window{ base_state = "right"; @@ -10611,15 +10611,15 @@ /obj/structure/cable{ icon_state = "0-2" }, -/obj/item/aiModule/protectStation{ +/obj/item/ai_module/protect_station{ pixel_x = -2; pixel_y = 2 }, -/obj/item/aiModule/nanotrasen{ +/obj/item/ai_module/nanotrasen{ pixel_x = 2; pixel_y = -2 }, -/obj/item/aiModule/freeform, +/obj/item/ai_module/freeform, /turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) "gUk" = ( @@ -12131,6 +12131,9 @@ dir = 1; pixel_y = 28 }, +/obj/item/qm_quest_tablet{ + pixel_y = -3 + }, /turf/simulated/floor/carpet/black, /area/quartermaster/storage) "hNH" = ( @@ -13742,11 +13745,11 @@ /obj/machinery/alarm{ pixel_y = 24 }, -/obj/item/aiModule/reset{ +/obj/item/ai_module/reset{ pixel_x = -3; pixel_y = 3 }, -/obj/item/aiModule/quarantine{ +/obj/item/ai_module/quarantine{ pixel_y = -3 }, /turf/simulated/floor/bluegrid, @@ -14586,11 +14589,11 @@ /obj/structure/window/reinforced{ dir = 8 }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, /obj/structure/table/reinforced, /obj/machinery/door/window{ base_state = "right"; @@ -16483,6 +16486,12 @@ /obj/structure/window/reinforced, /turf/simulated/floor/glass/reinforced, /area/hallway/secondary/entry) +"kFx" = ( +/obj/machinery/computer/supplyquest/workers{ + pixel_y = -2 + }, +/turf/simulated/wall/r_wall, +/area/quartermaster/storage) "kFy" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 @@ -18395,7 +18404,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, -/obj/effect/landmark/start/barber, /turf/simulated/floor/plasteel{ icon_state = "barber" }, @@ -24860,7 +24868,7 @@ /turf/simulated/floor/plasteel/stairs/left, /area/crew_quarters/dorms) "pYG" = ( -/obj/machinery/computer/borgupload, +/obj/machinery/computer/aiupload/cyborg, /obj/structure/cable{ icon_state = "1-8" }, @@ -31694,7 +31702,6 @@ }, /area/atmos) "uvM" = ( -/obj/effect/landmark/start/barber, /turf/simulated/floor/plasteel{ icon_state = "barber" }, @@ -336259,7 +336266,7 @@ lli jxt llI jxt -swj +kFx but llI brb diff --git a/_maps/map_files/generic/CentComm.dmm b/_maps/map_files/generic/CentComm.dmm index 4041cb454c2..f1d20573346 100644 --- a/_maps/map_files/generic/CentComm.dmm +++ b/_maps/map_files/generic/CentComm.dmm @@ -219,8 +219,8 @@ pixel_y = 6 }, /obj/item/paper/monitorkey{ - pixel_y = 4; - pixel_x = 3 + pixel_x = 3; + pixel_y = 4 }, /obj/item/paper/safe_code{ owner = "captain"; @@ -228,8 +228,8 @@ }, /obj/item/paper/safe_code{ owner = "hos"; - pixel_y = -2; - pixel_x = -3 + pixel_x = -3; + pixel_y = -2 }, /turf/simulated/floor/carpet/cyan, /area/centcom/zone2) @@ -311,6 +311,11 @@ icon_state = "darkredcorners" }, /area/syndicate_mothership/elite_squad) +"aeQ" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/loading/one) "aeY" = ( /obj/item/deck/cards, /obj/structure/table/wood, @@ -514,6 +519,12 @@ }, /turf/simulated/wall/indestructible/reinforced, /area/centcom/zone3) +"aiB" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "aiQ" = ( /obj/structure/table, /obj/item/storage/box/cups, @@ -807,8 +818,8 @@ /area/centcom/specops) "apl" = ( /obj/structure/falsewall/reinforced{ - req_access = list(114); - layer = 5 + layer = 5; + req_access = list(114) }, /turf/simulated/floor/fakespace, /area/centcom/specops) @@ -1217,8 +1228,8 @@ "ayt" = ( /obj/machinery/syndiepad/receivepad, /obj/machinery/conveyor{ - id = "SFBQMLoad"; - dir = 8 + dir = 8; + id = "SFBQMLoad" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -1451,6 +1462,11 @@ name = "floor" }, /area/syndicate_mothership/outside) +"aDR" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supply) "aDX" = ( /obj/machinery/computer/syndie_supplycomp, /turf/simulated/floor/plasteel{ @@ -1572,6 +1588,12 @@ name = "floor" }, /area/vox_station) +"aHe" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "aHf" = ( /obj/item/target, /obj/effect/decal/cleanable/confetti, @@ -1813,8 +1835,8 @@ "aLo" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/machinery/conveyor{ - id = "CO2"; - dir = 1 + dir = 1; + id = "CO2" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -1925,10 +1947,10 @@ "aOA" = ( /obj/machinery/door_control/secure{ id = "CC_Armory_SRT1"; - pixel_y = -25; - req_access = list(114); + name = "SRT Team 2"; pixel_x = -25; - name = "SRT Team 2" + pixel_y = -25; + req_access = list(114) }, /turf/simulated/floor/plasteel{ icon_state = "navybluealtstrip" @@ -1975,6 +1997,12 @@ name = "floor" }, /area/syndicate_mothership) +"aOM" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellowalt" + }, +/area/centcom/supply) "aOO" = ( /obj/effect/turf_decal/siding/yellow{ dir = 1 @@ -2173,8 +2201,8 @@ /obj/structure/bed, /obj/item/bedsheet/rd, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/arcade, /area/centcom/zone1) @@ -2309,6 +2337,12 @@ /obj/item/toy/desk/fan, /turf/simulated/floor/carpet/black, /area/centcom/zone2) +"aYk" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "aYm" = ( /obj/structure/window/plasmareinforced{ color = "#00f700"; @@ -2976,11 +3010,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 175; teleport_y = 62; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -3327,10 +3361,10 @@ height = 5; id = "sit"; name = "SIT shuttle"; - roundstart_move = "sit_away"; - width = 11; port_direction = 4; - preferred_direction = 2 + preferred_direction = 2; + roundstart_move = "sit_away"; + width = 11 }, /obj/docking_port/stationary{ dir = 4; @@ -3404,8 +3438,9 @@ }, /area/centcom/specops) "bBe" = ( -/obj/machinery/vending/ntcrates, -/turf/simulated/floor/plating, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, /area/centcom/specops) "bBg" = ( /obj/machinery/embedded_controller/radio/airlock/airlock_controller{ @@ -3524,12 +3559,12 @@ /obj/item/clothing/accessory/holster, /obj/item/storage/backpack/satcheldeluxe, /obj/item/reagent_containers/food/drinks/bottle/vodka/badminka{ - pixel_y = 8; - pixel_x = -4 + pixel_x = -4; + pixel_y = 8 }, /obj/item/reagent_containers/food/drinks/drinkingglass/shotglass{ - pixel_y = 12; - pixel_x = 10 + pixel_x = 10; + pixel_y = 12 }, /obj/item/megaphone, /turf/simulated/floor/carpet/black, @@ -3600,6 +3635,12 @@ icon_state = "fancy-wood-oak" }, /area/syndicate_mothership) +"bEA" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "bEB" = ( /obj/structure/showcase{ desc = "This one has an old damaged suit in it. Not working..."; @@ -3622,8 +3663,8 @@ /area/shuttle/syndicate) "bEW" = ( /obj/structure/railing{ - pixel_y = 32; - density = 0 + density = 0; + pixel_y = 32 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -3747,6 +3788,11 @@ icon_state = "dark" }, /area/centcom/specops) +"bIN" = ( +/turf/simulated/floor/plasteel{ + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "bIU" = ( /obj/machinery/smartfridge/secure/chemistry/preloaded/syndicate, /turf/simulated/floor/plasteel{ @@ -3996,6 +4042,12 @@ icon_state = "floorgrime" }, /area/ninja/holding) +"bRZ" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "bSd" = ( /obj/structure/window/reinforced{ dir = 4 @@ -4508,11 +4560,11 @@ name = "Escape Pod Hatch" }, /obj/docking_port/mobile/pod{ + dir = 8; id = "pod2"; name = "escape pod 2"; - roundstart_move = "pod2_home"; - dir = 8; - port_direction = 2 + port_direction = 2; + roundstart_move = "pod2_home" }, /obj/docking_port/stationary{ dir = 8; @@ -4780,36 +4832,36 @@ /obj/structure/table/reinforced, /obj/machinery/door_control/secure{ id = "CC_space_jail_sec"; - pixel_y = -6; - pixel_x = -7 + pixel_x = -7; + pixel_y = -6 }, /obj/machinery/embedded_controller/radio/airlock/access_controller{ frequency = 2000; id_tag = "CC-OP4"; name = "Access Controller OP4"; + pixel_x = -7; pixel_y = 6; req_access = list(109); tag_exterior_door = "CC-OP4-Ext"; - tag_interior_door = "CC-OP4-Int"; - pixel_x = -7 + tag_interior_door = "CC-OP4-Int" }, /obj/machinery/door_control/secure{ - id = "CC_space_jail_cell"; - pixel_y = -6; - pixel_x = 7; color = "#FFA500"; + id = "CC_space_jail_cell"; name = "remote cell shutters control"; + pixel_x = 7; + pixel_y = -6; req_access = list(104,114) }, /obj/machinery/door_control/secure{ - id = "CC_space_jail_door"; - pixel_y = 6; - pixel_x = 7; color = "#ffaaaa"; + id = "CC_space_jail_door"; name = "remote cell door bolts control"; + normaldoorcontrol = 1; + pixel_x = 7; + pixel_y = 6; req_access = list(104,114); - specialfunctions = 4; - normaldoorcontrol = 1 + specialfunctions = 4 }, /turf/simulated/floor/plasteel{ dir = 5; @@ -5099,11 +5151,11 @@ name = "Escape Pod Hatch" }, /obj/docking_port/mobile/pod{ + dir = 4; id = "pod4"; name = "escape pod 4"; - roundstart_move = "pod4_home"; - dir = 4; - port_direction = 2 + port_direction = 2; + roundstart_move = "pod4_home" }, /obj/docking_port/stationary{ dir = 4; @@ -5214,8 +5266,8 @@ /obj/structure/bed, /obj/item/bedsheet/red, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/arcade, /area/centcom/zone1) @@ -5540,6 +5592,12 @@ name = "floor" }, /area/syndicate_mothership) +"czW" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "cAA" = ( /obj/item/flag/nt, /turf/simulated/floor/plasteel{ @@ -5547,6 +5605,15 @@ icon_state = "navybluealt" }, /area/centcom/specops) +"cAU" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "cBk" = ( /obj/structure/sign/poster/official/ue_no, /turf/simulated/wall/indestructible/sandstone, @@ -5845,6 +5912,12 @@ name = "floor" }, /area/vox_station) +"cJx" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "cJJ" = ( /obj/effect/turf_decal{ dir = 8; @@ -5945,8 +6018,8 @@ /area/centcom/zone2) "cKU" = ( /obj/machinery/conveyor_switch/oneway{ - id = "SFBQMLoad2"; - dir = 8 + dir = 8; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -6001,8 +6074,8 @@ dir = 4 }, /obj/structure/window/reinforced{ - layer = 3.1; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); + layer = 3.1 }, /turf/simulated/floor/plasteel{ dir = 8; @@ -6250,11 +6323,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 175; teleport_y = 63; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -6466,8 +6539,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl2"; - name = "Armory level 2"; - layer = 5 + layer = 5; + name = "Armory level 2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -6766,10 +6839,10 @@ height = 5; id = "laborcamp"; name = "labor camp shuttle"; + port_direction = 4; rebuildable = 1; roundstart_move = "laborcamp_home"; - width = 9; - port_direction = 4 + width = 9 }, /obj/structure/fans/tiny, /turf/simulated/floor/shuttle, @@ -6872,7 +6945,7 @@ pixel_y = 32 }, /turf/simulated/floor/plasteel{ - dir = 5; + dir = 1; icon_state = "darkyellowalt" }, /area/centcom/supply) @@ -6980,8 +7053,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 2; id_tag = "ERT_armory_lvl3"; - name = "Armory level 3"; - layer = 5 + layer = 5; + name = "Armory level 3" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -7082,11 +7155,11 @@ name = "Escape Pod Hatch" }, /obj/docking_port/mobile/pod{ + dir = 8; id = "pod1"; name = "escape pod 1"; - roundstart_move = "pod1_home"; - dir = 8; - port_direction = 2 + port_direction = 2; + roundstart_move = "pod1_home" }, /obj/docking_port/stationary{ dir = 8; @@ -7109,6 +7182,12 @@ tag = "icon-stage_stairs" }, /area/ninja/holding) +"dtp" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "dtu" = ( /obj/structure/flora/ausbushes/reedbush, /turf/simulated/floor/indestructible/beach/coastline{ @@ -7467,11 +7546,11 @@ name = "Escape Pod Hatch" }, /obj/docking_port/mobile/pod{ + dir = 4; id = "pod3"; name = "escape pod 3"; - roundstart_move = "pod3_home"; - dir = 4; - port_direction = 2 + port_direction = 2; + roundstart_move = "pod3_home" }, /obj/docking_port/stationary{ dir = 4; @@ -7700,10 +7779,10 @@ /area/vox_station) "dEV" = ( /obj/machinery/door/airlock/external{ + hackProof = 1; id_tag = "supply_away"; name = "Central Command Supply"; - req_access = list(31); - hackProof = 1 + req_access = list(31) }, /obj/machinery/door/poddoor{ id_tag = "CC_supply_space"; @@ -7785,9 +7864,9 @@ "dGB" = ( /obj/effect/decal/warning_stripes/northwestsouth, /obj/machinery/door/poddoor/shutters/preopen/invincible{ + dir = 2; id_tag = "ERT_armory_lvl1"; - name = "Armory level 1"; - dir = 2 + name = "Armory level 1" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -7845,8 +7924,8 @@ req_access = list(114) }, /turf/simulated/floor/plasteel{ - icon_state = "darkgreencorners"; - dir = 1 + dir = 1; + icon_state = "darkgreencorners" }, /area/centcom/specops) "dIi" = ( @@ -8109,11 +8188,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 183; teleport_y = 60; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -8185,6 +8264,15 @@ "dTg" = ( /turf/simulated/floor/plating/airless, /area/centcom/specops) +"dTt" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "dTy" = ( /obj/structure/railing{ dir = 8 @@ -8565,10 +8653,10 @@ height = 18; id = "admin"; name = "administration shuttle"; - roundstart_move = "admin_away"; - width = 18; port_direction = 2; - preferred_direction = 8 + preferred_direction = 8; + roundstart_move = "admin_away"; + width = 18 }, /obj/machinery/door/airlock/external{ frequency = 1331; @@ -8991,8 +9079,8 @@ name = "Любимая игрушка бюрократов" }, /obj/item/reagent_containers/food/drinks/coffee{ - pixel_y = 9; - pixel_x = 6 + pixel_x = 6; + pixel_y = 9 }, /turf/simulated/floor/carpet/black, /area/centcom/zone2) @@ -9210,11 +9298,11 @@ /area/shuttle/administration) "erZ" = ( /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 137; teleport_y = 65; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -9387,8 +9475,8 @@ /area/centcom/zone3) "evD" = ( /obj/structure/falsewall/reinforced{ - req_access = list(114); - layer = 5 + layer = 5; + req_access = list(114) }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -9630,6 +9718,11 @@ }, /turf/simulated/floor/carpet/red, /area/syndicate_mothership/control) +"eDu" = ( +/turf/simulated/floor/plasteel{ + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "eDO" = ( /obj/effect/spawner/lootdrop/trade_sol/largeitem, /turf/simulated/floor/wood{ @@ -9730,6 +9823,15 @@ /obj/machinery/light/small, /turf/simulated/floor/wood/fancy/light, /area/ussp_centcom/secretariat) +"eGx" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "eHj" = ( /obj/item/storage/firstaid/ancient{ pixel_x = 3; @@ -10133,8 +10235,8 @@ name = "Supply Blastdoor" }, /obj/machinery/conveyor{ - id = "CC_crate"; - dir = 1 + dir = 1; + id = "CC_crate" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -10471,6 +10573,12 @@ icon_state = "darkyellowcornersalt" }, /area/centcom/zone3) +"eZq" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "eZx" = ( /obj/structure/table/wood, /obj/item/reagent_containers/glass/beaker/waterbottle/large{ @@ -10537,9 +10645,9 @@ }, /obj/effect/turf_decal/tile/neutral{ alpha = 85; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/specops) @@ -10788,8 +10896,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl4"; - name = "Armory level 4"; - layer = 5 + layer = 5; + name = "Armory level 4" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -11272,8 +11380,8 @@ /obj/structure/bed, /obj/item/bedsheet/wiz, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet, /area/centcom/zone1) @@ -11283,10 +11391,10 @@ color = "#666666"; damtype = "burn"; health = 1250; + name = "Quarantine Pulse Turret"; region_max = 12; scan_range = 12; - shot_delay = 8; - name = "Quarantine Pulse Turret" + shot_delay = 8 }, /obj/machinery/door/poddoor/shutters/invincible/fake_r_wall{ dir = 1; @@ -11678,8 +11786,8 @@ req_access = list(114) }, /turf/simulated/floor/plasteel{ - icon_state = "darkyellowcorners"; - dir = 1 + dir = 1; + icon_state = "darkyellowcorners" }, /area/centcom/specops) "fCZ" = ( @@ -11712,8 +11820,8 @@ "fDh" = ( /obj/machinery/camera{ c_tag = "CentComm Special Ops. Shuttle"; - network = list("ERT","CentComm"); - dir = 4 + dir = 4; + network = list("ERT","CentComm") }, /obj/machinery/recharge_station/ert, /turf/simulated/floor/shuttle{ @@ -11917,9 +12025,9 @@ /area/ninja/outpost) "fHe" = ( /obj/structure/window/reinforced{ + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); dir = 1; - layer = 2; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + layer = 2 }, /turf/simulated/floor/plasteel{ dir = 1; @@ -11972,6 +12080,16 @@ icon_state = "darkgreenfull" }, /area/centcom/specops) +"fJp" = ( +/obj/machinery/door_control/secure{ + id = "ShitRainSupply"; + pixel_x = 24; + pixel_y = 24 + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/specops) "fJz" = ( /obj/machinery/door/poddoor/shutters/invincible{ dir = 2; @@ -12292,6 +12410,12 @@ }, /turf/simulated/floor/carpet/black, /area/ninja/outpost) +"fRi" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellowalt" + }, +/area/centcom/supply) "fRH" = ( /obj/machinery/light/small{ dir = 4 @@ -12375,14 +12499,14 @@ /area/syndicate_mothership/control) "fUK" = ( /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 9; - pixel_y = 1; pixel_x = -1; - density = 0 + pixel_y = 1 }, /obj/structure/fishingrodcabinet{ - pixel_y = 32; - pixel_x = 3 + pixel_x = 3; + pixel_y = 32 }, /turf/simulated/floor/plasteel{ dir = 5; @@ -13262,15 +13386,15 @@ }, /obj/structure/fans/tiny, /obj/docking_port/mobile{ + alone_shuttle = 1; dir = 8; dwidth = 2; height = 5; id = "ruins_transport_shuttle"; name = "USSP Cargo Shuttle"; + port_direction = 4; roundstart_move = "ussp_dock"; - alone_shuttle = 1; - width = 8; - port_direction = 4 + width = 8 }, /turf/simulated/floor/shuttle{ icon_state = "floor4" @@ -13309,13 +13433,13 @@ id = "SFBQMLoad" }, /obj/machinery/conveyor{ - id = "SFBQMLoad"; dir = 8; + id = "SFBQMLoad"; layer = 2.494 }, /obj/machinery/conveyor{ - id = "SFBQMLoad"; dir = 4; + id = "SFBQMLoad"; layer = 2.494 }, /turf/simulated/floor/plasteel{ @@ -13575,6 +13699,12 @@ }, /turf/simulated/floor/wood/fancy/light, /area/ninja/outpost) +"gxu" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "gyg" = ( /obj/effect/turf_decal/siding/wood{ dir = 6 @@ -13688,9 +13818,9 @@ /area/syndicate_mothership/control) "gAp" = ( /obj/structure/closet/cardboard{ - icon_state = "cardboard_librarian"; icon_closed = "cardboard_librarian"; - icon_opened = "cardboard_librarian_open" + icon_opened = "cardboard_librarian_open"; + icon_state = "cardboard_librarian" }, /obj/item/paper/central_command/archive/memes{ info = "
"; @@ -13763,6 +13893,12 @@ icon_state = "darkfull" }, /area/syndicate_mothership/jail) +"gDh" = ( +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "gDk" = ( /obj/machinery/mech_bay_recharge_port{ dir = 8 @@ -13932,6 +14068,12 @@ }, /turf/simulated/floor/indestructible/asteroid, /area/syndicate_mothership/outside) +"gIp" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "gIz" = ( /obj/effect/decal/cleanable/confetti, /turf/simulated/floor/plasteel{ @@ -14478,6 +14620,12 @@ icon_state = "dark" }, /area/centcom/jail) +"gYf" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "gYq" = ( /obj/effect/turf_decal/stripes/asteroid/corner{ dir = 4 @@ -14694,14 +14842,14 @@ name = "Food CC Spawner #1" }, /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 6; - pixel_y = -1; pixel_x = 1; - density = 0 + pixel_y = -1 }, /obj/structure/window/reinforced{ - layer = 3.1; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); + layer = 3.1 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -15094,6 +15242,12 @@ icon_state = "freezerfloor" }, /area/syndicate_mothership) +"hsP" = ( +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "hsS" = ( /obj/effect/turf_decal/siding/wood{ dir = 4 @@ -15118,6 +15272,12 @@ icon_state = "darkyellowaltstrip" }, /area/centcom/zone3) +"htF" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "htL" = ( /obj/structure/flora/grass/brown, /obj/structure/flora/grass/both, @@ -15716,6 +15876,11 @@ /obj/effect/turf_decal/siding/wood/corner, /turf/simulated/floor/carpet/cyan, /area/ninja/outpost) +"hLb" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/loading/three) "hLh" = ( /turf/simulated/floor/indestructible/grass, /area/ninja/outside) @@ -15876,15 +16041,15 @@ "hNK" = ( /obj/structure/chair/sofa/right, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/arcade, /area/centcom/zone1) "hNX" = ( /obj/structure/railing{ - layer = 4.3; - density = 0 + density = 0; + layer = 4.3 }, /obj/effect/turf_decal/tile/neutral{ color = "black"; @@ -15962,6 +16127,12 @@ }, /turf/simulated/floor/shuttle/plating, /area/shuttle/specops) +"hQZ" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "hRt" = ( /obj/structure/curtain/open/shower/security, /turf/simulated/wall/indestructible/fakeglass, @@ -16049,8 +16220,8 @@ /area/centcom/specops) "hTK" = ( /obj/machinery/conveyor{ - id = "SFBQMLoad2"; - dir = 5 + dir = 5; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -16170,9 +16341,9 @@ "hYY" = ( /obj/effect/decal/warning_stripes/northeastsouth, /obj/machinery/door/poddoor/shutters/preopen/invincible{ + dir = 2; id_tag = "ERT_armory_lvl1"; - name = "Armory level 1"; - dir = 2 + name = "Armory level 1" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -16315,8 +16486,8 @@ /area/ninja/outpost) "idQ" = ( /obj/machinery/conveyor/inverted{ - id = "CC_crate"; - dir = 10 + dir = 10; + id = "CC_crate" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -16454,8 +16625,8 @@ /obj/docking_port/mobile/emergency{ dwidth = 11; height = 18; - width = 29; - port_direction = 8 + port_direction = 8; + width = 29 }, /obj/docking_port/stationary{ dir = 4; @@ -16641,6 +16812,11 @@ icon_state = "alien18" }, /area/abductor_ship) +"ilJ" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/loading/two) "imw" = ( /obj/machinery/light{ dir = 1 @@ -16996,6 +17172,12 @@ icon_state = "floor12" }, /area/shuttle/syndicate) +"iuR" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "ivb" = ( /obj/machinery/door_control/secure{ id = "Admin_shuttle_access"; @@ -17420,10 +17602,10 @@ height = 5; id = "sst"; name = "SST shuttle"; - roundstart_move = "sst_away"; - width = 11; port_direction = 8; - preferred_direction = 2 + preferred_direction = 2; + roundstart_move = "sst_away"; + width = 11 }, /obj/docking_port/stationary{ dir = 8; @@ -17442,10 +17624,10 @@ color = "#666666"; damtype = "burn"; health = 1250; + name = "Quarantine Pulse Turret"; region_max = 12; scan_range = 12; - shot_delay = 8; - name = "Quarantine Pulse Turret" + shot_delay = 8 }, /obj/machinery/door/poddoor/shutters/invincible/fake_r_wall{ dir = 2; @@ -17624,11 +17806,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 183; teleport_y = 63; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -17989,6 +18171,12 @@ icon_state = "dark" }, /area/syndicate_mothership/control) +"iRY" = ( +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + icon_state = "darkyellowalt" + }, +/area/centcom/supply) "iSs" = ( /obj/effect/turf_decal{ dir = 5; @@ -18507,6 +18695,12 @@ }, /turf/simulated/floor/indestructible/asteroid, /area/syndicate_mothership/outside) +"jfm" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "jfv" = ( /obj/machinery/computer/shuttle/ruins_transport_shuttle, /turf/simulated/floor/shuttle{ @@ -18695,10 +18889,10 @@ "jkL" = ( /obj/machinery/door_control/secure{ id = "CC_Armory_SRT"; - pixel_y = -25; - req_access = list(114); + name = "SRT Team 1"; pixel_x = 25; - name = "SRT Team 1" + pixel_y = -25; + req_access = list(114) }, /turf/simulated/floor/plasteel{ icon_state = "navybluealtstrip" @@ -19038,8 +19232,8 @@ /area/syndicate_mothership/outside) "jsa" = ( /obj/machinery/conveyor/inverted{ - id = "SFBQMLoad2"; - dir = 6 + dir = 6; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -19465,6 +19659,15 @@ /obj/structure/filingcabinet/employment, /turf/simulated/floor/bluegrid, /area/centcom/bridge) +"jCl" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "jCo" = ( /obj/structure/chair, /obj/effect/decal/cleanable/blood, @@ -19673,8 +19876,8 @@ /obj/machinery/door_control/secure{ id = "CC_supply_external2"; name = "Supply Internal Shutters"; - req_access = list(114); - pixel_x = -24 + pixel_x = -24; + req_access = list(114) }, /turf/simulated/floor/plasteel{ dir = 8; @@ -19697,16 +19900,16 @@ }, /obj/structure/fans/tiny, /obj/docking_port/mobile{ + alone_shuttle = 1; dir = 8; dwidth = 3; height = 4; id = "ruins_civil_shuttle"; name = "Regular Civilian Shuttle"; + port_direction = 4; rebuildable = 1; roundstart_move = "spacebar"; - alone_shuttle = 1; - width = 6; - port_direction = 4 + width = 6 }, /turf/simulated/floor/shuttle{ icon_state = "floor3" @@ -19742,8 +19945,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "N2"; - dir = 1 + dir = 1; + id = "N2" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -19827,6 +20030,18 @@ "jJA" = ( /turf/simulated/floor/carpet/red, /area/shuttle/administration) +"jJD" = ( +/obj/machinery/door_control/secure{ + id = "ERT_Supply_Pod"; + name = "ERT Supply Pod Loading Zone"; + pixel_x = 24; + pixel_y = 24; + req_access = list(114) + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/specops) "jJS" = ( /turf/simulated/floor/plasteel{ dir = 4; @@ -20041,10 +20256,10 @@ height = 5; id = "mining"; name = "mining shuttle"; + port_direction = 4; rebuildable = 1; roundstart_move = "mining_home"; - width = 7; - port_direction = 4 + width = 7 }, /obj/structure/fans/tiny, /obj/machinery/door/airlock/shuttle{ @@ -20345,6 +20560,12 @@ water_overlay_icon_state = null }, /area/syndicate_mothership/outside) +"jXp" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "jXt" = ( /obj/structure/window/reinforced{ dir = 8 @@ -20556,6 +20777,15 @@ }, /turf/simulated/floor/carpet/arcade, /area/syndicate_mothership/infteam) +"kek" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "keo" = ( /turf/simulated/wall/indestructible/iron, /area/syndicate_mothership) @@ -20848,11 +21078,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 183; teleport_y = 62; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -20873,8 +21103,8 @@ dir = 8 }, /turf/simulated/floor/plasteel{ - icon_state = "arrival"; - dir = 10 + dir = 10; + icon_state = "arrival" }, /area/centcom/evac) "kio" = ( @@ -20994,10 +21224,10 @@ color = "#666666"; damtype = "burn"; health = 1250; + name = "Quarantine Pulse Turret"; region_max = 12; scan_range = 12; - shot_delay = 8; - name = "Quarantine Pulse Turret" + shot_delay = 8 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -21262,8 +21492,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl4"; - name = "Armory level 4"; - layer = 5 + layer = 5; + name = "Armory level 4" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -21288,8 +21518,8 @@ /area/syndicate_mothership/control) "kse" = ( /obj/structure/window/reinforced{ - layer = 3.1; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); + layer = 3.1 }, /turf/simulated/floor/wood{ icon_state = "light-fancy-wood" @@ -21513,11 +21743,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 183; teleport_y = 61; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -21787,9 +22017,9 @@ /area/syndicate_mothership/elite_squad) "kGN" = ( /obj/structure/window/reinforced{ + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); dir = 1; - layer = 2; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + layer = 2 }, /turf/simulated/floor/plasteel{ dir = 5; @@ -22357,10 +22587,10 @@ height = 18; id = "trade_sol"; name = "sol trade shuttle"; - roundstart_move = "trade_sol_base"; - width = 15; port_direction = 2; - preferred_direction = 8 + preferred_direction = 8; + roundstart_move = "trade_sol_base"; + width = 15 }, /obj/machinery/door/airlock/shuttle/glass{ id_tag = "s_docking_airlock" @@ -22865,6 +23095,11 @@ icon_state = "dark" }, /area/centcom/zone1) +"lbx" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/loading/four) "lbH" = ( /obj/effect/turf_decal/stripes/black{ do_not_delete_me = 1 @@ -22894,6 +23129,11 @@ icon_state = "darkredalt" }, /area/centcom/jail) +"lbZ" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/pod_storage) "lcq" = ( /obj/machinery/atmospherics/pipe/manifold/hidden, /turf/simulated/floor/plasteel{ @@ -22959,8 +23199,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 2; id_tag = "ERT_armory_lvl3"; - name = "Armory level 3"; - layer = 5 + layer = 5; + name = "Armory level 3" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -23115,8 +23355,8 @@ /area/centcom/bridge) "lhS" = ( /turf/simulated/floor/plasteel{ - icon_state = "darkgreencorners"; - dir = 8 + dir = 8; + icon_state = "darkgreencorners" }, /area/centcom/specops) "lhU" = ( @@ -23325,8 +23565,8 @@ "lmr" = ( /obj/machinery/syndiepad/receivepad, /obj/machinery/conveyor/inverted{ - id = "SFBQMLoad"; - dir = 5 + dir = 5; + id = "SFBQMLoad" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -23762,6 +24002,12 @@ tag = "icon-stage_stairs" }, /area/syndicate_mothership) +"lwT" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "lwX" = ( /obj/effect/decal/warning_stripes/north, /obj/effect/decal/warning_stripes/south, @@ -24034,6 +24280,19 @@ icon_state = "dark" }, /area/syndicate_mothership/control) +"lDl" = ( +/obj/machinery/door/airlock/centcom{ + name = "Supply Pods Load 2"; + req_access = list(114) + }, +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "CC_Supply_Pods" + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supply) "lDp" = ( /obj/machinery/door/airlock/hatch/syndicate{ name = "Syndicate Base" @@ -24157,6 +24416,11 @@ name = "floor" }, /area/syndicate_mothership/elite_squad) +"lFA" = ( +/turf/simulated/floor/plasteel{ + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "lFP" = ( /obj/structure/sink{ dir = 4; @@ -24654,8 +24918,8 @@ dir = 1 }, /obj/machinery/portable_atmospherics/canister/oxygen{ - maximum_pressure = 50000; - anchored = 1 + anchored = 1; + maximum_pressure = 50000 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -24769,8 +25033,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl2"; - name = "Armory level 2"; - layer = 5 + layer = 5; + name = "Armory level 2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -25613,12 +25877,12 @@ name = "Ladder" }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; + mobs_only = 1; teleport_x = 135; teleport_y = 16; - teleport_z = 1; - mobs_only = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -25646,8 +25910,8 @@ /obj/structure/bed, /obj/item/bedsheet/rd, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/black, /area/centcom/zone1) @@ -25975,8 +26239,8 @@ id = "CC_toilet_unit2"; name = "Door Bolt Control"; normaldoorcontrol = 1; - specialfunctions = 4; - pixel_x = 25 + pixel_x = 25; + specialfunctions = 4 }, /turf/simulated/floor/plasteel{ icon_state = "white" @@ -26352,8 +26616,8 @@ /obj/machinery/door_control/secure{ id = "CC_supply_internal2"; name = "Supply External Shutters"; - req_access = list(114); - pixel_x = -24 + pixel_x = -24; + req_access = list(114) }, /turf/simulated/floor/plasteel{ dir = 8; @@ -26951,6 +27215,12 @@ }, /turf/simulated/floor/wood, /area/centcom/zone3) +"mRm" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "mRs" = ( /obj/structure/flora/ausbushes/brflowers, /obj/structure/flora/ausbushes/ppflowers, @@ -27085,8 +27355,8 @@ /area/centcom/bridge) "mUi" = ( /obj/machinery/vending/wallmed{ - pixel_y = -32; - pixel_x = -32 + pixel_x = -32; + pixel_y = -32 }, /turf/simulated/floor/plasteel{ icon_state = "darkyellowalt" @@ -27485,19 +27755,19 @@ /obj/effect/decal/remains, /obj/effect/turf_decal/tile/neutral{ alpha = 100; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /obj/item/fish/goldfish{ - pixel_y = 10; - pixel_x = 16 + pixel_x = 16; + pixel_y = 10 }, /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 5; pixel_x = -32; - pixel_y = -32; - density = 0 + pixel_y = -32 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/zone2) @@ -27907,15 +28177,15 @@ /area/syndicate_mothership/control) "njh" = ( /obj/structure/window/reinforced{ + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); dir = 1; - layer = 2; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + layer = 2 }, /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 9; - pixel_y = 1; pixel_x = -1; - density = 0 + pixel_y = 1 }, /turf/simulated/floor/plasteel{ dir = 1; @@ -28123,6 +28393,11 @@ "nnz" = ( /turf/simulated/wall/shuttle/onlyselfsmooth, /area/shuttle/supply) +"nnC" = ( +/turf/simulated/floor/plasteel{ + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "nnM" = ( /obj/machinery/recharge_station/ert, /obj/machinery/light{ @@ -28370,8 +28645,8 @@ /area/centcom/specops) "nsV" = ( /obj/machinery/conveyor_switch/oneway{ - id = "SFBQMLoad2"; - dir = 8 + dir = 8; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ icon_state = "darkyellowcornersalt" @@ -29110,11 +29385,11 @@ /area/centcom/specops) "nIr" = ( /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 216; teleport_y = 26; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -29375,6 +29650,11 @@ icon_state = "wood-broken6" }, /area/centcom/zone2) +"nNQ" = ( +/turf/simulated/floor/plasteel{ + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "nOe" = ( /obj/structure/window/reinforced{ dir = 8 @@ -29572,11 +29852,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 175; teleport_y = 60; - teleport_z = 1; - icon_state = "x2"; - icon = 'icons/mob/screen_gen.dmi' + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -29919,8 +30199,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl2"; - name = "Armory level 2"; - layer = 5 + layer = 5; + name = "Armory level 2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -30290,8 +30570,8 @@ color = "red" }, /obj/machinery/conveyor{ - id = "SFBQMLoad2"; - dir = 1 + dir = 1; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -30360,8 +30640,8 @@ "olk" = ( /obj/machinery/syndiepad/loadpad, /obj/machinery/conveyor/inverted{ - id = "SFBQMLoad2"; - dir = 6 + dir = 6; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -30540,9 +30820,9 @@ /area/syndicate_mothership) "opi" = ( /obj/structure/closet/cardboard{ - icon_state = "cardboard_cargo"; icon_closed = "cardboard_cargo"; - icon_opened = "cardboard_cargo_open" + icon_opened = "cardboard_cargo_open"; + icon_state = "cardboard_cargo" }, /mob/living/simple_animal/hostile/mimic{ faction = list("neutral"); @@ -30833,8 +31113,8 @@ /area/shuttle/siberia) "ovI" = ( /obj/structure/closet/acloset{ - name = "Undercover officer's closet"; - desc = "It's a basic storage unit." + desc = "It's a basic storage unit."; + name = "Undercover officer's closet" }, /obj/item/flashlight, /obj/item/encryptionkey/centcom, @@ -31113,8 +31393,8 @@ /area/syndicate_mothership/jail) "oDS" = ( /obj/machinery/conveyor/inverted{ - id = "CC_crate"; - dir = 9 + dir = 9; + id = "CC_crate" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -31194,8 +31474,8 @@ "oFM" = ( /obj/structure/chair/sofa/right, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/black, /area/centcom/zone1) @@ -31405,6 +31685,15 @@ icon_state = "dark" }, /area/centcom/supply) +"oKI" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "oKN" = ( /obj/item/flag/syndi, /obj/structure/curtain/black{ @@ -31457,9 +31746,9 @@ /obj/machinery/door_control/secure{ id = "gamma shuttle"; name = "Gamma Armory Shutters"; - req_access = list(114); pixel_x = 24; - pixel_y = 26 + pixel_y = 26; + req_access = list(114) }, /turf/simulated/floor/plasteel{ dir = 4; @@ -31500,8 +31789,8 @@ }, /obj/structure/table, /obj/machinery/computer/library/public{ - pixel_y = 4; - pixel_x = 1 + pixel_x = 1; + pixel_y = 4 }, /obj/machinery/ai_status_display{ pixel_y = 32 @@ -31635,10 +31924,10 @@ /area/syndicate_mothership/control) "oOK" = ( /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 6; - pixel_y = -1; pixel_x = 1; - density = 0 + pixel_y = -1 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -31690,6 +31979,12 @@ icon_state = "darkfull" }, /area/syndicate_mothership/jail) +"oPk" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "oPx" = ( /obj/structure/chair/comfy/shuttle{ dir = 8 @@ -31860,8 +32155,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 2; id_tag = "ERT_armory_lvl3"; - name = "Armory level 3"; - layer = 5 + layer = 5; + name = "Armory level 3" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -32133,6 +32428,12 @@ }, /turf/simulated/floor/carpet, /area/centcom/evac) +"paz" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "pbe" = ( /obj/structure/closet/crate/can, /turf/simulated/floor/wood, @@ -32293,8 +32594,8 @@ }, /obj/docking_port/mobile/supply{ dir = 2; - preferred_direction = 8; - port_direction = 4 + port_direction = 4; + preferred_direction = 8 }, /turf/simulated/wall/shuttle, /area/shuttle/supply) @@ -32331,6 +32632,15 @@ icon_state = "navyblue" }, /area/centcom/specops) +"pgz" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkyellowalt" + }, +/area/centcom/supply) "phK" = ( /obj/effect/decal/syndie_logo, /obj/effect/turf_decal/stripes/black{ @@ -32666,8 +32976,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "O2"; - dir = 1 + dir = 1; + id = "O2" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -32694,13 +33004,17 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "O2"; - dir = 1 + dir = 1; + id = "O2" }, /turf/simulated/floor/plasteel{ icon_state = "dark" }, /area/centcom/supply) +"psX" = ( +/obj/machinery/vending/ntcrates, +/turf/simulated/floor/plating, +/area/centcom/specops) "pte" = ( /obj/structure/table/wood{ color = "#996633" @@ -33206,9 +33520,9 @@ height = 12; id = "ferry"; name = "ferry shuttle"; + preferred_direction = 4; roundstart_move = "ferry_away"; - width = 5; - preferred_direction = 4 + width = 5 }, /obj/docking_port/stationary{ dir = 8; @@ -33416,11 +33730,11 @@ layer = 9 }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 175; teleport_y = 61; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -33575,6 +33889,15 @@ icon_state = "darkredalt" }, /area/centcom/jail) +"pPo" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "pPN" = ( /obj/effect/landmark/syndicate_commando{ tag = "Commando" @@ -33723,8 +34046,8 @@ "pST" = ( /obj/machinery/syndiepad/loadpad, /obj/machinery/conveyor{ - id = "SFBQMLoad2"; - dir = 8 + dir = 8; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -34041,6 +34364,12 @@ /obj/structure/window/full/shuttle/ninja, /turf/simulated/floor/shuttle/plating, /area/shuttle/ninja) +"pYT" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "pYZ" = ( /obj/structure/curtain/open/shower/security{ pixel_x = 32 @@ -34086,8 +34415,8 @@ name = "centcom bay 3"; top_left_corner = 8; top_right_corner = 1; - width = 5; - turf_type = /turf/simulated/floor/plating/airless + turf_type = /turf/simulated/floor/plating/airless; + width = 5 }, /turf/simulated/floor/shuttle/plating, /area/shuttle/specops) @@ -34399,8 +34728,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "N2O"; - dir = 1 + dir = 1; + id = "N2O" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -34459,8 +34788,8 @@ /obj/structure/bed, /obj/item/bedsheet/qm, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/arcade, /area/centcom/zone1) @@ -34475,9 +34804,9 @@ /obj/effect/decal/warning_stripes/south, /obj/effect/turf_decal/caution/stand_clear, /obj/machinery/door/poddoor/shutters/preopen/invincible{ + dir = 2; id_tag = "ERT_armory_lvl1"; - name = "Armory level 1"; - dir = 2 + name = "Armory level 1" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -34655,6 +34984,12 @@ icon_state = "dark" }, /area/centcom/zone2) +"qoL" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "qoS" = ( /obj/machinery/suit_storage_unit/standard_unit, /obj/effect/turf_decal/stripes/black{ @@ -34948,6 +35283,15 @@ icon_state = "floor4" }, /area/shuttle/gamma) +"qtl" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "qto" = ( /turf/simulated/floor/plasteel{ dir = 8; @@ -34983,8 +35327,8 @@ "quA" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/machinery/conveyor{ - id = "CO2"; - dir = 1 + dir = 1; + id = "CO2" }, /obj/machinery/portable_atmospherics/canister/carbon_dioxide{ maximum_pressure = 50000 @@ -35167,6 +35511,12 @@ }, /turf/simulated/floor/shuttle, /area/shuttle/nt_droppod) +"qAI" = ( +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "qAL" = ( /obj/structure/rack/holorack, /obj/item/clothing/under/assistantformal, @@ -36587,6 +36937,12 @@ /obj/structure/AIcore, /turf/simulated/floor/shuttle/objective_check/vox, /area/shuttle/vox) +"rdz" = ( +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "rdZ" = ( /obj/machinery/light/small{ dir = 8 @@ -36796,9 +37152,9 @@ "riy" = ( /obj/effect/turf_decal/tile/neutral{ alpha = 85; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /obj/structure/window/reinforced{ dir = 4; @@ -36806,10 +37162,10 @@ }, /obj/structure/flora/rock/pile, /obj/structure/window/reinforced/survival_pod{ + density = 0; dir = 6; - pixel_y = -7; pixel_x = 1; - density = 0 + pixel_y = -7 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/specops) @@ -37746,11 +38102,11 @@ /area/syndicate_mothership/elite_squad) "rFY" = ( /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 205; teleport_y = 89; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/space, /area/space) @@ -37984,16 +38340,16 @@ /obj/structure/fans/tiny, /obj/machinery/door/airlock/external, /obj/docking_port/mobile{ + alone_shuttle = 1; dir = 8; dwidth = 2; height = 7; id = "funeral"; name = "Funeral shuttle"; - roundstart_move = "graveyard_dock"; - alone_shuttle = 1; - width = 10; port_direction = 8; - preferred_direction = 2 + preferred_direction = 2; + roundstart_move = "graveyard_dock"; + width = 10 }, /turf/simulated/floor/shuttle/plating, /area/shuttle/funeral) @@ -38052,7 +38408,7 @@ /turf/simulated/floor/carpet/red, /area/centcom/bridge) "rLk" = ( -/obj/machinery/computer/borgupload, +/obj/machinery/computer/aiupload/cyborg, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -38081,8 +38437,8 @@ icon_state = "tile_full" }, /obj/structure/railing{ - layer = 4.3; - density = 0 + density = 0; + layer = 4.3 }, /turf/simulated/floor/plasteel{ dir = 8; @@ -38141,8 +38497,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "N2"; - dir = 1 + dir = 1; + id = "N2" }, /obj/machinery/portable_atmospherics/canister/nitrogen{ maximum_pressure = 50000 @@ -38726,6 +39082,19 @@ /obj/structure/window/full/shuttle/gray, /turf/simulated/floor/plating/airless, /area/shuttle/syndicate) +"saq" = ( +/obj/machinery/door/airlock/centcom{ + name = "Supply Pods Load 1"; + req_access = list(114) + }, +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "CC_Supply_Pods" + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supply) "sas" = ( /turf/simulated/floor/plasteel{ dir = 6; @@ -38939,9 +39308,9 @@ /obj/structure/flora/ausbushes/stalkybush, /obj/effect/turf_decal/tile/neutral{ alpha = 85; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/specops) @@ -38988,8 +39357,8 @@ }, /obj/effect/decal/warning_stripes/yellow/hollow, /obj/machinery/conveyor{ - id = "Toxin"; - dir = 1 + dir = 1; + id = "Toxin" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -39026,9 +39395,9 @@ }, /obj/effect/turf_decal/tile/neutral{ alpha = 85; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/specops) @@ -39395,6 +39764,15 @@ icon_state = "grimy" }, /area/centcom/zone1) +"srJ" = ( +/obj/machinery/light{ + dir = 4; + switchcount = 50 + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/specops) "sse" = ( /obj/effect/decal/syndie_logo{ icon_state = "logo11" @@ -39918,10 +40296,10 @@ /obj/structure/fans/tiny, /obj/effect/decal/warning_stripes/white, /obj/machinery/door/airlock/external{ + hackProof = 1; id_tag = "supply_away"; name = "Central Command Supply"; - req_access = list(31); - hackProof = 1 + req_access = list(31) }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -40014,8 +40392,8 @@ /obj/structure/bed, /obj/item/bedsheet/clown, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/black, /area/centcom/zone1) @@ -40061,10 +40439,30 @@ }, /turf/simulated/floor/shuttle/plating, /area/shuttle/ninja) +"sIz" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/specops) "sIC" = ( /obj/structure/table/wood, /turf/simulated/floor/carpet, /area/centcom/evac) +"sIQ" = ( +/obj/machinery/door_control/secure{ + id = "CC_Supply_Pods"; + name = "Supply Pods Load"; + pixel_y = -24; + req_access = list(114) + }, +/obj/machinery/light, +/turf/simulated/floor/plasteel{ + icon_state = "darkyellowalt" + }, +/area/centcom/supply) "sIY" = ( /obj/effect/decal/syndie_logo{ icon_state = "logo13" @@ -40576,6 +40974,19 @@ /obj/item/reagent_containers/food/snacks/sliceable/birthdaycake, /turf/simulated/floor/wood, /area/centcom/specops) +"sVB" = ( +/obj/machinery/door/airlock/centcom{ + name = "Supply Pods Load 4"; + req_access = list(114) + }, +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "CC_Supply_Pods" + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supply) "sVH" = ( /obj/machinery/vending/robotics/nt/gygax, /turf/simulated/floor/plasteel{ @@ -40726,76 +41137,76 @@ "sYS" = ( /obj/structure/table/reinforced, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = -4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = -4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = -4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = -4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = 4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = 4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = 4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ + icon_state = "walkietalkie_sec"; pixel_x = 4; - pixel_y = 5; - icon_state = "walkietalkie_sec" + pixel_y = 5 }, /obj/item/radio{ - pixel_x = -4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = -4 }, /obj/item/radio{ - pixel_x = -4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = -4 }, /obj/item/radio{ - pixel_x = -4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = -4 }, /obj/item/radio{ - pixel_x = -4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = -4 }, /obj/item/radio{ - pixel_x = 4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = 4 }, /obj/item/radio{ - pixel_x = 4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = 4 }, /obj/item/radio{ - pixel_x = 4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = 4 }, /obj/item/radio{ - pixel_x = 4; - icon_state = "walkietalkie_sec" + icon_state = "walkietalkie_sec"; + pixel_x = 4 }, /turf/simulated/floor/plasteel{ icon_state = "navyblue" @@ -40858,6 +41269,11 @@ icon_state = "bot" }, /area/shuttle/escape) +"sZK" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/loading/ert) "sZP" = ( /turf/simulated/floor/plasteel{ dir = 8; @@ -41188,6 +41604,12 @@ icon_state = "white" }, /area/centcom/zone1) +"thd" = ( +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "thl" = ( /obj/structure/table/glass, /obj/effect/spawner/lootdrop{ @@ -41251,8 +41673,8 @@ /area/shuttle/syndicate_sit) "tjx" = ( /obj/machinery/conveyor/inverted{ - id = "SFBQMLoad"; - dir = 5 + dir = 5; + id = "SFBQMLoad" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -41344,8 +41766,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "N2O"; - dir = 1 + dir = 1; + id = "N2O" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -41932,18 +42354,18 @@ pixel_y = -4 }, /obj/item/paper{ - pixel_x = 9; - info = "Обед через час, работа через вчера" + info = "Обед через час, работа через вчера"; + pixel_x = 9 }, /obj/item/paper{ - pixel_y = -9; + info = "Пофиксить в моем кабинете рантайм трубы в трубе... опять"; pixel_x = 7; - info = "Пофиксить в моем кабинете рантайм трубы в трубе... опять" + pixel_y = -9 }, /obj/item/paper{ - pixel_y = -6; + info = "Начать делать очередную новую станцию и сгореть на втором часу работы в самокопании"; pixel_x = -5; - info = "Начать делать очередную новую станцию и сгореть на втором часу работы в самокопании" + pixel_y = -6 }, /obj/machinery/atmospherics/pipe/manifold/visible{ dir = 8 @@ -42106,8 +42528,8 @@ /area/syndicate_mothership/infteam) "tBG" = ( /obj/machinery/conveyor{ - id = "CC_crate"; - dir = 4 + dir = 4; + id = "CC_crate" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -42243,11 +42665,11 @@ name = "Ladder" }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; teleport_x = 203; teleport_y = 41; - teleport_z = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ dir = 9; @@ -42409,6 +42831,13 @@ icon_state = "darkfull" }, /area/centcom/specops) +"tHL" = ( +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "ShitRainSupply" + }, +/turf/simulated/floor/plating, +/area/centcom/specops) "tHM" = ( /obj/effect/turf_decal/siding/blue{ dir = 1 @@ -42437,6 +42866,17 @@ }, /turf/simulated/floor/shuttle/transparent_floor, /area/shuttle/ninja) +"tIk" = ( +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "ERT_Supply_Pod" + }, +/obj/machinery/door/airlock/centcom{ + name = "ERT Supply Pod Loading Zone"; + req_access = list(114) + }, +/turf/simulated/floor/plating, +/area/centcom/specops) "tIn" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/wood{ @@ -42539,7 +42979,7 @@ /area/syndicate_mothership/control) "tKn" = ( /obj/structure/rack, -/obj/item/aiModule/syndicate, +/obj/item/ai_module/syndicate, /obj/item/multitool/ai_detect, /obj/item/jammer, /turf/simulated/floor/shuttle/objective_check/vox, @@ -43595,8 +44035,8 @@ /area/centcom/zone3) "ulG" = ( /obj/structure/railing{ - layer = 4.3; - density = 0 + density = 0; + layer = 4.3 }, /turf/simulated/floor/plasteel{ color = "gray"; @@ -44300,6 +44740,12 @@ "uAd" = ( /turf/simulated/wall/indestructible/iron, /area/syndicate_mothership/outside) +"uAg" = ( +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "uAh" = ( /obj/structure/chair/comfy/shuttle{ dir = 4 @@ -44511,8 +44957,8 @@ icon_state = "tile_full" }, /obj/structure/railing{ - pixel_y = 32; - density = 0 + density = 0; + pixel_y = 32 }, /turf/simulated/floor/plasteel{ dir = 8; @@ -44919,8 +45365,8 @@ /area/centcom/zone1) "uMD" = ( /obj/machinery/conveyor{ - id = "SFBQMLoad"; - dir = 6 + dir = 6; + id = "SFBQMLoad" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -45016,8 +45462,8 @@ /area/centcom/zone1) "uNG" = ( /obj/machinery/conveyor{ - id = "SFBQMLoad2"; - dir = 1 + dir = 1; + id = "SFBQMLoad2" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -45033,9 +45479,9 @@ /obj/item/ship_in_a_bottle, /obj/effect/turf_decal/tile/neutral{ alpha = 100; + icon = 'icons/misc/beach.dmi'; icon_state = "seadeep"; - layer = 9; - icon = 'icons/misc/beach.dmi' + layer = 9 }, /turf/simulated/floor/indestructible/beach/water/deep/sand_floor, /area/centcom/zone2) @@ -45155,8 +45601,8 @@ /area/shuttle/vox) "uRC" = ( /obj/structure/falsewall/reinforced{ - req_access = list(114); - layer = 5 + layer = 5; + req_access = list(114) }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -45612,6 +46058,15 @@ icon_state = "bot" }, /area/shuttle/escape) +"vdy" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "vdH" = ( /obj/structure/reagent_dispensers/watertank, /obj/effect/decal/cleanable/dirt, @@ -45887,6 +46342,11 @@ icon_state = "fancy-wood-cherry" }, /area/shuttle/trade/sol) +"vjb" = ( +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supplypod/supplypod_temp_holding) "vjo" = ( /obj/effect/turf_decal/box/corners{ dir = 1 @@ -45912,10 +46372,10 @@ /obj/docking_port/stationary{ dwidth = 3; height = 7; - name = "Emerjency droppod dock"; id = "shit_rain_base"; - width = 7; - pixel_y = -32 + name = "Emerjency droppod dock"; + pixel_y = -32; + width = 7 }, /turf/simulated/floor/shuttle, /area/shuttle/nt_droppod) @@ -45988,8 +46448,8 @@ /area/syndicate_mothership/jail) "vng" = ( /turf/simulated/floor/plasteel{ - icon_state = "darkyellowcorners"; - dir = 1 + dir = 1; + icon_state = "darkyellowcorners" }, /area/centcom/specops) "vnv" = ( @@ -46074,11 +46534,17 @@ /obj/structure/bed, /obj/item/bedsheet/mime, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet, /area/centcom/zone1) +"voW" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/four) "vpg" = ( /turf/simulated/floor/shuttle{ icon_state = "floor4" @@ -46352,8 +46818,8 @@ pixel_y = -1 }, /obj/structure/window/reinforced{ - layer = 3.1; - armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100) + armor = list("melee"=100,"bullet"=100,"laser"=0,"energy"=0,"bomb"=25,"bio"=100,"rad"=100,"fire"=80,"acid"=100); + layer = 3.1 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -46768,10 +47234,10 @@ /area/syndicate_mothership) "vGX" = ( /obj/item/flashlight/lamp/green{ - pixel_x = -6; - pixel_y = 16; + icon = 'icons/obj/library.dmi'; icon_state = "bigscenner"; - icon = 'icons/obj/library.dmi' + pixel_x = -6; + pixel_y = 16 }, /obj/item/paper_bin/nanotrasen{ pixel_x = -2; @@ -46811,10 +47277,12 @@ }, /area/syndicate_mothership) "vHF" = ( -/obj/machinery/door/poddoor/shutters/invincible{ - id_tag = "ShitRainSupply" +/obj/machinery/door/airlock/centcom{ + name = "ERT Supply Pods" + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" }, -/turf/simulated/floor/plating, /area/centcom/specops) "vHT" = ( /obj/effect/turf_decal/delivery, @@ -46843,8 +47311,8 @@ /area/shuttle/vox) "vIU" = ( /obj/structure/falsewall/reinforced{ - req_access = list(114); - layer = 2.9 + layer = 2.9; + req_access = list(114) }, /obj/effect/mine/sound/bwoink{ invisibility = 1; @@ -47417,6 +47885,12 @@ icon_state = "grimy" }, /area/centcom/jail) +"vUp" = ( +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/three) "vUy" = ( /obj/effect/decal/warning_stripes/yellow, /obj/machinery/door/airlock/gold/glass{ @@ -47625,6 +48099,12 @@ icon_state = "dark" }, /area/shuttle/administration) +"vZp" = ( +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/two) "vZr" = ( /obj/machinery/light, /obj/structure/chair/comfy/shuttle{ @@ -47764,12 +48244,12 @@ name = "Ladder" }, /obj/effect/step_trigger/teleporter{ + icon = 'icons/mob/screen_gen.dmi'; + icon_state = "x2"; + mobs_only = 1; teleport_x = 135; teleport_y = 16; - teleport_z = 1; - mobs_only = 1; - icon = 'icons/mob/screen_gen.dmi'; - icon_state = "x2" + teleport_z = 1 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -48633,39 +49113,39 @@ color = "#ff9999"; id = "ERT_armory_lvl3"; name = "ERT Armory Level 3"; - req_access = list(114); pixel_x = -6; - pixel_y = 8 + pixel_y = 8; + req_access = list(114) }, /obj/machinery/door_control/secure{ color = "#99ff99"; id = "ERT_armory_lvl1"; name = "ERT Armory Level 1"; - req_access = list(114); pixel_x = -6; - pixel_y = -8 + pixel_y = -8; + req_access = list(114) }, /obj/machinery/door_control/secure{ color = "#9999ff"; id = "ERT_armory_lvl2"; name = "ERT Armory Level 2"; - req_access = list(114); - pixel_x = -6 + pixel_x = -6; + req_access = list(114) }, /obj/machinery/door_control{ id = "ERT_Quarantine"; name = "ERT Quarantine"; - req_access = list(114); + pixel_x = 6; pixel_y = 6; - pixel_x = 6 + req_access = list(114) }, /obj/machinery/door_control/secure{ color = "#ffdd99"; id = "ERT_armory_lvl4"; name = "ERT Armory Level 4"; - req_access = list(114); pixel_x = 6; - pixel_y = -14 + pixel_y = -14; + req_access = list(114) }, /obj/effect/turf_decal/siding{ color = "#444444"; @@ -48817,6 +49297,15 @@ }, /turf/simulated/floor/carpet/black, /area/centcom/specops) +"wEu" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "wEF" = ( /obj/structure/grille, /obj/machinery/door/poddoor/shutters/preopen{ @@ -48921,6 +49410,12 @@ icon_state = "darkred" }, /area/shuttle/escape) +"wJm" = ( +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "wJo" = ( /obj/machinery/door_control/secure{ id = "ERT_Drop"; @@ -49135,6 +49630,12 @@ icon_state = "darkfull" }, /area/centcom/specops) +"wPe" = ( +/turf/simulated/floor/plasteel{ + dir = 6; + icon_state = "darkyellow" + }, +/area/centcom/supplypod/loading/one) "wPq" = ( /obj/structure/noticeboard{ pixel_x = -32 @@ -49316,6 +49817,19 @@ /obj/item/storage/belt/fannypack/black, /turf/simulated/floor/wood/fancy/light, /area/centcom/specops) +"wTC" = ( +/obj/machinery/door/airlock/centcom{ + name = "Supply Pods Load 3"; + req_access = list(114) + }, +/obj/machinery/door/poddoor/shutters/invincible{ + dir = 2; + id_tag = "CC_Supply_Pods" + }, +/turf/simulated/floor/plasteel{ + icon_state = "Dark" + }, +/area/centcom/supply) "wTK" = ( /turf/simulated/floor/shuttle/objective_check{ dir = 5; @@ -49681,10 +50195,10 @@ height = 10; id = "ombra"; name = "Spider Clan Ombra"; - roundstart_move = "ombra_home"; - width = 21; port_direction = 2; - preferred_direction = 2 + preferred_direction = 2; + roundstart_move = "ombra_home"; + width = 21 }, /obj/structure/fans/tiny/invisible, /obj/docking_port/stationary{ @@ -49775,9 +50289,9 @@ height = 22; id = "syndicate"; name = "syndicate shuttle"; + port_direction = 2; roundstart_move = "syndicate_away"; - width = 18; - port_direction = 2 + width = 18 }, /obj/structure/lattice/catwalk, /obj/docking_port/stationary{ @@ -50186,8 +50700,8 @@ dir = 4 }, /obj/machinery/conveyor{ - id = "Air"; - dir = 1 + dir = 1; + id = "Air" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -50232,10 +50746,10 @@ color = "#666666"; damtype = "burn"; health = 1250; + name = "Quarantine Pulse Turret"; region_max = 12; scan_range = 12; - shot_delay = 8; - name = "Quarantine Pulse Turret" + shot_delay = 8 }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -50507,17 +51021,6 @@ }, /turf/simulated/floor/plating, /area/syndicate_mothership/cargo) -"xqF" = ( -/obj/machinery/door_control/secure{ - id = "ShitRainSupply"; - pixel_x = -24; - pixel_y = -24 - }, -/turf/simulated/floor/plasteel{ - dir = 9; - icon_state = "navybluealt" - }, -/area/centcom/specops) "xqM" = ( /obj/structure/lattice/catwalk, /obj/structure/marker_beacon{ @@ -50937,8 +51440,8 @@ /obj/machinery/door/poddoor/shutters/invincible{ dir = 1; id_tag = "ERT_armory_lvl4"; - name = "Armory level 4"; - layer = 5 + layer = 5; + name = "Armory level 4" }, /turf/simulated/floor/plasteel{ icon_state = "darkfull" @@ -51028,8 +51531,8 @@ "xCr" = ( /obj/structure/bookcase, /obj/item/paper{ - name = "Главный строить мужик репорт"; - info = "
" + info = "
"; + name = "Главный строить мужик репорт" }, /turf/simulated/floor/plating, /area/centcom/zone2) @@ -51525,8 +52028,8 @@ /obj/structure/bed, /obj/item/bedsheet/qm, /obj/effect/mine/sound/bwoink{ - layer = 2.9; - alpha = 0 + alpha = 0; + layer = 2.9 }, /turf/simulated/floor/carpet/black, /area/centcom/zone1) @@ -51680,8 +52183,8 @@ /area/centcom/evac) "xSg" = ( /obj/structure/falsewall/reinforced{ - req_access = list(114); - layer = 5 + layer = 5; + req_access = list(114) }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -51699,8 +52202,8 @@ id = "CC_toilet_unit1"; name = "Door Bolt Control"; normaldoorcontrol = 1; - specialfunctions = 4; - pixel_x = 25 + pixel_x = 25; + specialfunctions = 4 }, /turf/simulated/floor/plasteel{ icon_state = "white" @@ -52142,6 +52645,12 @@ icon_state = "dark" }, /area/centcom/evac) +"yhG" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "navyblue" + }, +/area/centcom/supplypod/loading/ert) "yhH" = ( /obj/machinery/light/small, /turf/simulated/floor/carpet, @@ -85678,15 +86187,15 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +nAN +nAN +nAN +nAN +nAN +nAN +nAN +nAN mVX mVX mVX @@ -85935,15 +86444,15 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +wJm +yhG +pPo +yhG +aHe +nAN +sIz +nAN mVX mVX mVX @@ -86192,15 +86701,15 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +rdz +sZK +sZK +sZK +eDu +nAN +bBe +nAN jMD mVX mVX @@ -86449,15 +86958,15 @@ mVX mVX mVX jMD -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +rdz +sZK +sZK +sZK +eDu +tIk +jJD +nAN jMD mVX mVX @@ -86706,15 +87215,15 @@ mVX mVX mVX jMD -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +gxu +uAg +jCl +uAg +aYk +nAN +bBe +nAN jMD mVX mVX @@ -86963,15 +87472,15 @@ mVX mVX mVX jMD -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +nAN +nAN +nAN +nAN +nAN +nAN +bBe +nAN jMD mVX mVX @@ -87225,9 +87734,9 @@ nAN nAN nAN nAN -nAN -nAN -nAN +psX +tHL +fJp nAN nAN nAN @@ -87484,7 +87993,7 @@ nAN nAN nAN nAN -nAN +srJ bBe aoW nIr @@ -87999,7 +88508,7 @@ iXI wiU wiU kmM -xqF +aPL qap kpi kpi @@ -108814,8 +109323,8 @@ bbq mVX afP dlT -yeh -tgA +aDR +jHX hAX gFx aLo @@ -109070,9 +109579,9 @@ mVX mVX mVX afP -ecV -ecV -ecV +pgz +aDR +sIQ afP afP afP @@ -109326,17 +109835,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +qAI +aiB +dTt +aiB +dtp +afP mVX nAN lRD @@ -109583,17 +110092,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +htF +aeQ +aeQ +aeQ +bIN +afP mVX mVX rFY @@ -109840,21 +110349,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +jHX +saq +htF +aeQ +aeQ +aeQ +bIN +afP mVX mVX mVX @@ -109863,6 +110368,10 @@ mVX mVX mVX nAN +lbZ +lbZ +lbZ +nAN nAN nAN nAN @@ -110097,6 +110606,17 @@ mVX mVX mVX mVX +ecV +dZk +aDR +xhN +afP +bEA +mRm +wEu +mRm +wPe +afP mVX mVX mVX @@ -110104,22 +110624,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +lbZ +lbZ +lbZ +nAN mVX mVX mVX @@ -110354,6 +110863,17 @@ mVX mVX mVX mVX +afP +pgz +aDR +iRY +afP +afP +afP +afP +afP +afP +afP mVX mVX mVX @@ -110361,22 +110881,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +lbZ +lbZ +lbZ +nAN mVX mVX mVX @@ -110611,6 +111120,17 @@ mVX mVX mVX mVX +ecV +dZk +aDR +xhN +afP +vZp +lwT +oKI +lwT +gYf +afP mVX mVX mVX @@ -110618,22 +111138,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +nAN +nAN +nAN +nAN mVX mVX mVX @@ -110868,6 +111377,17 @@ mVX mVX mVX mVX +ecV +dZk +aDR +xhN +afP +paz +ilJ +ilJ +ilJ +lFA +afP mVX mVX mVX @@ -110875,22 +111395,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +vjb +vjb +vjb +nAN mVX mVX mVX @@ -111125,6 +111634,17 @@ mVX mVX mVX mVX +ecV +dZk +aDR +jHX +lDl +paz +ilJ +ilJ +ilJ +lFA +afP mVX mVX mVX @@ -111132,22 +111652,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +vjb +vjb +vjb +nAN mVX mVX mVX @@ -111382,6 +111891,17 @@ mVX mVX mVX mVX +ecV +dZk +aDR +xhN +afP +jfm +hQZ +vdy +hQZ +bRZ +afP mVX mVX mVX @@ -111389,22 +111909,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +vjb +vjb +vjb +nAN mVX mVX mVX @@ -111639,6 +112148,17 @@ mVX mVX mVX mVX +afP +pgz +aDR +iRY +afP +afP +afP +afP +afP +afP +afP mVX mVX mVX @@ -111646,22 +112166,11 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +nAN +nAN +nAN +nAN +nAN mVX mVX mVX @@ -111896,17 +112405,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +gDh +jXp +kek +jXp +hsP +afP mVX mVX mVX @@ -112153,17 +112662,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +iuR +hLb +hLb +hLb +nNQ +afP mVX mVX mVX @@ -112410,17 +112919,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +jHX +wTC +iuR +hLb +hLb +hLb +nNQ +afP mVX mVX mVX @@ -112667,17 +113176,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +vUp +gIp +cAU +gIp +eZq +afP mVX mVX mVX @@ -112924,17 +113433,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +afP +pgz +aDR +iRY +afP +afP +afP +afP +afP +afP +afP mVX mVX mVX @@ -113181,17 +113690,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +thd +voW +eGx +voW +cJx +afP mVX mVX mVX @@ -113438,17 +113947,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +xhN +afP +qoL +lbx +lbx +lbx +nnC +afP mVX mVX mVX @@ -113695,17 +114204,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +dZk +aDR +jHX +sVB +qoL +lbx +lbx +lbx +nnC +afP mVX mVX mVX @@ -113952,17 +114461,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +ecV +fRi +yeh +aOM +afP +czW +oPk +qtl +oPk +pYT +afP mVX mVX mVX @@ -114209,17 +114718,17 @@ mVX mVX mVX mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX -mVX +afP +ecV +ecV +ecV +afP +afP +afP +afP +afP +afP +afP mVX mVX mVX diff --git a/_maps/map_files/nova/nova.dmm b/_maps/map_files/nova/nova.dmm index 1ff16844f16..a26d13eb307 100644 --- a/_maps/map_files/nova/nova.dmm +++ b/_maps/map_files/nova/nova.dmm @@ -586,8 +586,8 @@ }, /obj/effect/decal/warning_stripes/red/hollow, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "redcorner" + dir = 9; + icon_state = "red" }, /area/security/prison/cell_block/A) "aeK" = ( @@ -875,24 +875,30 @@ /turf/simulated/floor/plating, /area/hallway/secondary/exit) "agU" = ( -/obj/structure/table, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/obj/item/ai_module/oxygen, +/obj/item/ai_module/one_crew_member, +/obj/item/ai_module/purge, +/obj/item/ai_module/antimov, +/obj/structure/table/glass, /obj/machinery/door/window{ + base_state = "right"; dir = 8; - name = "High-Risk Modules"; + icon_state = "right"; + name = "Core Modules"; req_access = list(20) }, -/obj/structure/window/reinforced, /obj/structure/window/reinforced{ - dir = 1 + dir = 2 }, -/obj/structure/window/reinforced{ - dir = 4 +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/obj/item/aiModule/oxygen, -/obj/item/aiModule/oneCrewMember, -/obj/item/aiModule/purge, -/obj/item/aiModule/antimov, -/turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) "agX" = ( /obj/structure/cable{ @@ -1923,24 +1929,15 @@ }, /area/maintenance/banya) "anR" = ( -/obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/iv_bag/bloodsynthetic/oxygenis, -/obj/item/reagent_containers/iv_bag/bloodsynthetic/oxygenis, -/obj/item/reagent_containers/iv_bag/bloodsynthetic/nitrogenis, -/obj/item/reagent_containers/iv_bag/bloodsynthetic/nitrogenis, -/obj/item/tank/internals/emergency_oxygen/engi/empty, -/obj/item/tank/internals/emergency_oxygen/engi/empty, -/obj/structure/window/reinforced{ - dir = 1 +/obj/item/twohanded/required/kirbyplants, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 }, -/obj/structure/cable{ - icon_state = "0-8" +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 8 }, -/obj/machinery/power/apc{ - cell_type = 5000; - dir = 4; - name = "east bump"; - pixel_x = 26 +/obj/structure/cable{ + icon_state = "4-8" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -2288,7 +2285,9 @@ /turf/simulated/floor/shuttle/plating, /area/shuttle/arrival/station) "ari" = ( -/obj/machinery/door/airlock/external, +/obj/machinery/door/airlock/external{ + use_power = 0 + }, /turf/simulated/floor/plating, /area/maintenance/casino) "arn" = ( @@ -2958,6 +2957,9 @@ /obj/item/radio/intercom{ pixel_x = 28 }, +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 8 + }, /turf/simulated/floor/plasteel, /area/atmos) "axC" = ( @@ -3414,20 +3416,6 @@ }, /turf/simulated/floor/plating, /area/maintenance/chapel) -"aAP" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/cable{ - icon_state = "2-8" - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cryo) "aAV" = ( /obj/machinery/atmospherics/pipe/multiz{ dir = 4 @@ -3542,6 +3530,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -4334,17 +4325,18 @@ /obj/structure/cable{ icon_state = "2-4" }, -/obj/structure/cable{ - icon_state = "1-2" - }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 6 + }, +/obj/structure/cable{ + icon_state = "2-8" }, /turf/simulated/floor/plasteel{ - dir = 1 + dir = 1; + icon_state = "red" }, /area/security/reception) "aGQ" = ( @@ -6619,21 +6611,12 @@ /turf/simulated/floor/plating, /area/maintenance/asmaint3) "aWH" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/machinery/vending/cart{ - pixel_x = -1; - pixel_y = 1 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 1 - }, -/turf/simulated/floor/plasteel{ - dir = 9; - icon_state = "brown" +/obj/machinery/status_display/supply_display{ + pixel_y = 32 }, -/area/quartermaster/office) +/obj/effect/decal/cleanable/dirt, +/turf/simulated/floor/plasteel, +/area/quartermaster/storage) "aWJ" = ( /obj/machinery/door/firedoor, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -8051,6 +8034,20 @@ icon_state = "dark" }, /area/engineering/aienter) +"bil" = ( +/obj/structure/cable{ + icon_state = "1-4" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 5 + }, +/turf/simulated/floor/plasteel{ + icon_state = "whiteblue" + }, +/area/medical/cloning) "bio" = ( /obj/structure/bookcase, /turf/simulated/floor/wood, @@ -9391,7 +9388,6 @@ }, /area/security/securearmory) "brw" = ( -/obj/effect/spawner/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ dir = 8; id_tag = "SecMedPrivInside" @@ -9402,6 +9398,7 @@ /obj/structure/cable{ icon_state = "2-4" }, +/obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/security/medbay) "bry" = ( @@ -9978,19 +9975,14 @@ }, /area/hallway/primary/central) "bvn" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/segment, -/obj/structure/cable{ - icon_state = "1-4" +/obj/item/radio/intercom{ + pixel_y = 23 }, /turf/simulated/floor/plasteel{ - dir = 1 + dir = 1; + icon_state = "red" }, -/area/security/prison/cell_block/A) +/area/hallway/primary/central/second/west) "bvt" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -11936,22 +11928,13 @@ /turf/simulated/floor/plating, /area/maintenance/atmospherics) "bIO" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/structure/table, -/obj/machinery/recharger{ - pixel_x = 1; - pixel_y = 3 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 1 +/obj/structure/stairs{ + layer = 2 }, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "brown" + icon_state = "neutralfull" }, -/area/quartermaster/office) +/area/hallway/primary/starboard) "bIT" = ( /obj/structure/barricade/wooden{ layer = 3.5 @@ -12014,9 +11997,6 @@ /obj/effect/decal/warning_stripes/yellow/hollow, /obj/structure/closet/crate, /obj/effect/spawner/lootdrop/maintenance/double, -/obj/structure/sign/poster/official/random{ - pixel_x = -32 - }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "dark" @@ -13252,9 +13232,6 @@ /obj/structure/cable{ icon_state = "1-8" }, -/obj/structure/cable{ - icon_state = "1-4" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /turf/simulated/floor/plasteel{ dir = 1 @@ -14469,8 +14446,13 @@ }, /area/toxins/misc_lab) "cbk" = ( -/obj/structure/cable{ - icon_state = "2-8" +/obj/machinery/photocopier, +/obj/machinery/light{ + dir = 1; + in_use = 1 + }, +/obj/machinery/alarm{ + pixel_y = 25 }, /turf/simulated/floor/plasteel{ dir = 1 @@ -15134,17 +15116,6 @@ icon_state = "darkred" }, /area/security/permahallway) -"cgR" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cryo) "cgX" = ( /obj/structure/cable{ icon_state = "4-8" @@ -16944,6 +16915,7 @@ pixel_x = -3; pixel_y = 5 }, +/obj/item/megaphone, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "red" @@ -17816,14 +17788,9 @@ }, /area/maintenance/medroom) "cAA" = ( -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -26 - }, -/obj/structure/cable{ - icon_state = "0-4" - }, +/obj/structure/closet/secure_closet/security, +/obj/item/clothing/mask/balaclava, +/obj/effect/decal/warning_stripes/red/hollow, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -18155,17 +18122,13 @@ /turf/simulated/floor/plasteel, /area/maintenance/atmospherics) "cDR" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/machinery/computer/merch{ - dir = 1 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 1 +/obj/structure/table, +/obj/machinery/recharger{ + pixel_x = 1; + pixel_y = 3 }, /turf/simulated/floor/plasteel{ - dir = 5; + dir = 8; icon_state = "brown" }, /area/quartermaster/office) @@ -18511,25 +18474,11 @@ }, /area/chapel/office) "cGc" = ( -/obj/structure/cable{ - icon_state = "0-4" - }, -/obj/structure/cable{ - icon_state = "1-4" - }, -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/effect/spawner/window/reinforced, -/obj/structure/curtain/open/shower/security{ - alpha = 255; - icon_state = "closed"; - name = "backstage"; - opacity = 1; - anchored = 1 +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "redcorner" }, -/turf/simulated/floor/plating, -/area/security/medbay) +/area/hallway/primary/central/second/west) "cGm" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -18744,8 +18693,10 @@ /obj/structure/cable{ icon_state = "1-4" }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 4 + }, /turf/simulated/floor/plasteel, /area/atmos) "cHB" = ( @@ -19010,8 +18961,8 @@ }, /area/atmos) "cKm" = ( -/obj/structure/table, -/obj/item/aiModule/freeform, +/obj/item/ai_module/freeform, +/obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -19532,12 +19483,13 @@ /turf/space/openspace, /area/space) "cOi" = ( -/obj/item/radio/intercom{ - pixel_x = 28; - pixel_y = 28 +/obj/structure/cable{ + icon_state = "1-2" }, -/turf/simulated/floor/wood, -/area/library) +/turf/simulated/floor/plasteel{ + icon_state = "redcorner" + }, +/area/security/prison/cell_block/A) "cOl" = ( /obj/effect/turf_decal/siding/wood/corner{ dir = 4 @@ -21115,23 +21067,6 @@ icon_state = "vault" }, /area/turret_protected/aisat_interior/secondary) -"cZo" = ( -/obj/structure/cable{ - icon_state = "1-4" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 5 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 5 - }, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/simulated/floor/plasteel{ - icon_state = "whiteblue" - }, -/area/medical/cloning) "cZt" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 6 @@ -21744,8 +21679,7 @@ dir = 9 }, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "red" + dir = 1 }, /area/hallway/primary/central/second/west) "dgl" = ( @@ -21973,12 +21907,17 @@ }, /area/hallway/secondary/exit) "dhv" = ( -/obj/machinery/door/airlock{ - id_tag = "toilet3"; - name = "Toilet" +/obj/effect/turf_decal/number/number_2{ + dir = 1; + pixel_y = -18 }, -/turf/simulated/floor/plasteel/freezer, -/area/crew_quarters/toilet2) +/obj/effect/turf_decal/arrows/white{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/hallway/primary/central/west) "dhz" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -22508,13 +22447,16 @@ }, /area/turret_protected/ai) "dlT" = ( -/obj/effect/spawner/window/reinforced, /obj/structure/cable{ - icon_state = "0-8" + icon_state = "0-4" }, -/obj/machinery/door/poddoor/shutters/preopen{ - dir = 1; - id_tag = "SecMedPrivOutside" +/obj/effect/spawner/window/reinforced, +/obj/structure/curtain/open/shower/security{ + alpha = 255; + icon_state = "closed"; + name = "backstage"; + opacity = 1; + anchored = 1 }, /turf/simulated/floor/plating, /area/security/medbay) @@ -23385,7 +23327,8 @@ pixel_y = 32 }, /turf/simulated/floor/plasteel{ - dir = 1 + dir = 1; + icon_state = "red" }, /area/security/prison/cell_block/A) "dsh" = ( @@ -23519,6 +23462,12 @@ icon_state = "neutralfull" }, /area/quartermaster/delivery) +"dsN" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "red" + }, +/area/security/prison/cell_block/A) "dsO" = ( /obj/machinery/libraryscanner, /turf/simulated/floor/wood, @@ -24374,8 +24323,8 @@ }, /area/maintenance/detectives_office) "dzE" = ( -/obj/structure/table, -/obj/item/aiModule/quarantine, +/obj/item/ai_module/quarantine, +/obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -25443,7 +25392,7 @@ /obj/machinery/firealarm{ dir = 4; name = "east fire alarm"; - pixel_x = 24 + pixel_x = 26 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -25547,6 +25496,16 @@ }, /turf/simulated/floor/plating, /area/maintenance/gambling_den) +"dIS" = ( +/obj/machinery/computer/secure_data, +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "red" + }, +/area/security/reception) "dIY" = ( /obj/structure/disposalpipe/segment{ dir = 6 @@ -25929,6 +25888,16 @@ icon_state = "vault" }, /area/bridge) +"dLR" = ( +/obj/item/radio/intercom{ + dir = 1; + pixel_y = -28 + }, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "red" + }, +/area/hallway/primary/central/west) "dMb" = ( /obj/machinery/light/small{ dir = 4 @@ -26619,29 +26588,8 @@ /turf/simulated/floor/plating, /area/maintenance/brig) "dQT" = ( -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/structure/cable{ - icon_state = "2-8" - }, -/obj/machinery/door/airlock/medical{ - name = "Brig Medical Bay"; - req_access = list(63); - security_level = 1 - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" - }, -/area/security/medbay) +/turf/simulated/openspace, +/area/hallway/primary/central/second/west) "dQY" = ( /obj/structure/chair{ dir = 1 @@ -26691,17 +26639,14 @@ /turf/simulated/floor/plating, /area/engineering/engine) "dRn" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/cable{ + icon_state = "4-8" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -29456,9 +29401,6 @@ /area/security/permabrig) "elt" = ( /obj/effect/landmark/start/brig_physician, -/obj/structure/cable{ - icon_state = "4-8" - }, /obj/structure/cable{ icon_state = "2-8" }, @@ -29468,8 +29410,8 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/cable{ + icon_state = "2-4" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -29821,19 +29763,14 @@ }, /area/maintenance/trading) "enP" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 +/obj/structure/disposalpipe/segment{ + dir = 9 }, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + dir = 4; + icon_state = "red" }, -/area/security/medbay) +/area/security/processing) "enR" = ( /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 4 @@ -29847,6 +29784,7 @@ /obj/structure/cable{ icon_state = "2-8" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -30062,6 +30000,16 @@ /obj/effect/spawner/window/reinforced, /turf/simulated/floor/plating, /area/bridge) +"epN" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cryo) "epP" = ( /obj/structure/table, /obj/effect/decal/cleanable/dirt, @@ -30136,8 +30084,16 @@ /obj/machinery/light{ dir = 4 }, +/obj/machinery/power/apc{ + cell_type = 5000; + dir = 4; + name = "east bump"; + pixel_x = 26 + }, +/obj/structure/cable, /turf/simulated/floor/plasteel{ - icon_state = "redcorner" + dir = 4; + icon_state = "red" }, /area/security/prison/cell_block/A) "eqO" = ( @@ -34164,7 +34120,7 @@ /obj/structure/table/reinforced, /obj/effect/decal/warning_stripes/yellow, /obj/machinery/door/window/westleft{ - dir = 1; + dir = 2; name = "Cargo Bay Desk"; req_access = list(31) }, @@ -34962,7 +34918,7 @@ dir = 8 }, /obj/structure/disposalpipe/segment{ - dir = 6 + dir = 10 }, /turf/simulated/floor/plasteel{ dir = 1; @@ -35648,25 +35604,6 @@ /obj/effect/decal/warning_stripes/southwest, /turf/simulated/floor/plasteel, /area/storage/tech) -"fgL" = ( -/obj/machinery/camera{ - c_tag = "Central Bridge Hallway East 2"; - dir = 10 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/computer/security/telescreen/entertainment{ - pixel_y = -30 - }, -/turf/simulated/floor/engine{ - slowdown = -0.3 - }, -/area/hallway/primary/central) "fgN" = ( /obj/machinery/computer/crew, /obj/structure/cable{ @@ -35789,6 +35726,9 @@ "fhv" = ( /obj/machinery/door/firedoor, /obj/effect/decal/warning_stripes/yellow, +/obj/machinery/vending/wallmed{ + pixel_x = -25 + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutral" @@ -36193,11 +36133,13 @@ /area/security/podbay) "fky" = ( /obj/effect/turf_decal/siding/red{ - dir = 4 + dir = 5 }, -/obj/effect/landmark/start/brig_physician, -/obj/structure/cable{ - icon_state = "1-8" +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 9 }, /turf/simulated/floor/carpet/red, /area/security/medbay) @@ -36240,6 +36182,10 @@ icon_state = "darkred" }, /area/turret_protected/aisat_interior/secondary) +"fkL" = ( +/obj/machinery/vending/boozeomat, +/turf/simulated/floor/carpet/black, +/area/ntrep) "fkO" = ( /obj/structure/railing/corner{ dir = 4 @@ -36391,6 +36337,7 @@ pixel_x = -2; pixel_y = 4 }, +/obj/item/storage/belt/medical, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -36865,8 +36812,8 @@ "fps" = ( /obj/structure/window/reinforced, /obj/item/flag/nt, -/obj/structure/sign/tajarplaque{ - desc = "Важное Уточнение! Рабочие пожелали оставаться анонимными, поэтому, обойдёмся их прозвищами. Особая благодарность за помощь Главному Инженеру Новы в поисках и устранении неисправностей на станции НаноТрейзен. С благодарностью, Daeberdir. Слава НаноТрейзен!"; +/obj/structure/sign/atmosplaque{ + desc = "Важное Уточнение! Рабочие пожелали оставаться анонимными, поэтому, обойдёмся их прозвищами. За помощь Главному Инженеру Новы в поисках и устранении неисправностей на станции НаноТрейзен. С благодарностью, Aeterna0. Слава НаноТрейзен!"; name = "Благодарственное Письмо от Главного Инженера станции Нова"; pixel_y = 32 }, @@ -36894,17 +36841,13 @@ /turf/simulated/floor/shuttle, /area/shuttle/arrival/station) "fpy" = ( -/obj/structure/cable, -/obj/effect/spawner/window/reinforced, -/obj/structure/curtain/open/shower/security{ - alpha = 255; - icon_state = "closed"; - name = "backstage"; - opacity = 1; - anchored = 1 +/obj/structure/sign/poster/official/obey{ + pixel_x = 32 }, -/turf/simulated/floor/plating, -/area/security/medbay) +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/security/prison/cell_block/A) "fpz" = ( /obj/structure/table/wood, /obj/effect/turf_decal/siding/wood{ @@ -38066,6 +38009,17 @@ }, /turf/simulated/floor/plasteel/grimy, /area/crew_quarters/bar) +"fxY" = ( +/obj/effect/decal/warning_stripes/west, +/obj/machinery/atmospherics/unary/vent_scrubber{ + dir = 4; + name = "standard air scrubber"; + on = 1; + scrub_N2O = 1; + scrub_Toxins = 1 + }, +/turf/simulated/floor/plasteel, +/area/atmos) "fyh" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ @@ -38657,26 +38611,6 @@ icon_state = "dark" }, /area/crew_quarters/chief) -"fCz" = ( -/obj/effect/mapping_helpers/airlock/unres{ - dir = 1 - }, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/airlock/medical/glass{ - id = "cloninglab"; - name = "Cloning Lab"; - req_access = list(5) - }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cloning) "fCA" = ( /obj/effect/turf_decal/siding/wideplating/dark{ dir = 1 @@ -42096,24 +42030,6 @@ }, /turf/simulated/floor/plasteel, /area/quartermaster/miningdock) -"gbK" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/computer/security/telescreen/entertainment{ - pixel_y = -30 - }, -/turf/simulated/floor/engine{ - slowdown = -0.3 - }, -/area/hallway/primary/central) "gbN" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -42458,14 +42374,11 @@ /turf/simulated/floor/plating, /area/maintenance/starboard) "geA" = ( -/obj/structure/table/reinforced, -/obj/item/storage/toolbox/mechanical{ - pixel_x = -2; - pixel_y = -1 - }, -/obj/item/storage/belt/utility, -/obj/effect/decal/warning_stripes/yellow/hollow, /obj/effect/decal/warning_stripes/northeast, +/obj/machinery/computer/roboquest, +/obj/effect/turf_decal/bot_red{ + layer = 1.99 + }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -44738,6 +44651,12 @@ /obj/effect/decal/warning_stripes/red, /turf/simulated/floor/plasteel, /area/hallway/secondary/entry/additional) +"guO" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on, +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/security/prison/cell_block/A) "guT" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -45137,13 +45056,9 @@ /area/maintenance/fore) "gxY" = ( /obj/structure/window/reinforced, -/obj/structure/sign/goldenplaque{ - pixel_y = 32; - desc = "Важное Уточнение! Рабочие пожелали оставаться анонимными, поэтому, обойдёмся их прозвищами. За помощь Главному Инженеру Новы в поисках и устранении неисправностей на станции НаноТрейзен. С благодарностью, Zhojaba. Слава НаноТрейзен!"; - name = "Благодарственное Письмо от Главного Инженера станции Нова" - }, -/obj/structure/statue/gold/hos{ - pixel_y = 7 +/obj/structure/statue/gold/tajaran{ + desc = "Перед собою вы наблюдаете статую таяры, которая держит в руках чертежи станции очень похожие на станцию Нова. Надпись на табличке - Один из двух главных инженеров союза Таяр и Унати, по прозвищу Daeberdir, принимавших участие в разработке передовой научно-исследовательской станции НаноТрейзен - Nova."; + name = "Л.А.Р" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -45651,6 +45566,7 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "redcorner" @@ -48331,6 +48247,15 @@ icon_state = "dark" }, /area/maintenance/trading) +"gYr" = ( +/obj/effect/turf_decal/number/number_1{ + dir = 1 + }, +/obj/effect/turf_decal/arrows/white, +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/hallway/primary/central/second/west) "gYu" = ( /obj/structure/cable/yellow{ d1 = 1; @@ -49064,6 +48989,21 @@ icon_state = "red" }, /area/hallway/primary/central/second/west) +"hdq" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/simulated/floor/engine{ + slowdown = -0.3 + }, +/area/hallway/primary/central) "hdt" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/girder, @@ -50655,6 +50595,16 @@ icon_state = "neutralcorner" }, /area/crew_quarters/fitness) +"hpC" = ( +/obj/structure/sign/directions/floor/alt{ + dir = 6; + pixel_x = -32 + }, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "red" + }, +/area/hallway/primary/central/second/west) "hpH" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small{ @@ -51520,6 +51470,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "red" @@ -52517,6 +52470,18 @@ icon_state = "whitehall" }, /area/maintenance/fsmaint3) +"hFZ" = ( +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, +/obj/structure/sign/directions/science{ + pixel_y = 32; + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "neutral" + }, +/area/hallway/primary/central/sw) "hGi" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -53846,10 +53811,8 @@ /turf/simulated/floor/plating/airless, /area/turret_protected/ai) "hSF" = ( -/obj/structure/disposalpipe/junction{ - dir = 1; - icon_state = "pipe-j2"; - tag = "icon-pipe-j1 (WEST)" +/obj/structure/disposalpipe/junction/reversed{ + dir = 1 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -54059,6 +54022,9 @@ c_tag = "East Primary Hallway 1"; dir = 1 }, +/obj/item/radio/intercom{ + pixel_y = -32 + }, /turf/simulated/floor/plasteel{ icon_state = "neutralcorner" }, @@ -54284,23 +54250,14 @@ /turf/simulated/floor/plating, /area/maintenance/apmaint) "hVx" = ( -/obj/structure/toilet{ - pixel_y = 19 - }, -/obj/machinery/light/small{ +/obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, -/obj/machinery/door_control{ - desiredstate = 1; - id = "toilet3"; - name = "Toilet Bolt Control"; - normaldoorcontrol = 1; - specialfunctions = 4; - pixel_x = 25 +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "neutral" }, -/obj/effect/decal/warning_stripes/yellow/hollow, -/turf/simulated/floor/plasteel/freezer, -/area/crew_quarters/toilet2) +/area/hallway/primary/central/west) "hVS" = ( /obj/machinery/vending/crittercare, /obj/item/radio/intercom{ @@ -55436,6 +55393,9 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/structure/cable{ + icon_state = "2-4" + }, /turf/simulated/floor/plasteel{ dir = 1 }, @@ -55745,6 +55705,17 @@ icon_state = "yellow" }, /area/engineering/break_room) +"igl" = ( +/obj/machinery/access_button{ + command = "cycle_exterior"; + frequency = 1379; + master_tag = "port4"; + name = "exterior access button"; + pixel_x = -28; + pixel_y = -28 + }, +/turf/space, +/area/space) "ign" = ( /obj/item/twohanded/required/kirbyplants, /turf/simulated/floor/plasteel{ @@ -57113,19 +57084,9 @@ }, /area/medical/research/restroom) "ipA" = ( -/obj/structure/cable{ - icon_state = "0-2" - }, -/obj/effect/spawner/window/reinforced, -/obj/structure/curtain/open/shower/security{ - alpha = 255; - icon_state = "closed"; - name = "backstage"; - opacity = 1; - anchored = 1 - }, -/turf/simulated/floor/plating, -/area/security/medbay) +/obj/effect/turf_decal/siding/wood, +/turf/simulated/floor/carpet, +/area/library) "ipK" = ( /obj/effect/decal/warning_stripes/yellow/hollow, /obj/effect/spawner/lootdrop/maintenance, @@ -57134,6 +57095,9 @@ c_tag = "Cargo Delivery Warehouse West"; dir = 5 }, +/obj/structure/sign/poster/official/random{ + pixel_x = -32 + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "dark" @@ -57681,11 +57645,14 @@ /turf/simulated/floor/wood, /area/library/game_zone) "itL" = ( -/obj/structure/table/wood, -/obj/item/folder, -/obj/item/pen, -/turf/simulated/floor/wood, -/area/library) +/obj/machinery/alarm{ + dir = 1; + pixel_y = -22 + }, +/turf/simulated/floor/plasteel{ + icon_state = "red" + }, +/area/hallway/primary/central/west) "itM" = ( /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 @@ -58427,6 +58394,26 @@ icon_state = "whitegreen" }, /area/medical/virology/lab) +"iza" = ( +/obj/structure/cable{ + icon_state = "2-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 10; + initialize_directions = 10 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 + }, +/obj/structure/cable{ + d1 = 4; + d2 = 8; + icon_state = "4-8" + }, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cloning) "izb" = ( /obj/machinery/power/solar{ name = "South-East Solar Panel" @@ -58694,10 +58681,14 @@ }, /area/crew_quarters/kitchen) "iAJ" = ( -/obj/effect/decal/warning_stripes/southeast, /obj/structure/extinguisher_cabinet{ pixel_x = 26 }, +/obj/machinery/roboquest_pad, +/obj/effect/turf_decal/bot_red{ + layer = 1.99 + }, +/obj/effect/decal/warning_stripes/southeast, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" }, @@ -59227,16 +59218,13 @@ /turf/simulated/floor/plating/airless, /area/toxins/test_area) "iGe" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, /obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, +/obj/structure/cable{ + icon_state = "4-8" + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -62341,13 +62329,6 @@ /obj/structure/closet, /turf/simulated/floor/plating, /area/maintenance/starboard) -"jfC" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/turf/simulated/floor/carpet/black, -/area/ntrep) "jfD" = ( /obj/structure/toilet/secret{ dir = 1 @@ -63080,12 +63061,10 @@ /turf/simulated/floor/plating, /area/maintenance/apmaint) "jkm" = ( -/obj/item/radio/intercom{ - pixel_x = 28; - pixel_y = -2 - }, +/obj/structure/table, /turf/simulated/floor/plasteel{ - icon_state = "redcorner" + dir = 6; + icon_state = "red" }, /area/security/prison/cell_block/A) "jkt" = ( @@ -63208,13 +63187,14 @@ }, /area/maintenance/livingcomplex) "jlI" = ( -/obj/structure/sign/redcross{ - pixel_x = 32 +/obj/effect/turf_decal/number/number_1{ + dir = 1 }, +/obj/effect/turf_decal/arrows/white, /turf/simulated/floor/plasteel{ icon_state = "redcorner" }, -/area/security/prison/cell_block/A) +/area/hallway/primary/central/second/west) "jlM" = ( /obj/machinery/optable, /obj/effect/decal/cleanable/dirt, @@ -66539,19 +66519,26 @@ /turf/simulated/floor/plating, /area/maintenance/fsmaint3) "jKN" = ( -/obj/effect/turf_decal/siding/red{ - dir = 5 +/obj/machinery/door/airlock/medical{ + name = "Brig Physician's Quarters"; + req_access = list(5); + security_level = 1 }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable{ - icon_state = "1-2" + d1 = 1; + d2 = 4; + icon_state = "1-4" }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 5 +/obj/structure/cable{ + icon_state = "1-8" }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 9 +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "dark" }, -/turf/simulated/floor/carpet/red, /area/security/medbay) "jKU" = ( /obj/structure/chair/sofa/right{ @@ -66759,32 +66746,6 @@ icon_state = "neutralfull" }, /area/civilian/barber) -"jLR" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/structure/cable{ - icon_state = "2-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/effect/turf_decal{ - dir = 8; - icon_state = "golden_stripes" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 8 - }, -/obj/structure/disposalpipe/sortjunction/reversed{ - dir = 1; - name = "Captain Office"; - sortType = 18 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/bridge) "jLT" = ( /obj/effect/decal/warning_stripes/east, /turf/simulated/floor/plasteel{ @@ -67105,6 +67066,31 @@ icon_state = "neutralfull" }, /area/hallway/primary/central/nw) +"jOe" = ( +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/machinery/button/windowtint{ + pixel_y = -24; + pixel_x = 24; + id = "ntrepprivate"; + name = "Door tint control" + }, +/obj/machinery/door_control{ + id = "NTRprivate"; + name = "Privacy Shutters Control"; + pixel_x = 26; + pixel_y = -33; + req_access = list(73) + }, +/turf/simulated/floor/carpet/black, +/area/ntrep) "jOs" = ( /obj/structure/cable{ icon_state = "0-8" @@ -67292,10 +67278,6 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/simulated/floor/plating, /area/maintenance/apmaint) -"jQq" = ( -/obj/effect/decal/cleanable/spiderling_remains, -/turf/simulated/floor/plating, -/area/maintenance/secpost) "jQt" = ( /obj/machinery/light{ dir = 1; @@ -67552,6 +67534,24 @@ icon_state = "darkblue" }, /area/medical/surgery/south) +"jRZ" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/machinery/power/apc{ + cell_type = 5000; + dir = 4; + name = "east bump"; + pixel_x = 26 + }, +/obj/structure/cable{ + icon_state = "0-8" + }, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "darkblue" + }, +/area/security/medbay) "jSd" = ( /obj/structure/cable{ icon_state = "2-4" @@ -68230,6 +68230,13 @@ /obj/item/camera, /turf/simulated/floor/plating, /area/maintenance/livingcomplex) +"jXF" = ( +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, +/turf/simulated/floor/plasteel{ + icon_state = "red" + }, +/area/hallway/primary/central/west) "jXN" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small{ @@ -68822,8 +68829,8 @@ "kdp" = ( /obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "red" + dir = 1; + icon_state = "redcorner" }, /area/hallway/primary/central/second/west) "kds" = ( @@ -69056,7 +69063,7 @@ dir = 4 }, /turf/simulated/floor/plasteel{ - dir = 4; + dir = 5; icon_state = "red" }, /area/security/reception) @@ -69206,7 +69213,7 @@ }, /turf/simulated/floor/plasteel{ dir = 4; - icon_state = "redcorner" + icon_state = "red" }, /area/security/prison/cell_block/A) "kgI" = ( @@ -69472,7 +69479,6 @@ /area/toxins/storage) "kiE" = ( /obj/structure/table, -/obj/item/aiModule/reset, /obj/item/radio/intercom{ name = "east station intercom (General)"; pixel_x = 28 @@ -71376,19 +71382,17 @@ /turf/simulated/floor/plasteel, /area/hallway/secondary/entry/additional) "kxQ" = ( -/obj/machinery/photocopier, -/obj/machinery/alarm{ - pixel_y = 25 +/obj/structure/window/reinforced{ + dir = 1 }, -/obj/machinery/light{ - dir = 1; - in_use = 1 +/obj/structure/cable{ + icon_state = "4-8" }, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "red" + dir = 9; + icon_state = "darkblue" }, -/area/security/reception) +/area/security/medbay) "kxR" = ( /obj/structure/table/wood, /obj/machinery/computer/library/public, @@ -71535,11 +71539,16 @@ dir = 5; network = list("SS13","Security") }, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 +/obj/machinery/power/apc{ + dir = 8; + name = "west bump"; + pixel_x = -24 + }, +/obj/structure/cable{ + icon_state = "0-4" }, /turf/simulated/floor/plasteel{ - dir = 8; + dir = 9; icon_state = "red" }, /area/security/reception) @@ -71599,12 +71608,19 @@ }, /area/crew_quarters/captain) "kAc" = ( -/obj/machinery/door/window/brigdoor/security{ - name = "Brig Medical Bay"; +/obj/machinery/light_switch{ + pixel_x = 7; + pixel_y = 29 + }, +/obj/machinery/door_control{ + id = "SecMedPrivInside"; + name = "Brig Medbay Privacy Shutters Control"; + pixel_x = -5; + pixel_y = 28; req_access = list(63) }, -/obj/structure/cable{ - icon_state = "4-8" +/obj/structure/window/reinforced{ + dir = 4 }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -71612,8 +71628,15 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/closet/crate/freezer, +/obj/item/tank/internals/emergency_oxygen/engi/empty, +/obj/item/tank/internals/emergency_oxygen/engi/empty, +/obj/item/reagent_containers/iv_bag/bloodsynthetic/nitrogenis, +/obj/item/reagent_containers/iv_bag/bloodsynthetic/nitrogenis, +/obj/item/reagent_containers/iv_bag/bloodsynthetic/oxygenis, +/obj/item/reagent_containers/iv_bag/bloodsynthetic/oxygenis, +/obj/structure/cable{ + icon_state = "4-8" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -73332,6 +73355,16 @@ /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plating, /area/maintenance/tourist) +"kOr" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cloning) "kOs" = ( /obj/structure/chair/sofa/left, /obj/random/therapy, @@ -74271,12 +74304,9 @@ /turf/space/openspace, /area/space) "kVT" = ( -/obj/machinery/vending/wallmed{ - pixel_x = -25 - }, /turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "neutral" + dir = 1; + icon_state = "neutralcorner" }, /area/hallway/primary/central/west) "kVV" = ( @@ -74503,7 +74533,9 @@ icon_state = "1-8" }, /obj/structure/cable{ - icon_state = "1-2" + d1 = 1; + d2 = 4; + icon_state = "1-4" }, /turf/simulated/floor/plasteel{ dir = 1; @@ -75425,23 +75457,13 @@ /turf/simulated/wall, /area/toxins/xenobiology) "ldS" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/structure/cable{ - icon_state = "4-8" +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 1 }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/turf/simulated/floor/plasteel{ + dir = 1 }, -/turf/simulated/floor/wood, -/area/hallway/primary/central/west) +/area/security/prison/cell_block/A) "ldZ" = ( /obj/machinery/power/apc{ name = "south bump"; @@ -75929,6 +75951,9 @@ }, /area/solar/starboard) "lhJ" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -76685,6 +76710,10 @@ }, /turf/simulated/floor/plating, /area/maintenance/fsmaint) +"loQ" = ( +/obj/machinery/disposal, +/turf/simulated/floor/carpet/black, +/area/ntrep) "loR" = ( /obj/effect/decal/warning_stripes/yellow, /obj/machinery/portable_atmospherics/canister/oxygen, @@ -77706,6 +77735,17 @@ icon_state = "red" }, /area/security/main) +"lxy" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "dark" + }, +/area/medical/genetics) "lxA" = ( /obj/effect/decal/warning_stripes/east, /obj/machinery/atmospherics/unary/vent_scrubber{ @@ -77902,6 +77942,20 @@ dir = 1 }, /area/security/brigstaff) +"lzn" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cloning) "lzo" = ( /obj/item/flag/nt, /obj/machinery/light{ @@ -79375,17 +79429,19 @@ icon_state = "darkgrey" }, /area/engineering/mechanic_workshop/hangar) -"lLc" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/decal/cleanable/vomit, -/turf/simulated/floor/plating, -/area/maintenance/secpost) "lLh" = ( /obj/effect/decal/cleanable/dust, /obj/effect/turf_decal/siding/wood, /obj/machinery/seed_extractor, /turf/simulated/floor/wood, /area/maintenance/garden) +"lLl" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "dark" + }, +/area/security/medbay) "lLp" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/warning_stripes/southwest, @@ -82724,7 +82780,7 @@ /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 + dir = 8 }, /turf/simulated/floor/plasteel{ icon_state = "neutralfull" @@ -83113,6 +83169,9 @@ }, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, /turf/simulated/floor/plasteel{ dir = 1 }, @@ -83556,6 +83615,33 @@ }, /turf/space/openspace, /area/space) +"mpH" = ( +/obj/structure/disposalpipe/sortjunction{ + dir = 1; + name = "Captain's Office"; + sortType = 18; + icon_state = "pipe-j2s" + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/obj/structure/cable{ + icon_state = "2-8" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/effect/turf_decal{ + dir = 8; + icon_state = "golden_stripes" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/bridge) "mpL" = ( /obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ @@ -86577,6 +86663,19 @@ icon_state = "neutralfull" }, /area/hallway/primary/central/se) +"mND" = ( +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/cable{ + icon_state = "2-8" + }, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cryo) "mNG" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -87134,17 +87233,6 @@ icon_state = "neutralcorner" }, /area/maintenance/apmaint) -"mRP" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cloning) "mSa" = ( /obj/machinery/light, /turf/simulated/floor/plasteel{ @@ -87945,10 +88033,10 @@ "mXr" = ( /obj/structure/window/reinforced, /obj/item/flag/nt, -/obj/structure/sign/atmosplaque{ +/obj/structure/sign/goldenplaque{ pixel_y = 32; - desc = "Важное Уточнение! Главный Инженер пожелал оставаться анонимным, поэтому, обойдёмся прозвищем. За выдающиеся успехи в области инженерного менеджмента, а также успешное проектирование станции Нова по всем стандартам НаноТрейзен. Благодарим вас за труд, PiroMage. Слава НаноТрейзен!"; - name = "Благодарственное Письмо Для Главного Инженера Команды Архитекторов Станции Нова" + desc = "Важное Уточнение! Рабочие пожелали оставаться анонимными, поэтому, обойдёмся их прозвищами. За помощь Главному Инженеру Новы в поисках и устранении неисправностей на станции НаноТрейзен. С благодарностью, Zhojaba. Слава НаноТрейзен!"; + name = "Благодарственное Письмо от Главного Инженера станции Нова" }, /turf/simulated/floor/plasteel{ icon_state = "dark" @@ -88035,10 +88123,10 @@ req_access = list(1); color = "red" }, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, -/obj/item/clothing/gloves/combat, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, +/obj/item/clothing/gloves/combat/riot, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -89570,9 +89658,16 @@ }, /area/crew_quarters/chief) "nio" = ( +/obj/effect/turf_decal/number/number_1{ + dir = 1; + pixel_y = -15 + }, +/obj/effect/turf_decal/arrows/white{ + dir = 1 + }, /turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "browncorner" + dir = 1; + icon_state = "brown" }, /area/quartermaster/office) "nix" = ( @@ -90678,6 +90773,23 @@ }, /turf/simulated/floor/wood/dark, /area/crew_quarters/bar/atrium) +"nrr" = ( +/obj/machinery/door/firedoor, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/door/airlock/medical/glass{ + id = "cloninglab"; + name = "Genetics Lab"; + req_access = list(9) + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "dark" + }, +/area/medical/genetics) "nrs" = ( /obj/structure/lattice, /obj/structure/window/reinforced, @@ -91183,6 +91295,7 @@ /obj/structure/cable{ icon_state = "0-4" }, +/obj/item/twohanded/required/kirbyplants, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "purple" @@ -91291,10 +91404,6 @@ icon_state = "dark" }, /area/atmos) -"nwA" = ( -/obj/effect/spawner/random_spawners/blood_5, -/turf/simulated/floor/plating, -/area/maintenance/secpost) "nwG" = ( /obj/structure/cable{ d1 = 4; @@ -92020,14 +92129,15 @@ }, /area/toxins/xenobiology) "nBo" = ( -/obj/structure/sign/redcross{ - pixel_x = -32 +/obj/effect/turf_decal/number/number_1{ + dir = 1 }, +/obj/effect/turf_decal/arrows/white, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "redcorner" + dir = 8; + icon_state = "red" }, -/area/security/prison/cell_block/A) +/area/hallway/primary/central/second/west) "nBq" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/blood/drip{ @@ -92097,6 +92207,26 @@ }, /turf/simulated/floor/plating, /area/maintenance/tourist) +"nBP" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 8 + }, +/obj/structure/cable{ + icon_state = "2-4" + }, +/obj/structure/cable{ + icon_state = "1-4" + }, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cryo) "nBS" = ( /obj/item/twohanded/required/kirbyplants, /obj/machinery/light/small{ @@ -93676,27 +93806,27 @@ /turf/simulated/wall/r_wall, /area/security/brigstaff) "nNK" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" }, /area/security/medbay) "nNM" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/structure/cable{ - icon_state = "1-4" + icon_state = "1-2" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + dir = 1 }, -/area/security/medbay) +/area/security/processing) "nNT" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ @@ -94630,12 +94760,14 @@ /turf/simulated/wall, /area/maintenance/fsmaint) "nVF" = ( -/obj/effect/decal/warning_stripes/west, -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 4 +/obj/structure/sign/poster/official/safety_report{ + pixel_x = -32 }, -/turf/simulated/floor/plasteel, -/area/atmos) +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "red" + }, +/area/hallway/primary/central/second/west) "nVI" = ( /obj/machinery/atmospherics/pipe/simple/insulated{ dir = 5 @@ -95744,7 +95876,11 @@ pixel_x = 28 }, /obj/structure/table/reinforced, -/obj/item/megaphone, +/obj/item/paper_bin{ + pixel_x = -3; + pixel_y = 7 + }, +/obj/item/pen, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "red" @@ -96534,19 +96670,15 @@ /turf/simulated/floor/wood, /area/maintenance/banya) "olq" = ( -/obj/effect/decal/warning_stripes/yellow/hollow, -/obj/structure/closet/crate, -/obj/effect/spawner/lootdrop/maintenance/double, -/obj/machinery/light{ - dir = 1; - in_use = 1 +/obj/structure/sign/directions/floor/alt{ + dir = 8; + pixel_y = 32 }, -/obj/effect/decal/cleanable/dirt, /turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "dark" + dir = 4; + icon_state = "neutralcorner" }, -/area/quartermaster/storage) +/area/hallway/primary/starboard) "ols" = ( /obj/machinery/gateway{ dir = 9 @@ -96661,9 +96793,14 @@ /obj/machinery/light{ dir = 4 }, +/obj/machinery/firealarm{ + dir = 4; + name = "east fire alarm"; + pixel_x = 24 + }, /turf/simulated/floor/plasteel{ dir = 4; - icon_state = "redcorner" + icon_state = "red" }, /area/security/prison/cell_block/A) "omm" = ( @@ -96793,11 +96930,17 @@ }, /area/hallway/primary/central/second/west) "onF" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 4 +/obj/machinery/light{ + dir = 8 + }, +/obj/structure/sign/directions/floor/alt{ + dir = 8; + pixel_x = -32 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "red" }, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/wood, /area/hallway/primary/central/west) "onK" = ( /obj/structure/cable{ @@ -97079,7 +97222,6 @@ /turf/simulated/floor/plating, /area/maintenance/asmaint3) "oqt" = ( -/obj/effect/landmark/start/barber, /turf/simulated/floor/wood/oak, /area/civilian/barber) "oqu" = ( @@ -97780,6 +97922,13 @@ icon_state = "dark" }, /area/security/securearmory) +"ovj" = ( +/obj/structure/table, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "red" + }, +/area/security/prison/cell_block/A) "ovn" = ( /obj/structure/table/reinforced, /obj/item/clipboard, @@ -97925,9 +98074,15 @@ }, /area/medical/chemistry) "ovR" = ( -/obj/structure/table, -/obj/item/aiModule/protectStation, -/obj/item/aiModule/nanotrasen, +/obj/item/ai_module/protect_station{ + pixel_x = -2; + pixel_y = 2 + }, +/obj/item/ai_module/nanotrasen{ + pixel_x = 2; + pixel_y = -2 + }, +/obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -100534,20 +100689,13 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/structure/cable{ - icon_state = "2-8" +/obj/structure/disposalpipe/segment{ + dir = 4 }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ dir = 1 }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 1 - }, -/obj/structure/disposalpipe/sortjunction/reversed{ - dir = 8; - name = "Brig Physician"; - sortType = 24 - }, /turf/simulated/floor/plasteel{ dir = 1 }, @@ -100629,9 +100777,10 @@ /obj/machinery/door/firedoor/border_only{ dir = 1 }, -/obj/item/paper_bin, -/obj/item/pen, -/obj/structure/table, +/obj/machinery/vending/cart{ + pixel_x = -1; + pixel_y = 1 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "brown" @@ -100744,6 +100893,9 @@ /obj/machinery/light/small{ dir = 1 }, +/obj/structure/sign/poster/official/random{ + pixel_y = 32 + }, /turf/simulated/floor/carpet, /area/library) "oSB" = ( @@ -102526,7 +102678,9 @@ }, /area/quartermaster/office) "pfw" = ( -/obj/machinery/computer/secure_data, +/obj/machinery/computer/security{ + network = list("SS13","Research Outpost","Mining Outpost") + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -102584,14 +102738,13 @@ /turf/simulated/floor/plating, /area/turret_protected/ai) "pfM" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, /turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "darkblue" + dir = 1; + icon_state = "red" }, -/area/security/medbay) +/area/hallway/primary/central/second/west) "pfN" = ( /obj/effect/decal/warning_stripes/southeastcorner, /obj/structure/disposalpipe/segment, @@ -103523,6 +103676,9 @@ /obj/structure/table/wood, /obj/item/flashlight/lamp/green, /obj/item/card/id/captains_spare, +/obj/structure/sign/poster/official/nanotrasen_logo{ + pixel_y = -32 + }, /turf/simulated/floor/carpet/royalblue, /area/crew_quarters/captain/bedroom) "plI" = ( @@ -104248,6 +104404,13 @@ /obj/effect/decal/warning_stripes/south, /turf/simulated/floor/plasteel, /area/engineering/controlroom) +"prN" = ( +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/hallway/primary/central/second/west) "prY" = ( /obj/effect/landmark/tiles/damageturf, /turf/simulated/floor/plating, @@ -104283,8 +104446,8 @@ /turf/simulated/floor/grass, /area/hallway/secondary/exit) "psl" = ( -/obj/structure/table, -/obj/item/aiModule/reset, +/obj/item/ai_module/reset, +/obj/structure/table/glass, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -104480,6 +104643,9 @@ "ptU" = ( /obj/item/storage/fancy/donut_box, /obj/structure/table/wood/fancy/purple, +/obj/item/radio/intercom{ + pixel_y = -28 + }, /turf/simulated/floor/carpet/purple, /area/crew_quarters/captain) "ptW" = ( @@ -105069,7 +105235,7 @@ icon_state = "1-2" }, /obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 + dir = 8 }, /turf/simulated/floor/plasteel, /area/atmos) @@ -105178,20 +105344,17 @@ /turf/simulated/floor/plating, /area/maintenance/starboard) "pyX" = ( -/obj/structure/cable{ - icon_state = "1-8" +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 10 }, /obj/structure/cable{ icon_state = "2-8" }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 9 +/obj/structure/sign/redcross{ + pixel_y = 32 }, /turf/simulated/floor/plasteel{ dir = 1; @@ -106407,6 +106570,7 @@ pixel_y = -24 }, /obj/machinery/light, +/obj/machinery/computer/secure_data, /turf/simulated/floor/plasteel{ icon_state = "darkred" }, @@ -107363,35 +107527,12 @@ /turf/simulated/floor/plating, /area/maintenance/asmaint4) "pOZ" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/obj/structure/table/glass, -/obj/effect/decal/warning_stripes/blue/hollow, -/obj/machinery/door_control{ - id = "SecMedPrivOutside"; - name = "Brig Medbay Privacy Shutters Control"; - pixel_x = 8; - pixel_y = 28; - req_access = list(63) - }, -/obj/machinery/light_switch{ - pixel_x = null; - pixel_y = 22 - }, -/obj/machinery/door_control{ - id = "SecMedPrivInside"; - name = "Brig Medbay Privacy Shutters Control"; - pixel_x = -8; - pixel_y = 28; - req_access = list(63) - }, -/obj/item/storage/belt/medical, +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + dir = 1 }, -/area/security/medbay) +/area/hallway/primary/central/west) "pPo" = ( /obj/effect/landmark/event/revenantspawn, /obj/machinery/computer/security/telescreen/entertainment{ @@ -107729,6 +107870,19 @@ /obj/structure/disposalpipe/trunk, /turf/simulated/floor/plating, /area/quartermaster/sorting) +"pRD" = ( +/obj/effect/turf_decal/number/number_2{ + dir = 1; + pixel_y = -18 + }, +/obj/effect/turf_decal/arrows/white{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "redcorner" + }, +/area/hallway/primary/central/west) "pRH" = ( /obj/structure/table/glass, /obj/item/storage/fancy/donut_box, @@ -109096,6 +109250,7 @@ /obj/structure/cable{ icon_state = "1-2" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "redcorner" @@ -110918,6 +111073,18 @@ dir = 1 }, /area/security/prison/cell_block/A) +"qpZ" = ( +/obj/structure/sign/security{ + pixel_x = -32 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "red" + }, +/area/hallway/primary/central/second/west) "qqg" = ( /obj/machinery/door/airlock/hatch{ name = "Telecommunications Access"; @@ -111633,6 +111800,9 @@ name = "Quarantine Lockdown"; opacity = 0 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "whiteblue"; @@ -111795,15 +111965,12 @@ /turf/simulated/floor/plating, /area/medical/virology/lab) "qwg" = ( -/obj/machinery/firealarm{ - dir = 4; - name = "east fire alarm"; - pixel_x = 24 - }, -/turf/simulated/floor/plasteel{ - icon_state = "redcorner" +/obj/effect/turf_decal/siding/red{ + dir = 4 }, -/area/security/prison/cell_block/A) +/obj/effect/landmark/start/brig_physician, +/turf/simulated/floor/carpet/red, +/area/security/medbay) "qwi" = ( /obj/structure/cable{ icon_state = "1-2" @@ -112019,6 +112186,19 @@ /obj/effect/decal/warning_stripes/northeast, /turf/simulated/floor/plasteel, /area/hallway/secondary/entry/additional) +"qxP" = ( +/obj/effect/turf_decal/number/number_1{ + dir = 1; + pixel_y = -15 + }, +/obj/effect/turf_decal/arrows/white{ + dir = 1 + }, +/turf/simulated/floor/plasteel{ + dir = 9; + icon_state = "brown" + }, +/area/quartermaster/office) "qxX" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/cable{ @@ -113846,7 +114026,8 @@ /area/hallway/secondary/entry/commercial) "qMk" = ( /obj/machinery/door/airlock/external{ - name = "Escape Pod Airlock" + name = "Escape Pod Airlock"; + use_power = 0 }, /turf/simulated/floor/plating, /area/maintenance/casino) @@ -114988,9 +115169,12 @@ }, /area/quartermaster/office) "qTF" = ( -/obj/structure/sign/nosmoking_2, -/turf/simulated/wall, -/area/library) +/obj/item/radio/intercom{ + dir = 1; + pixel_y = -28 + }, +/turf/simulated/floor/plasteel/freezer, +/area/crew_quarters/toilet2) "qTM" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 @@ -115458,10 +115642,10 @@ /turf/simulated/floor/plasteel, /area/quartermaster/miningdock) "qYe" = ( -/obj/structure/chair/comfy/brown, -/obj/effect/landmark/start/civilian, -/turf/simulated/floor/wood, -/area/library) +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/hallway/primary/central/west) "qYf" = ( /obj/structure/cable{ icon_state = "1-2" @@ -115951,13 +116135,6 @@ icon_state = "neutral" }, /area/crew_quarters/serviceyard) -"rcy" = ( -/obj/machinery/vending/boozeomat, -/obj/effect/turf_decal/siding/wood{ - dir = 4 - }, -/turf/simulated/floor/wood/dark, -/area/ntrep) "rcB" = ( /obj/structure/table/reinforced, /obj/item/radio, @@ -116692,14 +116869,12 @@ icon_state = "1-2" }, /obj/structure/disposalpipe/segment, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 - }, /obj/structure/cable{ icon_state = "1-8" }, /obj/item/radio/beacon, /obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel, /area/atmos) "riE" = ( @@ -118368,16 +118543,11 @@ }, /area/hallway/primary/central/ne) "rvo" = ( -/obj/effect/spawner/window/reinforced, -/obj/structure/cable{ - icon_state = "0-4" - }, -/obj/machinery/door/poddoor/shutters/preopen{ - dir = 1; - id_tag = "SecMedPrivOutside" +/turf/simulated/floor/plasteel{ + dir = 4; + icon_state = "red" }, -/turf/simulated/floor/plating, -/area/security/medbay) +/area/security/prison/cell_block/A) "rvp" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable{ @@ -119023,6 +119193,19 @@ }, /turf/simulated/floor/carpet, /area/crew_quarters/captain/bedroom) +"rzi" = ( +/obj/machinery/computer/rdconsole/core, +/obj/effect/decal/warning_stripes/northwest, +/obj/machinery/requests_console{ + department = "Science"; + departmentType = 2; + name = "Research Request Console"; + pixel_x = -30 + }, +/turf/simulated/floor/plasteel{ + icon_state = "dark" + }, +/area/toxins/lab) "rzl" = ( /obj/structure/girder, /turf/simulated/floor/plating, @@ -119254,34 +119437,6 @@ /obj/effect/decal/cleanable/dust, /turf/simulated/floor/wood, /area/maintenance/backstage) -"rAR" = ( -/obj/structure/cable{ - icon_state = "4-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 4 - }, -/obj/machinery/button/windowtint{ - pixel_y = -24; - pixel_x = 24; - id = "ntrepprivate"; - name = "Door tint control" - }, -/obj/machinery/door_control{ - id = "NTRprivate"; - name = "Privacy Shutters Control"; - pixel_x = 26; - pixel_y = -33; - req_access = list(73) - }, -/obj/structure/disposalpipe/trunk/multiz/down{ - dir = 2 - }, -/turf/simulated/floor/carpet/black, -/area/ntrep) "rBa" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/table/wood/fancy/red, @@ -120290,17 +120445,19 @@ }, /area/crew_quarters/chief) "rIa" = ( -/obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 +/obj/machinery/light{ + dir = 4 }, -/obj/item/pen, -/turf/simulated/floor/plasteel{ - dir = 5; - icon_state = "red" +/obj/structure/chair/comfy/brown{ + dir = 8 }, -/area/security/reception) +/obj/machinery/firealarm{ + dir = 4; + name = "east fire alarm"; + pixel_x = 26 + }, +/turf/simulated/floor/wood, +/area/library) "rIc" = ( /obj/machinery/light{ dir = 1; @@ -121486,8 +121643,14 @@ /area/blueshield) "rQj" = ( /obj/structure/table, -/obj/item/stack/packageWrap, -/obj/item/hand_labeler, +/obj/item/stack/packageWrap{ + pixel_y = 15 + }, +/obj/item/hand_labeler{ + pixel_y = 11 + }, +/obj/item/paper_bin, +/obj/item/pen, /turf/simulated/floor/plasteel{ dir = 10; icon_state = "brown" @@ -122118,6 +122281,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/structure/disposalpipe/segment, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, /turf/simulated/floor/plasteel{ icon_state = "white" }, @@ -127172,6 +127338,9 @@ name = "Quarantine Lockdown"; opacity = 0 }, +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 6; icon_state = "whiteblue"; @@ -127509,15 +127678,19 @@ }, /area/mimeoffice) "sJJ" = ( -/obj/machinery/camera{ - c_tag = "Second Floor Central Ring West Hallway 2"; - dir = 4 +/obj/structure/cable{ + icon_state = "0-8" }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "red" +/obj/effect/spawner/window/reinforced, +/obj/structure/curtain/open/shower/security{ + alpha = 255; + icon_state = "closed"; + name = "backstage"; + opacity = 1; + anchored = 1 }, -/area/hallway/primary/central/second/west) +/turf/simulated/floor/plating, +/area/security/medbay) "sJK" = ( /obj/machinery/light_switch{ pixel_x = 24 @@ -129609,7 +129782,9 @@ /obj/machinery/door/firedoor/border_only{ dir = 1 }, -/obj/machinery/vending/artvend, +/obj/machinery/computer/merch{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 5; icon_state = "brown" @@ -129707,6 +129882,9 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, /turf/simulated/floor/plasteel{ dir = 1 }, @@ -130025,11 +130203,6 @@ icon_state = "dark" }, /area/chapel/office) -"tbV" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/random_spawners/blood_5, -/turf/simulated/floor/plating, -/area/maintenance/secpost) "tci" = ( /obj/structure/chair/wood{ dir = 8 @@ -131759,7 +131932,6 @@ /obj/structure/chair/comfy/black{ dir = 4 }, -/obj/effect/landmark/start/barber, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -131990,6 +132162,16 @@ /obj/machinery/suit_storage_unit/engine, /turf/simulated/floor/plating, /area/storage/secure) +"tqp" = ( +/obj/machinery/door/window/brigdoor/security{ + name = "Brig Medical Bay"; + req_access = list(63) + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "dark" + }, +/area/security/medbay) "tqr" = ( /obj/structure/table, /obj/item/storage/box/bodybags, @@ -132281,14 +132463,13 @@ }, /area/security/prison/cell_block/A) "tsd" = ( -/obj/structure/cable{ - icon_state = "1-2" +/obj/structure/table, +/obj/item/radio/intercom{ + pixel_y = -28 }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/segment, +/obj/item/storage/fancy/donut_box, /turf/simulated/floor/plasteel{ - dir = 1 + icon_state = "red" }, /area/security/prison/cell_block/A) "tsf" = ( @@ -132378,6 +132559,21 @@ icon_state = "neutral" }, /area/hallway/primary/central/second/east) +"tsI" = ( +/obj/machinery/suit_storage_unit/brigmed{ + req_access = list(5) + }, +/obj/machinery/light{ + dir = 4 + }, +/obj/structure/sign/poster/official/space_cops{ + pixel_x = 32 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "dark" + }, +/area/security/medbay) "tsP" = ( /obj/structure/sink{ dir = 4; @@ -132396,12 +132592,10 @@ }, /area/crew_quarters/chief) "tsS" = ( -/obj/item/twohanded/required/kirbyplants, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + icon_state = "red" }, -/area/security/medbay) +/area/hallway/primary/central/west) "tsV" = ( /obj/structure/table/wood, /obj/machinery/computer/security/wooden_tv, @@ -133177,20 +133371,6 @@ icon_state = "dark" }, /area/chapel/office) -"txj" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/trunk/multiz{ - dir = 2 - }, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "dark" - }, -/area/medical/genetics) "txo" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -136224,17 +136404,14 @@ /turf/simulated/wall/r_wall, /area/engineering/controlroom) "tTq" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/cable{ - icon_state = "4-8" +/obj/structure/stairs{ + dir = 1; + layer = 2 }, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + icon_state = "neutralfull" }, -/area/security/medbay) +/area/hallway/primary/central/west) "tTs" = ( /obj/machinery/hydroponics/soil, /obj/item/seeds/tower, @@ -136283,13 +136460,6 @@ /area/maintenance/fore2) "tTL" = ( /obj/effect/decal/warning_stripes/east, -/obj/machinery/atmospherics/unary/vent_scrubber{ - dir = 8; - name = "standard air scrubber"; - on = 1; - scrub_N2O = 1; - scrub_Toxins = 1 - }, /obj/machinery/camera{ c_tag = "Atmospherics Access"; dir = 8; @@ -139602,12 +139772,11 @@ }, /area/hallway/primary/central) "utz" = ( -/obj/machinery/door/airlock{ - id_tag = "toilet2"; - name = "Toilet" +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralcorner" }, -/turf/simulated/floor/plasteel/freezer, -/area/crew_quarters/toilet2) +/area/hallway/primary/central/west) "utG" = ( /obj/structure/railing/corner, /turf/simulated/floor/glass/reinforced, @@ -139741,17 +139910,13 @@ }, /area/quartermaster/office) "uuC" = ( -/obj/machinery/suit_storage_unit/brigmed{ - req_access = list(5) - }, -/obj/machinery/light{ - dir = 4 +/obj/structure/disposalpipe/segment{ + dir = 10 }, /turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "dark" + dir = 1 }, -/area/security/medbay) +/area/security/processing) "uuH" = ( /obj/structure/closet/secure_closet/ntrep, /turf/simulated/floor/carpet/royalblue, @@ -141736,6 +141901,10 @@ /obj/item/shield/riot, /obj/item/shield/riot, /obj/item/shield/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, +/obj/item/clothing/shoes/combat/riot, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -142143,29 +142312,6 @@ dir = 8 }, /area/crew_quarters/locker) -"uNu" = ( -/obj/structure/cable{ - icon_state = "2-8" - }, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 10; - initialize_directions = 10 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 10 - }, -/obj/structure/cable{ - d1 = 4; - d2 = 8; - icon_state = "4-8" - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cloning) "uNv" = ( /obj/structure/cable/yellow{ d1 = 1; @@ -144331,14 +144477,15 @@ /turf/simulated/floor/engine, /area/engineering/supermatter) "vdg" = ( -/obj/structure/chair/comfy/purp{ - dir = 4 - }, -/obj/item/radio/intercom{ - pixel_y = -28 +/obj/effect/decal/warning_stripes/yellow/hollow, +/obj/effect/spawner/lootdrop/maintenance, +/obj/effect/decal/cleanable/dirt, +/obj/structure/closet/crate, +/turf/simulated/floor/plasteel{ + dir = 5; + icon_state = "dark" }, -/turf/simulated/floor/carpet/purple, -/area/crew_quarters/captain) +/area/quartermaster/storage) "vdh" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -144480,12 +144627,12 @@ "vep" = ( /obj/effect/decal/warning_stripes/blue/hollow, /obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, /obj/machinery/light{ dir = 8 }, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -144610,24 +144757,12 @@ /turf/simulated/floor/grass, /area/crew_quarters/serviceyard) "vfv" = ( -/obj/structure/toilet{ - pixel_y = 19 - }, -/obj/machinery/light/small{ - dir = 8 - }, -/obj/machinery/door_control{ - desiredstate = 1; - id = "toilet2"; - name = "Toilet Bolt Control"; - normaldoorcontrol = 1; - specialfunctions = 4; - pixel_x = 25 +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, +/turf/simulated/floor/plasteel{ + icon_state = "red" }, -/obj/effect/decal/warning_stripes/yellow/hollow, -/obj/effect/landmark/start/civilian, -/turf/simulated/floor/plasteel/freezer, -/area/crew_quarters/toilet2) +/area/hallway/primary/central/second/west) "vfx" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 9 @@ -146557,8 +146692,10 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/obj/structure/disposalpipe/sortjunction/reversed{ + dir = 8; + name = "Brig Physician"; + sortType = 24 }, /turf/simulated/floor/plasteel{ dir = 4; @@ -147156,9 +147293,6 @@ /obj/machinery/light{ dir = 8 }, -/obj/structure/sign/poster/official/obey{ - pixel_x = -32 - }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -147418,7 +147552,7 @@ }, /obj/structure/sign/directions/security{ pixel_y = 8; - dir = 1 + dir = 8 }, /turf/simulated/wall, /area/hallway/primary/central) @@ -147674,6 +147808,22 @@ icon_state = "dark" }, /area/turret_protected/aisat_interior) +"vFN" = ( +/obj/machinery/camera{ + c_tag = "Central Bridge Hallway East 2"; + dir = 10 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/obj/structure/cable{ + icon_state = "4-8" + }, +/turf/simulated/floor/engine{ + slowdown = -0.3 + }, +/area/hallway/primary/central) "vFV" = ( /obj/machinery/atmospherics/pipe/simple/visible{ dir = 6 @@ -148532,11 +148682,18 @@ }, /area/hallway/primary/starboard/south) "vMH" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 4 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/obj/structure/cable{ + icon_state = "1-2" }, -/turf/simulated/floor/wood, -/area/hallway/primary/central/west) +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/turf/simulated/floor/plasteel{ + dir = 1 + }, +/area/security/processing) "vMI" = ( /obj/structure/cable{ icon_state = "1-2" @@ -148850,6 +149007,25 @@ /obj/effect/spawner/lootdrop/maintenance, /turf/simulated/floor/plating, /area/maintenance/fore2) +"vOz" = ( +/obj/effect/mapping_helpers/airlock/unres{ + dir = 1 + }, +/obj/structure/cable{ + icon_state = "1-2" + }, +/obj/machinery/door/airlock/medical/glass{ + id = "cloninglab"; + name = "Cloning Lab"; + req_access = list(5) + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/simple/hidden/supply, +/turf/simulated/floor/plasteel{ + icon_state = "white" + }, +/area/medical/cloning) "vOB" = ( /turf/simulated/floor/plasteel{ icon_state = "white" @@ -149012,14 +149188,15 @@ }, /area/medical/medbay) "vQu" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 4 +/obj/machinery/camera{ + c_tag = "Second Floor Central Ring West Hallway 2"; + dir = 5 }, -/obj/structure/disposalpipe/segment{ - dir = 4 +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "red" }, -/turf/simulated/floor/wood, -/area/hallway/primary/central/west) +/area/hallway/primary/central/second/west) "vQE" = ( /turf/simulated/floor/plasteel{ dir = 5; @@ -149079,11 +149256,11 @@ }, /area/crew_quarters/fitness) "vRd" = ( -/obj/item/twohanded/required/kirbyplants, /obj/machinery/alarm{ dir = 1; pixel_y = -24 }, +/obj/structure/table, /turf/simulated/floor/plasteel{ icon_state = "redfull"; tag = "icon-redfull (NORTHWEST)" @@ -151295,6 +151472,17 @@ /obj/structure/flora/ausbushes/reedbush, /turf/simulated/floor/grass, /area/hydroponics) +"whL" = ( +/obj/structure/chair/comfy/brown{ + dir = 4 + }, +/obj/effect/landmark/start/civilian, +/obj/item/radio/intercom{ + dir = 1; + pixel_y = 24 + }, +/turf/simulated/floor/wood, +/area/library) "wih" = ( /obj/item/stack/medical/bruise_pack, /obj/item/stack/medical/bruise_pack/advanced, @@ -151703,6 +151891,9 @@ name = "west fire alarm"; pixel_x = -24 }, +/obj/item/storage/toolbox/emergency, +/obj/item/crowbar, +/obj/item/wrench, /turf/simulated/floor/plasteel/white, /area/teleporter) "wkD" = ( @@ -152523,20 +152714,14 @@ }, /area/engineering/break_room) "wqf" = ( -/obj/machinery/power/apc{ - cell_type = 5000; - dir = 4; - name = "east bump"; - pixel_x = 26 - }, -/obj/structure/cable{ - d2 = 8; - icon_state = "0-8" +/obj/machinery/alarm{ + pixel_y = 24 }, /turf/simulated/floor/plasteel{ - icon_state = "redcorner" + dir = 1; + icon_state = "red" }, -/area/security/prison/cell_block/A) +/area/hallway/primary/central/second/west) "wqh" = ( /obj/effect/decal/cleanable/dust, /obj/effect/spawner/random_spawners/rodent, @@ -152790,19 +152975,6 @@ }, /turf/simulated/floor/plating, /area/atmos) -"wrr" = ( -/obj/machinery/computer/rdconsole/core, -/obj/effect/decal/warning_stripes/northwest, -/obj/machinery/requests_console{ - department = "Research"; - departmentType = 2; - name = "Research Request Console"; - pixel_x = -30 - }, -/turf/simulated/floor/plasteel{ - icon_state = "dark" - }, -/area/toxins/lab) "wrz" = ( /turf/simulated/openspace, /area/security/processing) @@ -152996,9 +153168,6 @@ /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 8 }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, @@ -155336,17 +155505,13 @@ /turf/simulated/floor/carpet, /area/maintenance/casino) "wLT" = ( -/obj/machinery/door/airlock/medical{ - name = "Brig Physician's Quarters"; - req_access = list(5); - security_level = 1 +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 4 }, /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, /turf/simulated/floor/plasteel{ dir = 1; icon_state = "dark" @@ -155836,7 +156001,12 @@ /obj/structure/cable{ icon_state = "4-8" }, -/obj/item/twohanded/required/kirbyplants, +/obj/structure/table, +/obj/item/storage/toolbox/mechanical{ + pixel_x = -2; + pixel_y = -1 + }, +/obj/item/storage/belt/utility, /turf/simulated/floor/plasteel{ dir = 9; icon_state = "purple" @@ -156432,11 +156602,13 @@ }, /area/medical/ward) "wUn" = ( -/obj/item/radio/intercom{ - pixel_y = -32 +/obj/effect/turf_decal/number/number_2{ + dir = 1; + pixel_y = -4 }, +/obj/effect/turf_decal/arrows/white, /turf/simulated/floor/plasteel{ - icon_state = "neutralcorner" + icon_state = "neutralfull" }, /area/hallway/primary/starboard) "wUw" = ( @@ -156710,6 +156882,15 @@ icon_state = "whiteblue" }, /area/medical/reception) +"wWY" = ( +/obj/effect/turf_decal/siding/red{ + dir = 8 + }, +/obj/structure/chair/comfy/red{ + dir = 1 + }, +/turf/simulated/floor/carpet/red, +/area/security/medbay) "wWZ" = ( /obj/machinery/atmospherics/pipe/simple/visible/yellow{ desc = "Подаёт азот из атмосферки в систему охлаждения реактора, таким образом запитывая её хладагентом"; @@ -157924,7 +158105,8 @@ security_level = 1 }, /turf/simulated/floor/plasteel{ - icon_state = "redcorner" + dir = 0; + icon_state = "red" }, /area/security/prison/cell_block/A) "xgS" = ( @@ -158192,10 +158374,10 @@ }, /area/hallway/primary/central/sw) "xiz" = ( -/obj/machinery/computer/borgupload, /obj/item/radio/intercom/private{ pixel_y = -28 }, +/obj/machinery/computer/aiupload/cyborg, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -158818,26 +159000,31 @@ /turf/simulated/floor/wood, /area/maintenance/livingcomplex) "xnd" = ( -/obj/structure/table, +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/item/ai_module/crewsimov, +/obj/item/ai_module/freeformcore, +/obj/item/ai_module/corp, +/obj/item/ai_module/paladin, +/obj/item/ai_module/robocop, +/obj/structure/table/glass, /obj/machinery/door/window{ base_state = "right"; + dir = 4; icon_state = "right"; name = "Core Modules"; req_access = list(20) }, -/obj/structure/window/reinforced, /obj/structure/window/reinforced{ - dir = 1 + dir = 2 }, -/obj/structure/window/reinforced{ - dir = 8 +/turf/simulated/floor/plasteel{ + icon_state = "dark" }, -/obj/item/aiModule/crewsimov, -/obj/item/aiModule/freeformcore, -/obj/item/aiModule/corp, -/obj/item/aiModule/paladin, -/obj/item/aiModule/robocop, -/turf/simulated/floor/bluegrid, /area/turret_protected/ai_upload) "xnf" = ( /obj/machinery/ai_status_display, @@ -158885,6 +159072,9 @@ /obj/structure/disposalpipe/segment, /obj/machinery/door/firedoor, /obj/effect/decal/warning_stripes/yellow, +/obj/structure/sign/poster/official/obey{ + pixel_x = -32 + }, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "red" @@ -159126,24 +159316,6 @@ }, /turf/simulated/floor/plasteel, /area/maintenance/xenozoo) -"xqb" = ( -/obj/machinery/door/firedoor, -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/door/airlock/medical/glass{ - id = "cloninglab"; - name = "Genetics Lab"; - req_access = list(9) - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - dir = 8; - icon_state = "dark" - }, -/area/medical/genetics) "xqq" = ( /obj/structure/cable{ icon_state = "1-2" @@ -160655,14 +160827,21 @@ /turf/simulated/floor/plating, /area/maintenance/brig) "xBd" = ( -/obj/structure/closet/secure_closet/security, -/obj/item/clothing/mask/balaclava, -/obj/effect/decal/warning_stripes/red/hollow, +/obj/structure/sign/security{ + pixel_x = -32 + }, +/obj/effect/turf_decal/number/number_2{ + dir = 1; + pixel_y = -18 + }, +/obj/effect/turf_decal/arrows/white{ + dir = 1 + }, /turf/simulated/floor/plasteel{ - dir = 9; + dir = 8; icon_state = "red" }, -/area/security/reception) +/area/hallway/primary/central/west) "xBh" = ( /obj/structure/cable{ icon_state = "1-2" @@ -160777,12 +160956,10 @@ /area/toxins/mixing) "xBF" = ( /obj/structure/window/reinforced, -/obj/structure/sign/goldenplaque{ - pixel_y = 32; - desc = "Важное Уточнение! Рабочие пожелали оставаться анонимными, поэтому, обойдёмся их прозвищами. За помощь Главному Инженеру Новы в поисках и устранении неисправностей на станции НаноТрейзен. С благодарностью, Aeterna0. Слава НаноТрейзен!"; - name = "Благодарственное Письмо от Главного Инженера станции Нова" +/obj/structure/statue/gold/unathi{ + desc = "Перед собою вы наблюдаете статую унати, которая держит в руках чертежи станции очень похожие на станцию Нова. Надпись на табличке - Один из двух главных инженеров союза Таяр и Унати, по прозвищу PiroMage, принимавших участие в разработке передовой научно-исследовательской станции НаноТрейзен - Nova."; + name = "К.А.З" }, -/obj/structure/statue/gold/ce, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -161218,21 +161395,6 @@ }, /turf/simulated/floor/wood/fancy/cherry, /area/crew_quarters/theatre) -"xFq" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 - }, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cloning) "xFr" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -161297,26 +161459,6 @@ /obj/effect/decal/ants, /turf/simulated/floor/plating, /area/security/permabrig) -"xFT" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers{ - dir = 8 - }, -/obj/structure/cable{ - icon_state = "2-4" - }, -/obj/structure/cable{ - icon_state = "1-4" - }, -/obj/structure/disposalpipe/junction/reversed{ - dir = 2 - }, -/turf/simulated/floor/plasteel{ - icon_state = "white" - }, -/area/medical/cryo) "xGl" = ( /obj/machinery/alarm{ pixel_y = 24 @@ -161765,8 +161907,8 @@ frequency = 1379; layer = 3.3; master_tag = "port4"; - name = "exterior access button"; - pixel_x = 24; + name = "interior access button"; + pixel_x = 28; pixel_y = 28 }, /turf/simulated/floor/plasteel, @@ -163141,17 +163283,13 @@ /turf/simulated/floor/plating, /area/medical/cmo) "xTo" = ( -/obj/effect/turf_decal/siding/red{ - dir = 8 - }, -/obj/structure/chair/comfy/red{ - dir = 1 - }, -/obj/structure/cable{ - icon_state = "4-8" +/obj/machinery/door/firedoor, +/obj/effect/decal/warning_stripes/yellow, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "red" }, -/turf/simulated/floor/carpet/red, -/area/security/medbay) +/area/hallway/primary/central/west) "xTp" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/light/small{ @@ -163345,8 +163483,12 @@ }, /area/engineering/engine) "xUn" = ( -/obj/machinery/light{ - dir = 4 +/obj/structure/table/wood, +/obj/item/flashlight/lamp/bananalamp{ + pixel_y = 6 + }, +/obj/structure/sign/poster/official/random{ + pixel_y = 32 }, /turf/simulated/floor/wood, /area/library) @@ -163665,11 +163807,10 @@ /area/turret_protected/aisat) "xWy" = ( /obj/structure/table, -/obj/item/storage/fancy/donut_box, /obj/effect/decal/warning_stripes/red/hollow, /turf/simulated/floor/plasteel{ - dir = 4; - icon_state = "redcorner" + dir = 5; + icon_state = "red" }, /area/security/prison/cell_block/A) "xWA" = ( @@ -165843,8 +165984,10 @@ /obj/structure/cable{ icon_state = "1-2" }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, +/obj/machinery/atmospherics/pipe/manifold/hidden/supply{ + dir = 4 + }, /turf/simulated/floor/plasteel{ dir = 1 }, @@ -186823,7 +186966,7 @@ wkB jOC lDx lDx -lDx +qel cTb tho tgX @@ -187079,8 +187222,8 @@ jmb sbP jOC oSy -sbk -dyL +ipA +qel geO dGr iSL @@ -187337,7 +187480,7 @@ blG jOC lDx lDx -lDx +qel aRT wwd wCj @@ -187592,9 +187735,9 @@ qZX hKl hKl jOC -oSy -sbk -dyL +jOC +jOC +jOC qel qel ejf @@ -187755,7 +187898,7 @@ tkq tkq tkq tkq -tkq +igl tWQ xEB iZn @@ -187841,18 +187984,18 @@ eJI xpR eev eLc -vfv -utz +hDd +pBR jmb brS -jmb -pBR -hDd +qTF +hKl +tTq +xBd +onF +dLR jOC -lDx -lDx -lDx -qel +whL qel wMQ qog @@ -188102,20 +188245,20 @@ hKl hKl xGJ nMq -jmb -hKl +sbP hKl +tTq +dhv +qYe +tsS jOC -qel -qel -qel xUn -cOi +qel tcq fYU qel qel -cKt +qel qel qel qel @@ -188355,24 +188498,24 @@ gOS lWt yaO eLc -hVx -dhv +ucN +wtT raK brS nUw -wtT -ucN -jOC +hKl +tTq +pRD qYe itL -pSU -jOC -gnD -hoT -dxl -svv -gnD jOC +rIa +qel +tcq +fYU +qel +qel +cKt oPu wUB pSU @@ -188619,17 +188762,17 @@ pMp hKl hKl hKl +xTo +pOZ +jXF +jOC jOC gnD +hoT +dxl +svv gnD -gnD -qTF -onF -vQu -ldS -vMH -onF -qTF +jOC gnD gnD gnD @@ -188877,8 +189020,8 @@ gNr iFh fhv kVT -svF -dAF +qYe +utz dAF cLB dAF @@ -189391,7 +189534,7 @@ qUs twQ xQk dJD -dJD +hVx dJD dJD mZm @@ -190112,12 +190255,12 @@ jvn tdr clY qcI -txj -xqb -mRP -mRP -xFq -cZo +lxy +nrr +kOr +kOr +lzn +bil nEG hya fXb @@ -190374,13 +190517,13 @@ qRz eri fcC hPA -uNu -fCz -cgR -cgR -aAP -cgR -xFT +iza +vOz +epN +epN +mND +epN +nBP qgs rOG ieh @@ -190930,7 +191073,7 @@ tkq bje mKV bvT -gbK +hdq bje tkq tkq @@ -194029,7 +194172,7 @@ jCS jCS jCS wdB -tel +hFZ rKy pDd nMQ @@ -198620,7 +198763,7 @@ wqw uVH hQo jlb -wrr +rzi iuU tYL cod @@ -203266,7 +203409,7 @@ tkq bje vFC bvT -fgL +vFN bje tkq tkq @@ -205836,11 +205979,11 @@ gYL vBS wOy aAg -vSm +olq bib -hGX -uAS -xQi +wUn +bIO +cbh bJk xQi ipK @@ -206095,10 +206238,10 @@ jDY aAg vSm bib -hGX -jsN -fYb -fYb +wUn +bIO +cbh +aWH fYb fYb fYb @@ -206353,9 +206496,9 @@ aAg qgK bib wUn +bIO cbh -ulh -ulh +xQi tll xQi xQi @@ -206612,7 +206755,7 @@ bib hTM cbh cbh -olq +xQi xQi onM kBO @@ -207643,7 +207786,7 @@ cbh sbO eaG xQi -ulh +vdg uZS xAQ jVA @@ -214574,9 +214717,9 @@ pyG rfy kXM drY -lsl +fxY smY -nVF +lsl iuZ cMD kfW @@ -250292,12 +250435,12 @@ wZD pQL pQL mNo -arB -arB -arB -arB +vMH +pQL +pQL +pQL enR -laB +nNM gDv qaJ mlJ @@ -250549,7 +250692,7 @@ xJJ cZI xJJ vwC -xJJ +enP xAc xJJ xJJ @@ -250557,7 +250700,7 @@ xJJ eVq hVc pTl -pTl +meh uHl pTl uMr @@ -250814,7 +250957,7 @@ yal dlD uZW pTl -pTl +meh aor txH txH @@ -251071,7 +251214,7 @@ fbQ dlD opi pTl -pTl +uuC tah pTl pTl @@ -252363,7 +252506,7 @@ bLy ned ned kXl -nNM +uvt dTg dLI uJf @@ -252615,12 +252758,12 @@ iif sOq uKm iGe +lLl xkW iCq reV -iCq +kxQ hTF -tTq dTg kAM dyG @@ -252865,19 +253008,19 @@ vZU hqo lRi kYp -uWF +fpy tNj dlD pvc qVi flR dRn +uvt peM eSY aow -pfM +jRZ aow -anR dTg rUr vdY @@ -253121,20 +253264,20 @@ bpy vZU lLD dlD -pkl -kBk -onQ +dlD +dlD dlD dlD vvd -pOZ +vvd +vvd kAc +tqp iRC vvd -ipA -cGc -fpy -aag +vvd +vvd +vvd aag aag aag @@ -253371,28 +253514,28 @@ eeg pve dlD aeF -kco -kco -kco +dsN +dsN +dsN jIi vZU kco -jIi -jIi -jIi -jIi -jIi +dsN +ovj +dlD +hpC +qpZ nBo -rvo -tsS -dRn -ara +dQT vvd +anR +uvt +ara +dlT wSZ -xTo +wWY tHG aag -xBd cAA kzk lhJ @@ -253631,25 +253774,25 @@ dsf xzQ xzQ xzQ -xzQ +guO oQK +ldS +xzQ tsd -tsd -tsd -tsd +dlD bvn -tsd -tsd +fnc +gYr dQT -enP +vvd pyX nNK wLT jKN fky +qwg xAm aag -kxQ cbk aGO ylQ @@ -253885,34 +254028,34 @@ xkH wIv dlD xWy -vTl +rvo kgG omk vTl idP -hqo +cOi eqG jkm -qwg +dlD wqf -hqo +fnc jlI -dlT +dQT +vvd aBV ich sGQ -vvd +sJJ fwA -uuC +tsI wac aag -rIa sbZ kfv tIf oer wuy -tIf +dIS ctV uRS reI @@ -254148,12 +254291,13 @@ dlD cjK loc xgP -aYk -dlD -dlD -dlD dlD +aYk dlD +pfM +prN +vfv +vvd vvd vvd vvd @@ -254162,7 +254306,6 @@ vvd vvd vvd vvd -aag dov xQG taj @@ -254404,19 +254547,19 @@ nEP vBO kdp dgg -mvk +cGc vBG mvk -mvk -mvk -sJJ -mvk +vQu +boI +fnc +fnc mvk oHk -mvk mSB -vBG mvk +vBG +nVF mvk mvk wbN @@ -254619,7 +254762,7 @@ ozQ kux ozQ ozQ -rcy +fkL ozQ ozQ ozQ @@ -255218,7 +255361,7 @@ ieI gtF otn kZy -lLc +wUw chE ddQ bbZ @@ -255648,8 +255791,8 @@ lVZ wdp ozQ pLK -rAR -jfC +jOe +loQ lnp qIY tHo @@ -255732,7 +255875,7 @@ orm kfz xKb thB -nwA +wre xic ddQ oqM @@ -256758,7 +256901,7 @@ btM syu gVD bbZ -jQq +wre mVH bRV wre @@ -257275,7 +257418,7 @@ awC xRZ pWi wUw -jQq +wre ihn xXG aiQ @@ -257533,7 +257676,7 @@ xRZ wre wCs rhi -tbV +wUw uhC cPv tUk @@ -258047,7 +258190,7 @@ hoH mTa mqL jYH -jQq +wre awC ibR jAH @@ -264647,7 +264790,7 @@ aFR ges npi rsU -vdg +rsU qCF iOP ihk @@ -265423,7 +265566,7 @@ qCF mXr ihk kjR -jLR +mpH tbl biB icw @@ -271375,11 +271518,11 @@ cUD rwI rwI rwI -aWH -eEq +rwI +qxP pfs eEq -eEq +cDR rQj fFn gHy @@ -271632,8 +271775,8 @@ rNE rwI rwI rwI -bIO -cSB +rwI +nio aQz cqJ cSB @@ -271889,7 +272032,7 @@ cUD rwI rwI rwI -cDR +rwI nio emS sCT diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 397ab30cc3a..504bcb60d22 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -98,6 +98,14 @@ /datum/controller/subsystem/processing/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/processing/##X +#define FLUID_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/fluids/##X);\ +/datum/controller/subsystem/fluids/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/fluids/##X/fire() {..() /*just so it shows up on the profiler*/} \ +/datum/controller/subsystem/fluids/##X + #define TIMER_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/timer/##X);\ /datum/controller/subsystem/timer/##X/New(){\ NEW_SS_GLOBAL(SS##X);\ diff --git a/code/__DEFINES/blob.dm b/code/__DEFINES/blob.dm index 27f64f17f0a..b9e70680e5f 100644 --- a/code/__DEFINES/blob.dm +++ b/code/__DEFINES/blob.dm @@ -13,6 +13,7 @@ #define THIRD_STAGE_COEF 0.75 #define FIRST_STAGE_THRESHOLD 300 #define SECOND_STAGE_THRESHOLD 400 +#define THIRD_STAGE_DELTA_THRESHOLD 250 #define BLOB_STAGE_NONE -1 #define BLOB_STAGE_ZERO 0 #define BLOB_STAGE_FIRST 1 @@ -48,9 +49,144 @@ #define FIRST_STAGE_WARN span_userdanger("Вы чувствуете усталость и раздутость.") #define SECOND_STAGE_WARN span_userdanger("Вы чувствуете, что вот-вот лопнете.") -#define isblobbernaut(M) istype((M), /mob/living/simple_animal/hostile/blob/blobbernaut) + +#define TOTAL_BLOB_MASS SSticker?.mode?.legit_blobs?.len +#define NEEDED_BLOB_MASS SSticker?.mode?.blob_win_count //Few global vars to track the blob GLOBAL_LIST_EMPTY(blobs) GLOBAL_LIST_EMPTY(blob_cores) GLOBAL_LIST_EMPTY(blob_nodes) + +// Overmind defines + +#define OVERMIND_MAX_POINTS_DEFAULT 100 // Max point storage +#define OVERMIND_STARTING_POINTS 60 // Points granted upon start +#define OVERMIND_STARTING_REROLLS 1 // Free strain rerolls at the start +#define OVERMIND_MAX_CAMERA_STRAY "3x3" // How far the overmind camera is allowed to stray from blob tiles. 3x3 is 1 tile away, 5x5 2 tiles etc + + +// Generic blob defines + +#define BLOB_BASE_POINT_RATE 2 // Base amount of points per process() +#define BLOB_EXPAND_COST 4 // Price to expand onto a new tile +#define BLOB_ZOMBIFICATION_COST 5 +#define BLOB_ATTACK_REFUND 2 // Points 'refunded' when the expand attempt actually attacks something instead +#define BLOB_BRUTE_RESIST 0.5 // Brute damage taken gets multiplied by this value +#define BLOB_FIRE_RESIST 1 // Burn damage taken gets multiplied by this value +#define BLOB_EXPAND_CHANCE_MULTIPLIER 1 // Increase this value to make blobs naturally expand faster +#define BLOB_REINFORCE_CHANCE 2.5 // The seconds_per_tick chance for cores/nodes to reinforce their surroundings +#define BLOB_REAGENT_ATK_VOL 25 // Amount of strain-reagents that get injected when the blob attacks: main source of blob damage +#define BLOB_REAGENT_SPORE_VOL 10 +#define BLOB_BONUS_POINTS 60 +#define BLOB_REAGENTS_METABOLISM 1 + + +// Structure properties + +#define BLOB_CORE_MAX_HP 400 +#define BLOB_CORE_HP_REGEN 2 // Bases health regeneration rate every process(), can be added on by strains +#define BLOB_CORE_CLAIM_RANGE 12 // Range in which blob tiles are 'claimed' (converted from dead to alive, rarely useful) +#define BLOB_CORE_PULSE_RANGE 4 // The radius up to which the core activates structures, and up to which structures can be built +#define BLOB_CORE_EXPAND_RANGE 3 // Radius of automatic expansion +#define BLOB_CORE_STRONG_REINFORCE_RANGE 1 // The radius of tiles surrounding the core that get upgraded +#define BLOB_CORE_REFLECTOR_REINFORCE_RANGE 0 +#define BLOB_CORE_FIRE_RESIST 2 +#define BLOB_CORE_POINT_RATE 2 + +#define BLOB_NODE_MAX_HP 200 +#define BLOB_NODE_HP_REGEN 3 +#define BLOB_NODE_MIN_DISTANCE 5 // Minimum distance between nodes +#define BLOB_NODE_CLAIM_RANGE 10 +#define BLOB_NODE_PULSE_RANGE 3 // The radius up to which the core activates structures, and up to which structures can be built +#define BLOB_NODE_EXPAND_RANGE 2 // Radius of automatic expansion +#define BLOB_NODE_STRONG_REINFORCE_RANGE 0 // The radius of tiles surrounding the node that get upgraded +#define BLOB_NODE_REFLECTOR_REINFORCE_RANGE 0 + +#define BLOB_FACTORY_MAX_HP 200 +#define BLOB_FACTORY_HP_REGEN 1 +#define BLOB_FACTORY_MIN_DISTANCE 7 // Minimum distance between factories +#define BLOB_FACTORY_MAX_SPORES 3 + +#define BLOB_RESOURCE_MAX_HP 60 +#define BLOB_RESOURCE_HP_REGEN 15 +#define BLOB_RESOURCE_MIN_DISTANCE 4 // Minimum distance between resource blobs +#define BLOB_RESOURCE_GATHER_DELAY (4 SECONDS) // Gather points when pulsed outside this interval +#define BLOB_RESOURCE_GATHER_ADDED_DELAY (0.25 SECONDS) // Every additional resource blob adds this amount to the gather delay +#define BLOB_RESOURCE_POINT_RATE 1 + +#define BLOB_REGULAR_MAX_HP 25 +#define BLOB_REGULAR_HP_INIT 21 // The starting HP of a normal blob tile +#define BLOB_REGULAR_HP_REGEN 1 // Health regenerated when pulsed by a node/core + +#define BLOB_STRONG_MAX_HP 150 +#define BLOB_STRONG_HP_REGEN 2 +#define BLOB_STRONG_BRUTE_RESIST 0.25 // Brute damage taken gets multiplied by this value + +#define BLOB_CAP_NUKE_MAX_HP 100 +#define BLOB_CAP_NUKE_HP_REGEN 1 + +#define BLOB_REFLECTOR_MAX_HP 150 +#define BLOB_REFLECTOR_HP_REGEN 2 + +#define BLOB_STORAGE_MAX_HP 30 +#define BLOB_STORAGE_MAX_POINTS_BONUS 50 +#define BLOB_STORAGE_MIN_DISTANCE 3 +#define BLOB_STORAGE_FIRE_RESIST 2 + + +// Structure purchasing + +#define BLOB_UPGRADE_STRONG_COST 15 // Upgrade and build costs here +#define BLOB_UPGRADE_REFLECTOR_COST 15 +#define BLOB_STRUCTURE_RESOURCE_COST 40 +#define BLOB_STRUCTURE_STORAGE_COST 40 +#define BLOB_STRUCTURE_FACTORY_COST 60 +#define BLOB_STRUCTURE_NODE_COST 50 +#define BLOB_CORE_SPLIT_COST 100 + +#define BLOB_REFUND_STRONG_COST 4 // Points refunded when destroying the structure +#define BLOB_REFUND_REFLECTOR_COST 4 +#define BLOB_REFUND_RESOURCE_COST 15 +#define BLOB_REFUND_FACTORY_COST 25 +#define BLOB_REFUND_NODE_COST 25 +#define BLOB_REFUND_STORAGE_COST 12 +#define BLOB_REFUND_CORE_COST -1 +#define BLOB_REFUND_CAP_NUKE_COST 0 + +// Blob power properties + +#define BLOB_POWER_RELOCATE_COST 80 // Resource cost to move your core to a different node +#define BLOB_POWER_REROLL_COST 40 // Strain reroll +#define BLOB_POWER_REROLL_FREE_TIME (4 MINUTES) // Gain a free strain reroll every x minutes +#define BLOB_POWER_REROLL_CHOICES 6 // Possibilities to choose from; keep in mind increasing this might fuck with the radial menu + + +// Mob defines + +#define BLOBMOB_HEALING_MULTIPLIER 0.0125 // Multiplies by -maxHealth and heals the blob by this amount every blob_act +#define BLOBMOB_SPORE_HEALTH 30 // Base spore health +#define BLOBMOB_SPORE_SPAWN_COOLDOWN (8 SECONDS) +#define BLOBMOB_SPORE_DMG_LOWER 3 +#define BLOBMOB_SPORE_DMG_UPPER 7 +#define BLOBMOB_SPORE_OBJ_DMG 20 +#define BLOBMOB_SPORE_SPEED_MOD -1 +#define BLOBMOB_ZOMBIE_HEALTH 70 // Base spore health +#define BLOBMOB_ZOMBIE_DMG_LOWER 10 +#define BLOBMOB_ZOMBIE_DMG_UPPER 15 +#define BLOBMOB_ZOMBIE_OBJ_DMG 20 +#define BLOBMOB_ZOMBIE_SPEED_MOD -0.3 +#define BLOBMOB_BLOBBERNAUT_RESOURCE_COST 40 // Purchase price for making a blobbernaut +#define BLOBMOB_BLOBBERNAUT_HEALTH 200 // Base blobbernaut health +#define BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER 20 // Damage without active overmind (core dead or xenobio mob) +#define BLOBMOB_BLOBBERNAUT_DMG_SOLO_UPPER 20 +#define BLOBMOB_BLOBBERNAUT_DMG_LOWER 4 // Damage dealt with active overmind (most damage comes from strain chems) +#define BLOBMOB_BLOBBERNAUT_DMG_UPPER 4 +#define BLOBMOB_BLOBBERNAUT_REAGENT_ATK_VOL 20 // Amounts of strain reagents applied on attack -- basically the main damage stat +#define BLOBMOB_BLOBBERNAUT_DMG_OBJ 60 // Damage dealth to objects/machines +#define BLOBMOB_BLOBBERNAUT_HEALING_CORE 0.05 // Percentage multiplier HP restored on Life() when within 2 tiles of the blob core +#define BLOBMOB_BLOBBERNAUT_HEALING_NODE 0.025 // Same, but for a nearby node +#define BLOBMOB_BLOBBERNAUT_HEALING_TILE 0.0125 // Same, but for a nearby blob tile +#define BLOBMOB_BLOBBERNAUT_HEALTH_DECAY 0.0125 // Percentage multiplier HP lost when not near blob tiles or without factory + +#define BLOB_ACT_PROTECTION_TIME 2 SECONDS diff --git a/code/__DEFINES/cargo.dm b/code/__DEFINES/cargo.dm new file mode 100644 index 00000000000..d8aa74db8fa --- /dev/null +++ b/code/__DEFINES/cargo.dm @@ -0,0 +1,37 @@ +#define STYLE_STANDARD 1 +#define STYLE_BLUESPACE 2 +#define STYLE_CENTCOM 3 +#define STYLE_SYNDICATE 4 +#define STYLE_BLUE 5 +#define STYLE_CULT 6 +#define STYLE_MISSILE 7 +#define STYLE_RED_MISSILE 8 +#define STYLE_BOX 9 +#define STYLE_HONK 10 +#define STYLE_FRUIT 11 +#define STYLE_INVISIBLE 12 +#define STYLE_GONDOLA 13 + +#define POD_ICON_STATE 1 +#define POD_NAME 2 +#define POD_DESC 3 + +#define POD_SHAPE_NORMAL 1 +#define POD_SHAPE_OTHER 2 + +#define RUBBLE_NONE 1 +#define RUBBLE_NORMAL 2 +#define RUBBLE_WIDE 3 +#define RUBBLE_THIN 4 + +#define POD_TRANSIT "1" +#define POD_FALLING "2" +#define POD_OPENING "3" +#define POD_LEAVING "4" + +#define MOB_OPTION "Mobs" +#define UNANCHORED_OPTION "Unanchored" +#define ANCHORED_OPTION "Anchored" +#define MECHA_OPTION "Mecha" + +#define SUPPLYPOD_X_OFFSET -16 diff --git a/code/__DEFINES/contracts.dm b/code/__DEFINES/contracts.dm index 26351cfbac2..b333b769108 100644 --- a/code/__DEFINES/contracts.dm +++ b/code/__DEFINES/contracts.dm @@ -19,40 +19,3 @@ #define CONTRACT_FOOD "Food" #define CONTRACT_SPACE "Space Gear" #define CONTRACT_CALAMITY "Calamity" - -#define BANE_SALT "salt" -#define BANE_LIGHT "light" -#define BANE_IRON "iron" -#define BANE_WHITECLOTHES "whiteclothes" -#define BANE_SILVER "silver" -#define BANE_HARVEST "harvest" -#define BANE_TOOLBOX "toolbox" - -#define OBLIGATION_FOOD "food" -#define OBLIGATION_FIDDLE "fiddle" -#define OBLIGATION_DANCEOFF "danceoff" -#define OBLIGATION_GREET "greet" -#define OBLIGATION_PRESENCEKNOWN "presenceknown" -#define OBLIGATION_SAYNAME "sayname" -#define OBLIGATION_ANNOUNCEKILL "announcekill" -#define OBLIGATION_ANSWERTONAME "answername" - -#define BAN_HURTWOMAN "hurtwoman" -#define BAN_HURTMAN "hurtman" -#define BAN_CHAPEL "chapel" -#define BAN_HURTPRIEST "hurtpriest" -#define BAN_AVOIDWATER "avoidwater" -#define BAN_STRIKEUNCONCIOUS "strikeunconcious" -#define BAN_HURTLIZARD "hurtlizard" -#define BAN_HURTANIMAL "hurtanimal" - -#define BANISH_WATER "water" -#define BANISH_COFFIN "coffin" -#define BANISH_FORMALDYHIDE "embalm" -#define BANISH_RUNES "runes" -#define BANISH_CANDLES "candles" -#define BANISH_DESTRUCTION "destruction" -#define BANISH_FUNERAL_GARB "funeral" - -#define LORE 1 -#define LAW 2 diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index aa521e47314..68234a9e0bb 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -139,6 +139,8 @@ #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" + /// if returned, forces nothing to happen when the atom is attacked by a blob + #define COMPONENT_CANCEL_BLOB_ACT (1<<0) ///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) @@ -387,6 +389,8 @@ #define COMSIG_MOB_LOGIN "mob_login" ///from base of /mob/Logout(): () #define COMSIG_MOB_LOGOUT "mob_logout" +///from base of /mob/mind_initialize +#define COMSIG_MOB_MIND_INITIALIZED "mob_mind_inited" ///from base of mob/death(): (gibbed) #define COMSIG_MOB_DEATH "mob_death" ///from base of mob/ghostize(): (mob/dead/observer/ghost) @@ -452,8 +456,10 @@ #define COMSIG_MOB_THROW "mob_throw" ///called when a user is getting new weapon and we want to remove previous weapon to clear hands #define COMSIG_MOB_WEAPON_APPEARS "mob_weapon_appears" -///from base of /mob/verb/examinate(): (atom/target) -#define COMSIG_MOB_EXAMINATE "mob_examinate" +/// from base of /mob/verb/examinate(): (atom/target) +#define COMSIG_MOB_VERB_EXAMINATE "mob_examinate" +/// from base of /mob/proc/run_examinate(): (atom/target, list/result) +#define COMSIG_MOB_RUN_EXAMINATE "mob_run_examinate" ///from base of /mob/update_sight(): () #define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" ////from /mob/living/say(): () @@ -470,6 +476,9 @@ ////from mob/living/adjust_fire_stacks() #define COMSIG_MOB_ADJUST_FIRE "mob_adjust_fire" +////from mob/living/adjust_wet_stacks() +#define COMSIG_MOB_ADJUST_WET "mob_adjust_wet" + ///from base of /mob/living/toggle_move_intent(): (old_move_intent) #define COMSIG_MOB_MOVE_INTENT_TOGGLE "mob_move_intent_toggle" #define COMPONENT_BLOCK_INTENT_TOGGLE (1<<0) @@ -508,6 +517,9 @@ /// Performed after the hands are swapped. #define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" +/// from mob/get_status_tab_items(): (list/items) +#define COMSIG_MOB_GET_STATUS_TAB_ITEMS "mob_get_status_tab_items" + ///From base of mob/update_movespeed():area #define COMSIG_MOB_MOVESPEED_UPDATED "mob_update_movespeed" @@ -520,6 +532,8 @@ #define COMSIG_CLIENT_SET_EYE "client_set_eye" // from /client/proc/change_view() : (new_size) #define COMSIG_VIEW_SET "view_set" +/// from /mob/proc/change_mob_type() : () +#define COMSIG_MOB_CHANGED_TYPE "mob_changed_type" // /mob/living signals @@ -527,6 +541,8 @@ #define COMSIG_LIVING_RESIST "living_resist" ///from base of mob/living/IgniteMob() (/mob/living) #define COMSIG_LIVING_IGNITED "living_ignite" +///from base of mob/living/WetMob() (/mob/living) +#define COMSIG_LIVING_WET "living_weted" ///from base of mob/living/ExtinguishMob() (/mob/living) #define COMSIG_LIVING_EXTINGUISHED "living_extinguished" ///from base of mob/living/electrocute_act(): (shock_damage, source, siemens_coeff, flags) @@ -537,6 +553,9 @@ #define COMSIG_LIVING_SHOCK_PREVENTED "living_shock_prevented" ///sent by stuff like stunbatons and tasers: () #define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" +/// Source: /mob/living/proc/flash_eyes(intensity, override_blindness_check, affect_silicon, visual, type) +#define COMSIG_LIVING_EARLY_FLASH_EYES "living_flash_eyes" + #define STOP_FLASHING_EYES (1<<0) ///from base of mob/living/revive() (full_heal, admin_revive) #define COMSIG_LIVING_REVIVE "living_revive" ///from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs) @@ -559,11 +578,15 @@ #define COMSIG_BORG_SAFE_DECONSTRUCT "borg_safe_decon" ///sent from living mobs every tick of fire #define COMSIG_LIVING_FIRE_TICK "living_fire_tick" +///sent from living mobs every tick of wet +#define COMSIG_LIVING_WET_TICK "living_wet_tick" //sent from living mobs when they are ahealed #define COMSIG_LIVING_AHEAL "living_aheal" ///From living/Life(). (deltatime, times_fired) #define COMSIG_LIVING_LIFE "living_life" ///from base of mob/living/death(): (gibbed) +#define COMSIG_LIVING_EARLY_DEATH "living_early_death" +///from base of mob/living/death(): (gibbed) #define COMSIG_LIVING_DEATH "living_death" //sent from mobs when they exit their body as a ghost #define COMSIG_LIVING_GHOSTIZED "ghostized" @@ -579,6 +602,14 @@ #define COMSIG_LIVING_RESTING "living_resting" ///from base of mob/update_transform() #define COMSIG_LIVING_POST_UPDATE_TRANSFORM "living_post_update_transform" +/// Source: /mob/living/proc/apply_status_effect(datum/status_effect/new_instance) +#define COMSIG_LIVING_GAINED_STATUS_EFFECT "living_gained_status_effect" +/// Source: /mob/living/proc/remove_status_effect(datum/status_effect/existing_effect) +#define COMSIG_LIVING_EARLY_LOST_STATUS_EFFECT "living_early_lost_status_effect" // Called before qdel +/// From mob/living/try_speak(): (message) +#define COMSIG_MOB_TRY_SPEECH "living_vocal_speech" + /// Return if the mob cannot speak. + #define COMPONENT_CANNOT_SPEAK (1<<0) ///called on /living when someone starts pulling (atom/movable/pulled, state, force) #define COMSIG_LIVING_START_PULL "living_start_pull" @@ -593,6 +624,8 @@ /// Called from /mob/living/PushAM -- Called when this mob is about to push a movable, but before it moves /// (aotm/movable/being_pushed) #define COMSIG_LIVING_PUSHING_MOVABLE "living_pushing_movable" +///from base of /mob/living/examine(): (mob/user, list/.) +#define COMSIG_LIVING_EXAMINE "living_examine" ///from base of mob/living/Stun() (amount, ignore_canstun) #define COMSIG_LIVING_STATUS_STUN "living_stun" @@ -618,11 +651,25 @@ #define COMSIG_LIVING_CAN_TRACK "mob_cantrack" #define COMPONENT_CANT_TRACK (1<<0) +/// Source: /mob/living/AdjustBlood(amount) +#define COMSIG_LIVING_BLOOD_ADJUST "living_blood_adjust" + #define COMPONENT_PREVENT_BLOODLOSS (1<<0) +/// Source: /mob/living/AdjustBlood(amount) +#define COMSIG_LIVING_BLOOD_ADJUSTED "living_blood_adjusted" +/// Source: /mob/living/setBlood(amount) +#define COMSIG_LIVING_EARLY_SET_BLOOD "living_early_set_blood" +/// Source: /mob/living/setBlood(amount) +#define COMSIG_LIVING_SET_BLOOD "living_set_blood" + /// From /mob/add_language() (language_name) #define COMSIG_MOB_LANGUAGE_ADD "mob_language_add" /// From /mob/remove_language() (language_name) #define COMSIG_MOB_LANGUAGE_REMOVE "mob_language_remove" +/// Source: /mob/living/say (message, verb, ignore_speech_problems, ignore_atmospherics, ignore_languages, datum/multilingual_say_piece) +#define COMSIG_LIVING_EARLY_SAY "living_early_say" + #define COMPONENT_PREVENT_SPEAKING (1<<0) + /// From base of /client/Move(): (new_loc, direction) #define COMSIG_MOB_CLIENT_PRE_MOVE "mob_client_pre_move" /// Should always match COMPONENT_MOVABLE_BLOCK_PRE_MOVE as these are interchangeable and used to block movement. @@ -642,6 +689,9 @@ /// from base of /client/proc/handle_popup_close() : (window_id) #define COMSIG_POPUP_CLEARED "popup_cleared" +/// Source: /mob/living/UnarmedAttack (atom/atom, proximity_flag) +#define COMSIG_LIVING_UNARMED_ATTACK "living_unarmed_attack" + // /mob/living/carbon signals ///from base of mob/living/carbon/soundbang_act(): (list(intensity)) @@ -678,7 +728,10 @@ #define COMSIG_CARBON_APPLY_OVERLAY "carbon_apply_overlay" ///Called from remove_overlay(cache_index, overlay) #define COMSIG_CARBON_REMOVE_OVERLAY "carbon_remove_overlay" - +#define COMSIG_CARBON_UPDATING_HEALTH_HUD "carbon_health_hud_update" +#define COMSIG_HUMAN_UPDATING_HEALTH_HUD "human_health_hud_update" + /// Return if you override the carbon's or human's health hud with something else + #define COMPONENT_OVERRIDE_HEALTH_HUD (1<<0) // /mob/living/simple_animal signals ///from /mob/living/attack_animal(): (mob/living/simple_animal/M) #define COMSIG_SIMPLE_ANIMAL_ATTACKEDBY "simple_animal_attackedby" @@ -688,6 +741,9 @@ #define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget" #define COMPONENT_HOSTILE_NO_ATTACK (1<<0) +///after attackingtarget has happened, source is the attacker and target is the attacked, extra argument for if the attackingtarget was successful +#define COMSIG_HOSTILE_POST_ATTACKINGTARGET "hostile_post_attackingtarget" + /// Called when a /mob/living/simple_animal/hostile fines a new target: (atom/source, give_target) #define COMSIG_HOSTILE_FOUND_TARGET "comsig_hostile_found_target" @@ -713,6 +769,10 @@ #define COMSIG_OBJ_POSSESSED "obj_possessed" ///from base of /proc/release(): (mob/user) #define COMSIG_OBJ_RELEASED "obj_released" +///from [/obj/structure/sink/attack_hand] +#define COMSIG_SINK_ACT "sink_act" + /// returns on succes of species special sink_act() + #define COMSIG_SINK_ACT_SUCCESS (1<<0) // /obj/machinery signals @@ -778,6 +838,9 @@ #define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/item/organ/internal/remove]: #define COMSIG_ORGAN_REMOVED "organ_removed" +///from [/obj/item/organ/internal/cyberimp/mouth/translator/check_lang] +#define COMSIG_LANG_PRE_ACT "check_language" + #define COMSIG_LANG_SECURED (1<<0) /// Defib-specific signals @@ -910,12 +973,15 @@ #define COMSIG_HUMAN_REGENERATE_ICONS "human_regenerate_icons" ///From /mob/living/carbon/human/proc/set_species(): (datum/species/old_species) #define COMSIG_HUMAN_SPECIES_CHANGED "human_species_changed" - +/// Source: /mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) +#define COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT "human_early_handle_environment" ///from /mob/living/carbon/human/proc/check_shields(): (atom/hit_by, damage, attack_text, attack_type, armour_penetration, damage_type) #define COMSIG_HUMAN_CHECK_SHIELDS "human_check_shields" #define SHIELD_BLOCK (1<<0) +#define COMSIG_HUMAN_DESTROYED "human_destroyed" + // /datum/species signals ///from datum/species/on_species_gain(): (datum/species/new_species, datum/species/old_species) #define COMSIG_SPECIES_GAIN "species_gain" @@ -949,6 +1015,11 @@ ///from base of obj/item/reagent_containers/food/snacks/attack(): (mob/living/eater, mob/feeder) #define COMSIG_FOOD_EATEN "food_eaten" +/// Reagents +/// Source: /datum/reagents/proc/add_reagent (datum/reagents, reagent_id, amount, data, reagtemp, no_react, chem_temp) +#define COMSIG_EARLY_REAGENT_ADDED "reagent_early_added" + #define COMPONENT_PREVENT_ADD_REAGENT (1<<0) + //Gibs ///from base of /obj/effect/decal/cleanable/blood/gibs/streak(): (list/directions, list/diseases) @@ -1133,6 +1204,9 @@ ///from base of [/datum/element/light_eater/proc/devour]: (atom/eaten_light) #define COMSIG_LIGHT_EATER_DEVOUR "light_eater_devour" +/// datum/element/reagent_attack +/// Source: /datum/element/reagent_attack/proc/inject (datum/element/reagent_attack, mob/living/carbon/target, reagent_id, reagent_amount, target_zone) +#define COMSIG_REAGENT_INJECTED "reagent_inject" // /datum/element/movetype_handler signals /// Called when the floating anim has to be temporarily stopped and restarted later: (timer) @@ -1200,3 +1274,11 @@ /// Source: /mob/living/simple_animal/borer, listening in datum/antagonist/borer #define COMSIG_BORER_ENTERED_HOST "borer_on_enter" // when borer entered host #define COMSIG_BORER_LEFT_HOST "borer_on_leave" // when borer left host + +///from /datum/spawners_menu/ui_act(): (mob/user) +#define COMSIG_IS_GHOST_CONTROLABLE "is_ghost_controllable" + /// Return this to signal that the mob can be controlled by ghosts + #define COMPONENT_GHOST_CONTROLABLE (1<<0) + +/// Source: /proc/random_hair_style (mob/living/carbon/human/human, valid_hairstyles, robohead) +#define COMSIG_RANDOM_HAIR_STYLE "random_hair_style" diff --git a/code/__DEFINES/dcs/signals_blob.dm b/code/__DEFINES/dcs/signals_blob.dm new file mode 100644 index 00000000000..3fd9a3ec608 --- /dev/null +++ b/code/__DEFINES/dcs/signals_blob.dm @@ -0,0 +1,9 @@ +/// Signal sent when a blob overmind picked a new strain (/mob/camera/blob/overmind, /datum/blobstrain/new_strain) +#define COMSIG_BLOB_SELECTED_STRAIN "blob_selected_strain" +/// Signal sent by a blob spore when it creates a zombie (/mob/living/basic/blob_minion/spore/spore, //mob/living/basic/blob_minion/zombie/zombie) +#define COMSIG_BLOB_ZOMBIFIED "blob_zombified" + +/// Signal sent by a blob when it try expand +#define COMSIG_TRY_CONSUME_TURF "try_consume_turf" + /// Component blocks consuming + #define COMPONENT_CANT_CONSUME (1<<0) diff --git a/code/__DEFINES/dcs/signals_object.dm b/code/__DEFINES/dcs/signals_object.dm index ce5845f447c..90cbb8b0150 100644 --- a/code/__DEFINES/dcs/signals_object.dm +++ b/code/__DEFINES/dcs/signals_object.dm @@ -16,7 +16,19 @@ /// Return to prevent the default behavior (attack_selfing) from ocurring. #define COMPONENT_ITEM_ACTION_SLOT_INVALID (1<<0) -/// from base of /obj/item/slimepotion/speed/interact_with_atom(): (obj/target, /obj/src, mob/user) #define COMSIG_SPEED_POTION_APPLIED "speed_potion" #define SPEED_POTION_STOP (1<<0) + +///from base of [/obj/proc/update_integrity]: (old_value, new_value) +#define COMSIG_OBJ_INTEGRITY_CHANGED "obj_integrity_changed" + + +///sent to targets during the process_hit proc of projectiles +#define COMSIG_FIRE_CASING "fire_casing" + +///called in /obj/item/grenade/proc/prime(): (user) +#define COMSIG_GRENADE_DETONATE "grenade_prime" + +///from [/obj/structure/closet/supplypod/proc/preOpen]: +#define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom" diff --git a/code/__DEFINES/devil.dm b/code/__DEFINES/devil.dm new file mode 100644 index 00000000000..f58afda46b9 --- /dev/null +++ b/code/__DEFINES/devil.dm @@ -0,0 +1,87 @@ +GLOBAL_LIST_INIT(whiteness, list( + /obj/item/clothing/under/color/white = 2, + /obj/item/clothing/under/rank/bartender = 1, + /obj/item/clothing/under/rank/chef = 1, + /obj/item/clothing/under/rank/chief_engineer = 1, + /obj/item/clothing/under/rank/scientist = 1, + /obj/item/clothing/under/rank/chemist = 1, + /obj/item/clothing/under/rank/chief_medical_officer = 1, + /obj/item/clothing/under/rank/geneticist = 1, + /obj/item/clothing/under/rank/virologist = 1, + /obj/item/clothing/under/rank/nursesuit = 1, + /obj/item/clothing/under/rank/medical = 1, + /obj/item/clothing/under/rank/psych = 1, + /obj/item/clothing/under/rank/orderly = 1, + /obj/item/clothing/under/rank/security/brigphys = 1, + /obj/item/clothing/under/rank/internalaffairs = 1, + /obj/item/clothing/under/rank/ntrep = 1, + /obj/item/clothing/under/det = 1, + /obj/item/clothing/under/wedding/bride_white = 1, + /obj/item/clothing/under/mafia/white = 1, + /obj/item/clothing/under/noble_clothes = 1, + /obj/item/clothing/under/sl_suit = 1, + /obj/item/clothing/under/burial = 1 +)) + +#define ENRAGED_THRESHOLD 4 +#define BLOOD_THRESHOLD 7 +#define TRUE_THRESHOLD 10 + +#define BASIC_DEVIL_REGEN_THRESHOLD 10 SECONDS +#define ENRAGED_DEVIL_REGEN_THRESHOLD 10 SECONDS +#define BLOOD_LIZARD_REGEN_THRESHOLD 5 SECONDS +#define TRUE_DEVIL_REGEN_THRESHOLD 3 SECONDS + +#define BASIC_DEVIL_REGEN_AMOUNT 20 +#define ENRAGED_DEVIL_REGEN_AMOUNT 40 +#define BLOOD_LIZARD_REGEN_AMOUNT 60 +#define TRUE_DEVIL_REGEN_AMOUNT 80 + +#define BASIC_DEVIL_RANK /datum/devil_rank/basic_devil +#define ENRAGED_DEVIL_RANK /datum/devil_rank/enraged_devil +#define BLOOD_LIZARD_RANK /datum/devil_rank/blood_lizard +#define TRUE_DEVIL_RANK /datum/devil_rank/true_devil + +#define BANE_SALT "salt" +#define BANE_LIGHT "light" +#define BANE_IRON "iron" +#define BANE_WHITECLOTHES "whiteclothes" +#define BANE_SILVER "silver" +#define BANE_HARVEST "harvest" +#define BANE_TOOLBOX "toolbox" + +#define OBLIGATION_FOOD "food" +#define OBLIGATION_FIDDLE "fiddle" +#define OBLIGATION_DANCEOFF "danceoff" +#define OBLIGATION_GREET "greet" +#define OBLIGATION_PRESENCEKNOWN "presenceknown" +#define OBLIGATION_SAYNAME "sayname" +#define OBLIGATION_ANNOUNCEKILL "announcekill" +#define OBLIGATION_ANSWERTONAME "answername" + +#define BAN_HURTWOMAN "hurtwoman" +#define BAN_HURTMAN "hurtman" +#define BAN_CHAPEL "chapel" +#define BAN_HURTPRIEST "hurtpriest" +#define BAN_AVOIDWATER "avoidwater" +#define BAN_STRIKEUNCONCIOUS "strikeunconcious" +#define BAN_HURTLIZARD "hurtlizard" +#define BAN_HURTANIMAL "hurtanimal" + +#define BANISH_WATER "water" +#define BANISH_COFFIN "coffin" +#define BANISH_FORMALDYHIDE "embalm" +#define BANISH_RUNES "runes" +#define BANISH_CANDLES "candles" +#define BANISH_DESTRUCTION "destruction" +#define BANISH_FUNERAL_GARB "funeral" + +#define BANE_TOOLBOX_DAMAGE_MODIFIER 2.5 +#define BANE_HARVEST_DAMAGE_MULTIPLIER 2 + +GLOBAL_LIST_EMPTY(allDevils) +//These are also used in the codex gigas, so let's declare them globally. +GLOBAL_LIST_INIT(devil_pre_title, list("Dark ", "Hellish ", "Fallen ", "Fiery ", "Sinful ", "Blood ", "Fluffy ")) +GLOBAL_LIST_INIT(devil_title, list("Lord ", "Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept ")) +GLOBAL_LIST_INIT(devil_syllable, list("hal", "ve", "odr", "neit", "ci", "quon", "mya", "folth", "wren", "geyr", "hil", "niet", "twou", "phi", "coa")) +GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.")) diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 756ab49803e..4ddca05d5c2 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -37,6 +37,10 @@ /// Update the atom's icon #define UPDATE_ICON (UPDATE_ICON_STATE|UPDATE_OVERLAYS) +/// If the thing can reflect light (lasers/energy) +#define RICOCHET_SHINY (1<<0) +/// If the thing can reflect matter (bullets/bomb shrapnel) +#define RICOCHET_HARD (1<<1) //Reagent flags #define REAGENT_NOREACT 1 @@ -145,6 +149,8 @@ #define MOB_SPAWN_ALLOWED (1<<3) /// If megafauna can be spawned by natural random generation #define MEGAFAUNA_SPAWN_ALLOWED (1<<4) +/// If blobs can spawn there and if it counts towards their score. +#define BLOBS_ALLOWED (1<<5) //ORGAN TYPE FLAGS #define AFFECT_ROBOTIC_ORGAN 1 @@ -204,6 +210,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204 #define MOBILITY_FLAGS_CARBON_DEFAULT (MOBILITY_MOVE|MOBILITY_STAND|MOBILITY_PICKUP|MOBILITY_USE|MOBILITY_UI|MOBILITY_STORAGE|MOBILITY_PULL|MOBILITY_REST|MOBILITY_LIEDOWN) #define MOBILITY_FLAGS_REST_CAPABLE_DEFAULT (MOBILITY_MOVE|MOBILITY_STAND|MOBILITY_PICKUP|MOBILITY_USE|MOBILITY_UI|MOBILITY_STORAGE|MOBILITY_PULL|MOBILITY_REST|MOBILITY_LIEDOWN) + // timed_action_flags parameter for [/proc/do_after()] /// Can do the action even if mob moves location. #define DA_IGNORE_USER_LOC_CHANGE (1<<0) diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 33958d237c2..121121aa158 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -25,49 +25,52 @@ #define GAMEMODE_IS_WIZARD (SSticker && istype(SSticker.mode, /datum/game_mode/wizard)) #define GAMEMODE_IS_RAGIN_MAGES (SSticker && istype(SSticker.mode, /datum/game_mode/wizard/raginmages)) -//special roles +// special roles // Distinct from the ROLE_X defines because some antags have multiple special roles but only one ban type -#define SPECIAL_ROLE_ABDUCTOR_AGENT "Abductor Agent" -#define SPECIAL_ROLE_ABDUCTOR_SCIENTIST "Abductor Scientist" -#define SPECIAL_ROLE_BLOB "Blob" -#define SPECIAL_ROLE_BLOB_OVERMIND "Blob Overmind" -#define SPECIAL_ROLE_BORER "Borer" -#define SPECIAL_ROLE_CARP "Space Carp" -#define SPECIAL_ROLE_CHANGELING "Changeling" -#define SPECIAL_ROLE_CULTIST "Cultist" -#define SPECIAL_ROLE_CLOCKER "Clockwork cultist" -#define SPECIAL_ROLE_DEATHSQUAD "Death Commando" -#define SPECIAL_ROLE_ERT "Response Team" -#define SPECIAL_ROLE_FREE_GOLEM "Free Golem" -#define SPECIAL_ROLE_GOLEM "Golem" -#define SPECIAL_ROLE_HEAD_REV "Head Revolutionary" -#define SPECIAL_ROLE_HEADSLUG "HeadSlug" -#define SPECIAL_ROLE_HONKSQUAD "Honksquad" -#define SPECIAL_ROLE_REV "Revolutionary" -#define SPECIAL_ROLE_MORPH "Morph" -#define SPECIAL_ROLE_MULTIVERSE "Multiverse Traveller" -#define SPECIAL_ROLE_NUKEOPS "Syndicate" -#define SPECIAL_ROLE_PYROCLASTIC_SLIME "Pyroclastic Anomaly Slime" -#define SPECIAL_ROLE_RAIDER "Vox Raider" -#define SPECIAL_ROLE_REVENANT "Revenant" -#define SPECIAL_ROLE_SHADOWLING "Shadowling" -#define SPECIAL_ROLE_SHADOWLING_THRALL "Shadowling Thrall" -#define SPECIAL_ROLE_DEMON "Demon" -#define SPECIAL_ROLE_SUPER "Super" -#define SPECIAL_ROLE_SYNDICATE_DEATHSQUAD "Syndicate Commando" -#define SPECIAL_ROLE_TRAITOR "Traitor" -#define SPECIAL_ROLE_VAMPIRE "Vampire" -#define SPECIAL_ROLE_VAMPIRE_THRALL "Vampire Thrall" -#define SPECIAL_ROLE_WIZARD "Wizard" -#define SPECIAL_ROLE_WIZARD_APPRENTICE "Wizard Apprentice" -#define SPECIAL_ROLE_XENOMORPH "Xenomorph" -#define SPECIAL_ROLE_XENOMORPH_QUEEN "Xenomorph Queen" -#define SPECIAL_ROLE_XENOMORPH_HUNTER "Xenomorph Hunter" -#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone" -#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel" -#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva" -#define SPECIAL_ROLE_SPACE_NINJA "Space Ninja" -#define SPECIAL_ROLE_THIEF "Thief" -#define SPECIAL_ROLE_SPACE_DRAGON "Space Dragon" -#define SPECIAL_ROLE_EVENTMISC "Event Role" -#define SPECIAL_ROLE_MALFAI "Malfunctioning AI" +#define SPECIAL_ROLE_ABDUCTOR_AGENT "Abductor Agent" +#define SPECIAL_ROLE_ABDUCTOR_SCIENTIST "Abductor Scientist" +#define SPECIAL_ROLE_BLOB "Blob" +#define SPECIAL_ROLE_BLOB_OVERMIND "Blob Overmind" +#define SPECIAL_ROLE_BLOB_MINION "Blob Minion" +#define SPECIAL_ROLE_BORER "Borer" +#define SPECIAL_ROLE_CARP "Space Carp" +#define SPECIAL_ROLE_CHANGELING "Changeling" +#define SPECIAL_ROLE_CULTIST "Cultist" +#define SPECIAL_ROLE_CLOCKER "Clockwork cultist" +#define SPECIAL_ROLE_DEATHSQUAD "Death Commando" +#define SPECIAL_ROLE_ERT "Response Team" +#define SPECIAL_ROLE_FREE_GOLEM "Free Golem" +#define SPECIAL_ROLE_GOLEM "Golem" +#define SPECIAL_ROLE_HEAD_REV "Head Revolutionary" +#define SPECIAL_ROLE_HEADSLUG "HeadSlug" +#define SPECIAL_ROLE_HONKSQUAD "Honksquad" +#define SPECIAL_ROLE_REV "Revolutionary" +#define SPECIAL_ROLE_MORPH "Morph" +#define SPECIAL_ROLE_MULTIVERSE "Multiverse Traveller" +#define SPECIAL_ROLE_NUKEOPS "Syndicate" +#define SPECIAL_ROLE_PYROCLASTIC_SLIME "Pyroclastic Anomaly Slime" +#define SPECIAL_ROLE_RAIDER "Vox Raider" +#define SPECIAL_ROLE_REVENANT "Revenant" +#define SPECIAL_ROLE_SHADOWLING "Shadowling" +#define SPECIAL_ROLE_SHADOWLING_THRALL "Shadowling Thrall" +#define SPECIAL_ROLE_DEMON "Demon" +#define SPECIAL_ROLE_SUPER "Super" +#define SPECIAL_ROLE_SYNDICATE_DEATHSQUAD "Syndicate Commando" +#define SPECIAL_ROLE_TRAITOR "Traitor" +#define SPECIAL_ROLE_VAMPIRE "Vampire" +#define SPECIAL_ROLE_VAMPIRE_THRALL "Vampire Thrall" +#define SPECIAL_ROLE_WIZARD "Wizard" +#define SPECIAL_ROLE_WIZARD_APPRENTICE "Wizard Apprentice" +#define SPECIAL_ROLE_XENOMORPH "Xenomorph" +#define SPECIAL_ROLE_XENOMORPH_QUEEN "Xenomorph Queen" +#define SPECIAL_ROLE_XENOMORPH_HUNTER "Xenomorph Hunter" +#define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone" +#define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel" +#define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva" +#define SPECIAL_ROLE_SPACE_NINJA "Space Ninja" +#define SPECIAL_ROLE_THIEF "Thief" +#define SPECIAL_ROLE_SPACE_DRAGON "Space Dragon" +#define SPECIAL_ROLE_EVENTMISC "Event Role" +#define SPECIAL_ROLE_MALFAI "Malfunctioning AI" +#define SPECIAL_ROLE_SINTOUCHED "Sintouched" +#define SPECIAL_ROLE_DEVIL_PAWN "Devil's pawn" diff --git a/code/__DEFINES/generators.dm b/code/__DEFINES/generators.dm new file mode 100644 index 00000000000..3ad34d39f2c --- /dev/null +++ b/code/__DEFINES/generators.dm @@ -0,0 +1,12 @@ +//generator types +#define GEN_NUM "num" +#define GEN_VECTOR "vector" +#define GEN_BOX "box" +#define GEN_CIRCLE "circle" +#define GEN_SPHERE "sphere" + +///particle editor var modifiers +#define P_DATA_GENERATOR "generator" +#define P_DATA_ICON_ADD "icon_add" +#define P_DATA_ICON_REMOVE "icon_remove" +#define P_DATA_ICON_WEIGHT "icon_edit" diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index 84bbaf40e6e..5bd97e6aabe 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -25,7 +25,8 @@ #define DISABILITY_FLAG_TEA_ADDICT (1<<13) #define DISABILITY_FLAG_ALCOHOLE_ADDICT (1<<14) #define DISABILITY_FLAG_NICOTINE_ADDICT (1<<15) -#define DISABILITY_FLAG_PARAPLEGIA (1<<16) +#define DISABILITY_FLAG_PARAPLEGIA (1<<16) +#define DISABILITY_FLAG_APHASIA (1<<17) //Nutrition levels for humans. No idea where else to put it diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm index 8a69bac57f7..7ee61dccdfb 100644 --- a/code/__DEFINES/hud.dm +++ b/code/__DEFINES/hud.dm @@ -97,3 +97,6 @@ #define PLANE_GROUP_MAIN "main" /// A secondary group, used when a client views a generic window #define PLANE_GROUP_POPUP_WINDOW(screen) "popup-[screen.UID()]" + +//Blobbernauts +#define ui_blobbernaut_overmind_health "EAST-1:28,CENTER+0:19" diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index 404e38e8039..1aad276d335 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -29,17 +29,6 @@ #define SMOOTH_DIAGONAL (1 << 12) //if atom should smooth diagonally, this should be present in 'smooth' var -DEFINE_BITFIELD(smoothing_flags, list( - "SMOOTH_CORNERS" = SMOOTH_CORNERS, - "SMOOTH_BITMASK" = SMOOTH_BITMASK, - "SMOOTH_DIAGONAL_CORNERS" = SMOOTH_DIAGONAL_CORNERS, - "SMOOTH_BORDER" = SMOOTH_BORDER, - "SMOOTH_QUEUED" = SMOOTH_QUEUED, - "SMOOTH_OBJ" = SMOOTH_OBJ, - "SMOOTH_BORDER_OBJECT" = SMOOTH_BORDER_OBJECT, - "SMOOTH_BROKEN_TURF" = SMOOTH_BROKEN_TURF, - "SMOOTH_BURNT_TURF" = SMOOTH_BURNT_TURF, -)) /// Components of a smoothing junction /// Redefinitions of the diagonal directions so they can be stored in one var without conflicts @@ -52,17 +41,6 @@ DEFINE_BITFIELD(smoothing_flags, list( #define SOUTHWEST_JUNCTION (1<<6) #define NORTHWEST_JUNCTION (1<<7) -DEFINE_BITFIELD(smoothing_junction, list( - "NORTH_JUNCTION" = NORTH_JUNCTION, - "SOUTH_JUNCTION" = SOUTH_JUNCTION, - "EAST_JUNCTION" = EAST_JUNCTION, - "WEST_JUNCTION" = WEST_JUNCTION, - "NORTHEAST_JUNCTION" = NORTHEAST_JUNCTION, - "SOUTHEAST_JUNCTION" = SOUTHEAST_JUNCTION, - "SOUTHWEST_JUNCTION" = SOUTHWEST_JUNCTION, - "NORTHWEST_JUNCTION" = NORTHWEST_JUNCTION, -)) - /*smoothing macros*/ #define QUEUE_SMOOTH(thing_to_queue) if(thing_to_queue.smooth & (SMOOTH_CORNERS|SMOOTH_BITMASK)) {SSicon_smooth.add_to_queue(thing_to_queue)} diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 65a3903b03f..0f780e66738 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -56,6 +56,12 @@ #define isstorage(A) (istype(A, /obj/item/storage)) +#define isgrenade(A) (istype(A, /obj/item/grenade)) + +#define issupplypod(A) (istype(A, /obj/structure/closet/supplypod)) + +#define isammocasing(A) (istype(A, /obj/item/ammo_casing)) + #define ismachinery(A) (istype(A, /obj/machinery)) #define isapc(A) (istype(A, /obj/machinery/power/apc)) diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm index ce3fa060264..6687e3eac35 100644 --- a/code/__DEFINES/job.dm +++ b/code/__DEFINES/job.dm @@ -60,7 +60,6 @@ #define JOB_FLAG_REPRESENTATIVE (1<<0) #define JOB_FLAG_BLUESHIELD (1<<1) -#define JOB_FLAG_BARBER (1<<3) #define JOB_FLAG_MECHANIC (1<<4) #define JOB_FLAG_BRIGDOC (1<<5) #define JOB_FLAG_JUDGE (1<<6) @@ -120,7 +119,6 @@ #define JOB_TITLE_MIME "Mime" #define JOB_TITLE_JANITOR "Janitor" #define JOB_TITLE_LIBRARIAN "Librarian" -#define JOB_TITLE_BARBER "Barber" #define JOB_TITLE_EXPLORER "Explorer" #define JOB_TITLE_SYNDICATE "Syndicate Officer" diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index ab01afc8fd6..eb59543fdb0 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -87,6 +87,17 @@ #define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 //For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell. #define LIGHTING_PLANE_ALPHA_INVISIBLE 0 +#define standartize_alpha(__alpha) (__alpha / LIGHTING_PLANE_ALPHA_VISIBLE) + +#define ALPHA_SOURCE_DEFAULT "default" +#define ALPHA_SOURCE_CHAMELEON "chameleon_gene" +#define ALPHA_SOURCE_SHADOW_CLOAK "shadow_cloak_gene" +#define ALPHA_SOURCE_VAMPIRE "vampire" +#define ALPHA_SOURCE_SHADOW_THRALL "shadowling_thrall" +#define ALPHA_SOURCE_SHADOWLING "shadowling" +#define ALPHA_SOURCE_NINJA "ninja" +#define ALPHA_SOURCE_CLOCKROBE "clockrobe" + //code assumes higher numbers override lower numbers. #define LIGHTING_NO_UPDATE 0 diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm index f1322a4b7d3..95a7f556d9c 100644 --- a/code/__DEFINES/math.dm +++ b/code/__DEFINES/math.dm @@ -34,6 +34,9 @@ // Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive #define WRAP(val, min, max) clamp(( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) ),min,max) +/// Increments a value and wraps it if it exceeds some value. Can be used to circularly iterate through a list through `idx = WRAP_UP(idx, length_of_list)`. +#define WRAP_UP(val, max) (((val) % (max)) + 1) + // Real modulus that handles decimals #define MODULUS(x, y) ( (x) - FLOOR(x, y)) @@ -119,3 +122,6 @@ /// Like SPT_PROB_RATE but easier to use, simply put `if(SPT_PROB(10, 5))` #define SPT_PROB(prob_per_second_percent, seconds_per_tick) (prob(100*SPT_PROB_RATE((prob_per_second_percent)/100, (seconds_per_tick)))) // ) + +/// The number of cells in a taxicab circle (rasterized diamond) of radius X. +#define DIAMOND_AREA(X) (1 + 2*(X)*((X)+1)) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 7ced20b795e..e8aba89f51a 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -503,3 +503,5 @@ #define MECH_TYPE_LOCKER (1<<9) #define MECH_TYPE_MARAUDER (1<<10) #define MECH_TYPE_SIDEWINTER (1<<11) +#define MECH_TYPE_OLD_DURAND (1<<12) +#define MECH_TYPE_DARK_GYGAX (1<<13) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 089ea970b09..f7d286e9db8 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -61,6 +61,8 @@ ////////REAGENT STUFF//////// // How many units of reagent are consumed per tick, by default. #define REAGENTS_METABOLISM 0.4 +#define REAGENTS_EFFECT_MULTIPLIER (REAGENTS_METABOLISM / 0.4) // By defining the effect multiplier this way, it'll exactly adjust all effects according to how they originally were with the 0.4 metabolism +#define REM REAGENTS_EFFECT_MULTIPLIER //! Shorthand for the above define for ease of use in equations and the like // Factor of how fast mob nutrition decreases #define HUNGER_FACTOR 0.1 @@ -318,6 +320,9 @@ #define isAIEye(A) (istype((A), /mob/camera/aiEye)) #define isovermind(A) (istype((A), /mob/camera/blob)) +#define isminion(A) (istype((A), /mob/living/simple_animal/hostile/blob_minion)) +#define isblobbernaut(M) istype((M), /mob/living/simple_animal/hostile/blob_minion/blobbernaut) + #define isSpirit(A) (istype((A), /mob/spirit)) #define ismask(A) (istype((A), /mob/spirit/mask)) @@ -441,6 +446,7 @@ /// Makes the weaken into a knockdown #define SHOCK_KNOCKDOWN (1<<7) + /// Vomit defines #define VOMIT_NUTRITION_LOSS 10 #define VOMIT_STUN_TIME (8 SECONDS) @@ -455,3 +461,6 @@ /// For babylon fever disease. #define DISEASE_MOB_LANGUAGE_PROCESSED (1<<0) + +/// Eyes examine time mod +#define EXAMINE_INSTANT 0 // 0 seconds diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index dd64f3f88af..5e2e651c8ce 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -16,6 +16,8 @@ #define NODECONSTRUCT (1<<5) /// Objects will ignore item attacks #define IGNORE_HITS (1<<6) +/// Objects will ignore blob_act +#define IGNORE_BLOB_ACT (1<<7) // Flags for the item_flags var on /obj/item @@ -73,3 +75,6 @@ /// Checks for finger coverage, prevents damage from nettles #define FINGERS_COVERED (1<<6) +/// Flags for the pod_flags var on /obj/structure/closet/supplypod +#define FIRST_SOUNDS (1<<0) // If it shouldn't play sounds the first time it lands, used for reverse mode + diff --git a/code/__DEFINES/organ_defines.dm b/code/__DEFINES/organ_defines.dm index 34e97a90c74..6ff85f2db7b 100644 --- a/code/__DEFINES/organ_defines.dm +++ b/code/__DEFINES/organ_defines.dm @@ -33,6 +33,7 @@ #define INTERNAL_ORGAN_EYE_SHIELD_DEVICE "eye_shield" #define INTERNAL_ORGAN_EYE_LING "eye_ling" #define INTERNAL_ORGAN_BREATHING_TUBE "breathing_tube" +#define INTERNAL_ORGAN_SPEECH_TRANSLATOR "voice_translator" #define INTERNAL_ORGAN_STOMACH "stomach" #define INTERNAL_ORGAN_HEART_DRIVE "heartdrive" #define INTERNAL_ORGAN_BRAIN_ANTIDROP "brain_antidrop" @@ -59,3 +60,6 @@ /// used for species that can see without eyes #define NO_VISION_ORGAN "no_vision_organ" +/// Species organs +#define DRASK_LUNGS_COOLING_START_TEMP 280 +#define DRASK_LUNGS_COOLING_STOP_TEMP 400 diff --git a/code/__DEFINES/particles.dm b/code/__DEFINES/particles.dm new file mode 100644 index 00000000000..5657566a63b --- /dev/null +++ b/code/__DEFINES/particles.dm @@ -0,0 +1,5 @@ +// /obj/effect/abstract/particle_holder/var/particle_flags +// Flags that effect how a particle holder displays something + +/// If we're inside something inside a mob, display off that mob too +#define PARTICLE_ATTACH_MOB (1<<0) diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 0285fecd7bf..a0429a5cb97 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(special_roles, list( ROLE_CULTIST = /datum/game_mode/cult, // Cultist ROLE_CLOCKER = /datum/game_mode/clockwork, // Clockwork Cultist ROLE_DEMON, // Demons (Slaughter/Laughter/Shadow) - ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil + ROLE_DEVIL, // Devil ROLE_GSPIDER, // Giant spider ROLE_GUARDIAN, // Guardian ROLE_ELITE, // Lavaland Elite diff --git a/code/__DEFINES/ru_lang_rules.dm b/code/__DEFINES/ru_lang_rules.dm new file mode 100644 index 00000000000..9a1d2946455 --- /dev/null +++ b/code/__DEFINES/ru_lang_rules.dm @@ -0,0 +1,7 @@ +// Падежи русского языка +#define NOMINATIVE 1 // Именительный: кто это? Клоун и ассистуха +#define GENITIVE 2 // Родительный: откусить кусок от кого? От клоуна и ассистухи +#define DATIVE 3 // Дательный: дать полный доступ кому? Клоуну и ассистухе +#define ACCUSATIVE 4 // Винительный: обвинить кого? Клоуна и ассистуху +#define INSTRUMENTAL 5 // Творительный: возить по полу кем? Клоуном и ассистухой +#define PREPOSITIONAL 6 // Предложный: прохладная история о ком? О клоуне и об ассистухе diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm new file mode 100644 index 00000000000..990108e1550 --- /dev/null +++ b/code/__DEFINES/say.dm @@ -0,0 +1,4 @@ +// A link given to ghost alice to follow bob +#define FOLLOW_LINK(alice, bob) "(F)" +#define TURF_LINK(alice, turfy) "(T)" +#define FOLLOW_OR_TURF_LINK(alice, bob, turfy) "(F)" diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm index 5c13748e141..0e40365ee2b 100644 --- a/code/__DEFINES/span.dm +++ b/code/__DEFINES/span.dm @@ -18,6 +18,7 @@ #define span_alien(str) ("" + str + "") #define span_announce(str) ("" + str + "") #define span_big(str) ("" + str + "") +#define span_blob(str) ("" + str + "") //#define span_bigicon(str) ("" + str + "") //#define span_binarysay(str) ("" + str + "") //#define span_blue(str) ("" + str + "") diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index b48227307a6..c22533ce055 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -30,6 +30,8 @@ #define STATUS_EFFECT_REGENERATIVE_CORE /datum/status_effect/regenerative_core +#define STATUS_EFFECT_DRASK_COMA /datum/status_effect/drask_coma + #define STATUS_EFFECT_TERROR_REGEN /datum/status_effect/terror/regeneration //over time healing, 125 HP within 25~ seconds #define STATUS_EFFECT_TERROR_FOOD_REGEN /datum/status_effect/terror/food_regen //over time healing for mobs to gain full HP within 25~ seconds diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 5cb1e4ae138..2e06c7645f1 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -130,6 +130,7 @@ #define FIRE_PRIORITY_BURNING 40 #define FIRE_PRIORITY_DEFAULT 50 #define FIRE_PRIORITY_PARALLAX 65 +#define FIRE_PRIORITY_FLUIDS 80 #define FIRE_PRIORITY_MOBS 100 #define FIRE_PRIORITY_ASSETS 105 #define FIRE_PRIORITY_TGUI 110 diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm index 4e9b6571a4e..1cb58f9f41a 100644 --- a/code/__DEFINES/tools.dm +++ b/code/__DEFINES/tools.dm @@ -5,6 +5,7 @@ #define TOOL_WIRECUTTER "wirecutter" #define TOOL_WRENCH "wrench" #define TOOL_WELDER "welder" +#define TOOL_ANALYZER "analyzer" // Surgery tools #define TOOL_RETRACTOR "retractor" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index f6eefaa8686..2f24d20b028 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -26,6 +26,11 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_CHASM_STOPPER "chasm_stopper" /// `do_teleport` will not allow this atom to teleport #define TRAIT_NO_TELEPORT "no-teleport" + +/// This atom is a secluded location, which is counted as out of bounds. +/// Anything that enters this atom's contents should react if it wants to stay in bounds. +#define TRAIT_SECLUDED_LOCATION "secluded_loc" + #define TRAIT_SILENT_FOOTSTEPS "silent_footsteps" //turf traits @@ -43,6 +48,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai //mob traits #define TRAIT_GODMODE "godmode" #define TRAIT_PACIFISM "pacifism" +#define TRAIT_NO_DEATH "nodeath" #define TRAIT_WATERBREATH "waterbreathing" #define TRAIT_BLOODCRAWL "bloodcrawl" #define TRAIT_BLOODCRAWL_EAT "bloodcrawl_eat" @@ -60,6 +66,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SHOCKIMMUNE "shock_immunity" /// Are we immune to specifically tesla / SM shocks? #define TRAIT_TESLA_SHOCKIMMUNE "tesla_shock_immunity" +/// Are we immune to wet effect +#define TRAIT_WET_IMMUNITY "wet_immunity" /// We place people into a fireman carry quicker than standard #define TRAIT_QUICK_CARRY "quick-carry" @@ -85,6 +93,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_HUSK "husk" #define TRAIT_SKELETON "skeleton" #define TRAIT_NO_CLONE "no_clone" +/// Isn't attacked harmfully by blob structures +#define TRAIT_BLOB_ALLY "blob_ally" +/// Objects with this trait are deleted if they fall into chasms, rather than entering abstract storage +#define TRAIT_CHASM_DESTROYED "chasm_destroyed" /// "Magic" trait that blocks the mob from moving or interacting with anything. Used for transient stuff like mob transformations or incorporality in special cases. /// Will block movement, `Life()` (!!!), and other stuff based on the mob. @@ -177,6 +189,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_ADVANCED_SURGICAL "advanced_surgical" /// This trait makes it so that an item literally cannot be removed at all, or at least that's how it should be. Only deleted. #define TRAIT_NODROP "nodrop" +/// Applied with attachment to the cyberimplant when it is inserted in mob with TRAIT_ADVANCED_CYBERIMPLANTS +#define TRAIT_CYBERIMP_IMPROVED "cyberimp_improved" + +#define TRAIT_SHRAPNEL "shrapnel" ///Movement type traits for movables. See elements/movetype_handler.dm @@ -268,3 +284,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_SPECIES_LIMBS "only_species_limbs" /// Phohibits using the "Book Of Babel" #define TRAIT_NO_BABEL "cannot_use_babel" +/// Improves the function of some cyberimps for the Grey species +/// Rename and split into several if you want to make a different functionality to another species/etc +#define TRAIT_ADVANCED_CYBERIMPLANTS "advanced_cyberimplants" +/// Any movement of non-item objects or mobs expends stamina (10 run, 5 walk) +#define TRAIT_WEAK_PULLING "weak_pulling" +/// Makes species acid proof(not it's items), affects: acetic, sulfiric, fluorosulfuric acids +#define TRAIT_ACID_PROTECTED "acid_protected" +/// Species with no vocal cords can't speak without translator +#define TRAIT_NO_VOCAL_CORDS "no_vocal_cords" + +#define TRAIT_BLOB_ZOMBIFIED "blob_zombified" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index 92565ab1354..e7a5d9d1837 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -153,7 +153,11 @@ /// inherited from riding vehicles #define VEHICLE_TRAIT "vehicle" -// blob trait sourses +/// blob trait sourses #define BLOB_INFECTED_TRAIT "blob_infected" #define VENDOR_FLATTENING_TRAIT "vendor_flattening" + +#define WET_TRAIT "wet" + +#define BLOB_ZOMBIE_TRAIT "blob_zombie_trait" diff --git a/code/__DEFINES/turfs.dm b/code/__DEFINES/turfs.dm index 82f12afc7f6..e66a50ceb84 100644 --- a/code/__DEFINES/turfs.dm +++ b/code/__DEFINES/turfs.dm @@ -32,6 +32,15 @@ min(CENTER.x + (H_RADIUS), world.maxx), min(CENTER.y + (V_RADIUS), world.maxy), CENTER.z \ ) +#define RANGE_TURFS_MULTIZ(RADIUS, CENTER, Z_MIN, Z_MAX) \ + RECT_TURFS_MULTIZ(RADIUS, RADIUS, Z_MIN, Z_MAX, CENTER) + +#define RECT_TURFS_MULTIZ(H_RADIUS, V_RADIUS, Z_MIN, Z_MAX, CENTER) \ + block( \ + max(CENTER.x - (H_RADIUS), 1), max(CENTER.y - (V_RADIUS), 1), Z_MIN, \ + min(CENTER.x + (H_RADIUS), world.maxx), min(CENTER.y + (V_RADIUS), world.maxy), Z_MAX \ + ) + /// Returns the turfs on the edge of a square with CENTER in the middle and with the given RADIUS. If used near the edge of the map, will still work fine. // order of the additions: top edge + bottom edge + left edge + right edge #define RANGE_EDGE_TURFS(RADIUS, CENTER)\ diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index cd44f85a5f5..bd793cf400e 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -19,6 +19,7 @@ #define VV_NULL "NULL" #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" +#define VV_BITFIELD "Bitfield" #define VV_REGEX "Regex" // Flags for debug_variable() that do little things to what we end up rendering diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index d9f5946d34e..21c8b3aaf3a 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -880,6 +880,8 @@ proc/dd_sortedObjectList(list/incoming) ///Returns the list if it's actually a valid list, otherwise will initialize it #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) +///Qdel every item in the list before setting the list to null +#define QDEL_LAZYLIST(L) for(var/I in L) qdel(I); L = null; ///Adds to the item K the value V, if the list is null it will initialize it #define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += V; ///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects) @@ -1180,3 +1182,12 @@ proc/dd_sortedObjectList(list/incoming) used_key_list[input_key] = 1 return input_key + + +/** + * Checks to make sure that the lists have the exact same contents, ignores the order of the contents. + */ +/proc/lists_equal_unordered(list/list_one, list/list_two) + // This ensures that both lists contain the same elements by checking if the difference between them is empty in both directions. + return !length(list_one ^ list_two) + diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 41433156e43..4ca9c6d1225 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -268,14 +268,17 @@ GLOBAL_PROTECT(log_end) var/list/targets = target for(var/t in targets) add_attack_logs(user, t, what_done, custom_level) + return var/user_str - if(ismecha(user?.loc) || isspacepod(user?.loc)) + if((user?.loc) && (ismecha(user?.loc) || isspacepod(user?.loc))) var/obj/vehicle = user.loc user_str = key_name_log(user) + COORD(vehicle) + else user_str = key_name_log(user) + COORD(user) + var/target_str var/target_info if(isatom(target)) @@ -396,3 +399,16 @@ GLOBAL_PROTECT(log_end) else user.mob.create_log(OOC_LOG, text) log_ooc(text, user) + +/proc/loc_name(atom/A) + if(!istype(A)) + return "(INVALID LOCATION)" + + var/turf/T = A + if(!istype(T)) + T = get_turf(A) + + if(istype(T)) + return "([AREACOORD(T)])" + else if(A.loc) + return "(UNKNOWN (?, ?, ?))" diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm index e801df08a6e..a5c34d5dc70 100644 --- a/code/__HELPERS/atoms.dm +++ b/code/__HELPERS/atoms.dm @@ -132,3 +132,14 @@ return FALSE return (mover.pass_flags & passflag) + +///Returns a list of all locations (except the area) the movable is within. +/proc/get_nested_locs(atom/movable/atom_on_location, include_turf = FALSE) + . = list() + var/atom/location = atom_on_location.loc + var/turf/our_turf = get_turf(atom_on_location) + while(location && location != our_turf) + . += location + location = location.loc + if(our_turf && include_turf) //At this point, only the turf is left, provided it exists. + . += our_turf diff --git a/code/__HELPERS/bitflags.dm b/code/__HELPERS/bitflags.dm new file mode 100644 index 00000000000..5aa80c92560 --- /dev/null +++ b/code/__HELPERS/bitflags.dm @@ -0,0 +1,7 @@ +#define HASBIT(CONTAINER, FLAG) ((CONTAINER) & (FLAG)) + +#define SETBIT(CONTAINER, FLAG) (CONTAINER |= (FLAG)) + +#define CLEARBIT(CONTAINER, FLAG) (CONTAINER &= ~(FLAG)) + +#define TOGGLEBIT(CONTAINER, FLAG) (CONTAINER ^= (FLAG)) diff --git a/code/__HELPERS/chat.dm b/code/__HELPERS/chat.dm new file mode 100644 index 00000000000..3f9679e3433 --- /dev/null +++ b/code/__HELPERS/chat.dm @@ -0,0 +1,15 @@ +/// Sends a message to all dead and observing players, if a source is provided a follow link will be attached. +/proc/send_to_observers(message, source) + var/list/all_observers = GLOB.dead_player_list + GLOB.current_observers_list + for(var/mob/observer as anything in all_observers) + if(isnull(source)) + to_chat(observer, "[message]") + continue + var/link = FOLLOW_LINK(observer, source) + to_chat(observer, "[link] [message]") + +/// Sends a message to everyone within the list, as well as all observers. +/proc/relay_to_list_and_observers(message, list/mob_list, source) + for(var/mob/creature as anything in mob_list) + to_chat(creature, message) + send_to_observers(message, source) diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 539019a41c8..38e70efa2a6 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -78,35 +78,48 @@ GLOB.pai_software_by_key[P.id] = P // Setup loadout gear - for(var/geartype in subtypesof(/datum/gear)) - var/datum/gear/G = geartype + for(var/datum/gear/gear as anything in subtypesof(/datum/gear)) - var/use_name = initial(G.display_name) - var/use_category = initial(G.sort_category) + if(gear == gear.path) + continue - if(G == initial(G.subtype_path)) + if(gear == gear.subtype_path) continue - if(!use_name) - error("Loadout - Missing display name: [G]") + if(!gear.index_name) + stack_trace("Loadout - Missing index name: [gear]") continue - if(!initial(G.cost)) - error("Loadout - Missing cost: [G]") + if(!gear.cost) + stack_trace("Loadout - Missing cost: [gear]") continue - if(!initial(G.path)) - error("Loadout - Missing path definition: [G]") + if(!gear.path) + stack_trace("Loadout - Missing path definition: [gear]") continue - - if(!GLOB.loadout_categories[use_category]) - GLOB.loadout_categories[use_category] = new /datum/loadout_category(use_category) - var/datum/loadout_category/LC = GLOB.loadout_categories[use_category] - GLOB.gear_datums[use_name] = new geartype - LC.gear[use_name] = GLOB.gear_datums[use_name] - - GLOB.loadout_categories = sortAssoc(GLOB.loadout_categories) - for(var/loadout_category in GLOB.loadout_categories) - var/datum/loadout_category/LC = GLOB.loadout_categories[loadout_category] - LC.gear = sortAssoc(LC.gear) + gear = new gear + var/obj/gear_item = gear.path + var/list/tweaks = list() + for(var/datum/gear_tweak/tweak as anything in gear.gear_tweaks) + tweaks[tweak.type] += list(list( + "name" = tweak.display_type, + "icon" = tweak.fa_icon, + "tooltip" = tweak.info, + )) + + GLOB.gear_tgui_info[gear.sort_category] += list( + "[gear]" = list( + "name" = gear.get_display_name() , + "index_name" = gear.index_name, + "desc" = gear.description, + "icon" = gear_item.icon, + "icon_state" = gear_item.icon_state, + "cost" = gear.cost, + "gear_tier" = gear.donator_tier, + "allowed_roles" = gear.allowed_roles, + "tweaks" = tweaks, + ) + ) + + GLOB.gear_datums[gear.index_name] = gear // Setup a list of robolimbs diff --git a/code/__HELPERS/level_traits.dm b/code/__HELPERS/level_traits.dm index 3575a008d7b..a7807445229 100644 --- a/code/__HELPERS/level_traits.dm +++ b/code/__HELPERS/level_traits.dm @@ -1,5 +1,5 @@ /proc/is_level_reachable(z) - return check_level_trait(z, REACHABLE) + return check_level_trait(z, REACHABLE) /proc/is_station_level(z) return check_level_trait(z, STATION_LEVEL) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 5409069747a..4cd2817a65d 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -46,41 +46,33 @@ return pick(valid_picks) -/proc/random_hair_style(gender, species = SPECIES_HUMAN, datum/robolimb/robohead, mob/living/carbon/human/H) +/proc/random_hair_style( + gender, + datum/species/species, + datum/robolimb/robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"], + mob/living/carbon/human/human + ) var/h_style = "Bald" var/list/valid_hairstyles = list() - if(species == SPECIES_WRYN) // wryns antennaes now bound to hivenode, no need to change them - if(H) - var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) - if(head_organ?.h_style) - return head_organ.h_style - else - return "Antennae" - for(var/hairstyle in GLOB.hair_styles_public_list) - var/datum/sprite_accessory/S = GLOB.hair_styles_public_list[hairstyle] + var/datum/sprite_accessory/style = GLOB.hair_styles_public_list[hairstyle] - if(hairstyle == "Bald") //Just in case. - valid_hairstyles += hairstyle + if(!LAZYIN(style.species_allowed, species.name)) continue - if(gender == S.unsuitable_gender) + + if(gender == style.unsuitable_gender) + continue + + if(!species.is_allowed_hair_style(human, robohead, style)) continue - if(species == SPECIES_MACNINEPERSON) //If the user is a species who can have a robotic head... - if(!robohead) - robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] - if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. - valid_hairstyles += hairstyle //Give them their hairstyles if they do. - else - if(!robohead.is_monitor && (SPECIES_HUMAN in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. - But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */ - valid_hairstyles += hairstyle - else //If the user is not a species who can have robotic heads, use the default handling. - if(species in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list. - valid_hairstyles += hairstyle - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) + LAZYADD(valid_hairstyles, hairstyle) + + if(human) + SEND_SIGNAL(human, COMSIG_RANDOM_HAIR_STYLE, valid_hairstyles, robohead) + + h_style = safepick(valid_hairstyles) return h_style @@ -668,4 +660,3 @@ out_ckey = "(Disconnected)" return out_ckey - diff --git a/code/__HELPERS/ref.dm b/code/__HELPERS/ref.dm new file mode 100644 index 00000000000..365df03dc88 --- /dev/null +++ b/code/__HELPERS/ref.dm @@ -0,0 +1,15 @@ +/** + * \ref behaviour got changed in 512 so this is necesary to replicate old behaviour. + * If it ever becomes necesary to get a more performant REF(), this lies here in wait + * #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : text_ref(thing)) +**/ +/proc/REF(input) + if(isdatum(input)) + var/datum/thing = input + if(thing.datum_flags & DF_USE_TAG) + if(!thing.tag) + stack_trace("A ref was requested of an object with DF_USE_TAG set but no tag: [thing]") + thing.datum_flags &= ~DF_USE_TAG + else + return "\[[url_encode(thing.tag)]\]" + return text_ref(input) diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 2369f9c8f64..7e693fa506f 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -307,9 +307,12 @@ return "" -//Returns a string with reserved characters and spaces before the first word and after the last word removed. -/proc/trim(text) - return trim_reduced(text) +/// Returns a string with reserved characters and spaces before the first word and after the last word removed. +/proc/trim(text, max_length) + if(max_length) + text = copytext_char(text, 1, max_length) + + return trimtext(text) || "" /// Returns a string that does not exceed max_length characters in size /proc/trim_length(text, max_length) @@ -803,21 +806,3 @@ if(ofthree == 0) return "[num]" return "[num / (10 ** (ofthree * 3))][GLOB.si_suffixes[round(length(GLOB.si_suffixes) / 2) + ofthree + 1]]" - -//Returns a string with reserved characters and spaces after the first and last letters removed -//Like trim(), but very slightly faster. worth it for niche usecases -/proc/trim_reduced(text) - var/starting_coord = 1 - var/text_len = length(text) - for (var/i in 1 to text_len) - if (text2ascii(text, i) > 32) - starting_coord = i - break - - for (var/i = text_len, i >= starting_coord, i--) - if (text2ascii(text, i) > 32) - return copytext(text, starting_coord, i + 1) - - if(starting_coord > 1) - return copytext(text, starting_coord) - return "" diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm new file mode 100644 index 00000000000..fa0f9639d4a --- /dev/null +++ b/code/__HELPERS/turfs.dm @@ -0,0 +1,27 @@ +///Returns a random turf on the station +/proc/get_random_station_turf() + var/list/turfs = get_area_turfs(pick(SSmapping.existing_station_areas)) + if (length(turfs)) + return pick(turfs) + +///Returns a random turf on the station, excludes dense turfs (like walls) and areas that have valid_territory set to FALSE +/proc/get_safe_random_station_turf(list/areas_to_pick_from = SSmapping.existing_station_areas) + for (var/i in 1 to 5) + var/list/turf_list = get_area_turfs(pick(areas_to_pick_from)) + var/turf/target + while (turf_list.len && !target) + var/I = rand(1, turf_list.len) + var/turf/checked_turf = turf_list[I] + var/area/turf_area = get_area(checked_turf) + if(!checked_turf.density && (turf_area.valid_territory) && !isgroundlessturf(checked_turf)) + var/clear = TRUE + for(var/obj/checked_object in checked_turf) + if(checked_object.density) + clear = FALSE + break + if(clear) + target = checked_turf + if (!target) + turf_list.Cut(I, I + 1) + if (target) + return target diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index caf4f02f671..5586a81b04e 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -511,8 +511,8 @@ Returns 1 if the chain up to the area contains the given typepath ///Step-towards method of determining whether one atom can see another. Similar to viewers() ///note: this is a line of sight algorithm, view() does not do any sort of raycasting and cannot be emulated by it accurately -/proc/can_see(atom/source, atom/target, length = 5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. - var/turf/current_turf = get_turf(source) +/atom/proc/can_see(atom/target, length = 5) // I couldnt be arsed to do actual raycasting :I This is horribly inaccurate. + var/turf/current_turf = get_turf(src) var/turf/target_turf = get_turf(target) if(!current_turf || !target_turf) // nullspace return FALSE @@ -1594,6 +1594,29 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) if(areas) . |= T.loc +/proc/urange_multiz(dist=0, atom/center=usr, orange=0, areas=0) + if(!dist) + if(!orange) + return list(center) + else + return list() + var/list/stations_z = levels_by_trait(STATION_LEVEL) + var/min_z = max(center.z - dist, stations_z[1]) + var/max_z = min(center.z + dist, stations_z[length(stations_z)]) + var/list/turfs = RANGE_TURFS_MULTIZ(dist, center, min_z, max_z) + if(orange) + turfs -= get_turf(center) + . = list() + for(var/V in turfs) + var/turf/T = V + . += T + . += T.contents + if(areas) + . |= T.loc + +/proc/is_there_multiz() + return SSmapping?.map_datum?.traits?.len > 1 + /proc/screen_loc2turf(scr_loc, turf/origin) var/tX = splittext(scr_loc, ",") diff --git a/code/_globalvars/_regexes.dm b/code/_globalvars/_regexes.dm index 7c2a73a4548..a9ad1b03e55 100644 --- a/code/_globalvars/_regexes.dm +++ b/code/_globalvars/_regexes.dm @@ -1,3 +1,4 @@ GLOBAL_DATUM_INIT(is_http_protocol, /regex, regex("^https?://")) GLOBAL_DATUM_INIT(filename_forbidden_chars, /regex, regex(@{""|[\\\n\t/?%*:|<>]|\.\."}, "g")) +GLOBAL_DATUM_INIT(is_color, /regex, regex("^#\[0-9a-fA-F]{6}$")) GLOBAL_PROTECT(filename_forbidden_chars) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm deleted file mode 100644 index f78faaef94a..00000000000 --- a/code/_globalvars/bitfields.dm +++ /dev/null @@ -1,31 +0,0 @@ -GLOBAL_LIST_INIT(bitfields, generate_bitfields()) - -/// Specifies a bitfield for smarter debugging -/datum/bitfield - /// The variable name that contains the bitfield - var/variable - - /// An associative list of the readable flag and its true value - var/list/flags - -/// Turns /datum/bitfield subtypes into a list for use in debugging -/proc/generate_bitfields() - var/list/bitfields = list() - for (var/_bitfield in subtypesof(/datum/bitfield)) - var/datum/bitfield/bitfield = new _bitfield - bitfields[bitfield.variable] = bitfield.flags - return bitfields - -DEFINE_BITFIELD(datum_flags, list( - "DF_ISPROCESSING" = DF_ISPROCESSING, - "DF_VAR_EDITED" = DF_VAR_EDITED, - "DF_USE_TAG" = DF_USE_TAG, -)) - -DEFINE_BITFIELD(turf_flags, list( - "NOJAUNT" = NOJAUNT, - "UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF, - "RESERVATION_TURF" = RESERVATION_TURF, - "NO_LAVA_GEN" = NO_LAVA_GEN, - "NO_RUINS" = NO_RUINS, -)) diff --git a/code/_globalvars/bitfields/admin.dm b/code/_globalvars/bitfields/admin.dm new file mode 100644 index 00000000000..d83298621f6 --- /dev/null +++ b/code/_globalvars/bitfields/admin.dm @@ -0,0 +1,30 @@ +DEFINE_BITFIELD(mute_category, list( + "MUTE_IC" = MUTE_IC, + "MUTE_OOC" = MUTE_OOC, + "MUTE_PRAY" = MUTE_PRAY, + "MUTE_ADMINHELP" = MUTE_ADMINHELP, + "MUTE_DEADCHAT" = MUTE_DEADCHAT, + "MUTE_TTS" = MUTE_TTS, + "MUTE_EMOTE" = MUTE_EMOTE, +)) + +DEFINE_BITFIELD(rights, list( + "R_BUILDMODE" = R_BUILDMODE, + "R_ADMIN" = R_ADMIN, + "R_BAN" = R_BAN, + "R_EVENT" = R_EVENT, + "R_SERVER" = R_SERVER, + "R_DEBUG" = R_DEBUG, + "R_POSSESS" = R_POSSESS, + "R_PERMISSIONS" = R_PERMISSIONS, + "R_STEALTH" = R_STEALTH, + "R_REJUVINATE" = R_REJUVINATE, + "R_VAREDIT" = R_VAREDIT, + "R_SOUNDS" = R_SOUNDS, + "R_SPAWN" = R_SPAWN, + "R_MOD" = R_MOD, + "R_MENTOR" = R_MENTOR, + "R_PROCCALL" = R_PROCCALL, + "R_VIEWRUNTIMES" = R_VIEWRUNTIMES, +)) + diff --git a/code/_globalvars/bitfields/bitfields.dm b/code/_globalvars/bitfields/bitfields.dm new file mode 100644 index 00000000000..7f74cc4d50a --- /dev/null +++ b/code/_globalvars/bitfields/bitfields.dm @@ -0,0 +1,56 @@ +GLOBAL_LIST_INIT(bitfields, generate_bitfields()) + +/// Specifies a bitfield for smarter debugging +/datum/bitfield + /// The variable name that contains the bitfield + var/variable + + /// An associative list of the readable flag and its true value + var/list/flags + + + +/datum/bitfield/can_vv_delete() + return FALSE + +/datum/bitfield/vv_edit_var(var_name, var_value) + return FALSE // no. + +/// Turns /datum/bitfield subtypes into a list for use in debugging +/proc/generate_bitfields() + var/list/bitfields = list() + for (var/_bitfield in subtypesof(/datum/bitfield)) + var/datum/bitfield/bitfield = new _bitfield + bitfields[bitfield.variable] = bitfield.flags + return bitfields + + +/proc/translate_bitfield(variable_type, variable_name, variable_value) + if(variable_type != VV_BITFIELD) + return variable_value + + var/list/flags = list() + for(var/flag in GLOB.bitfields[variable_name]) + if(variable_value & GLOB.bitfields[variable_name][flag]) + flags += flag + if(length(flags)) + return jointext(flags, ", ") + return "NONE" + +/proc/input_bitfield(mob/user, bitfield, current_value) + if(!user || !(bitfield in GLOB.bitfields)) + return + var/list/currently_checked = list() + for(var/name in GLOB.bitfields[bitfield]) + currently_checked[name] = (current_value & GLOB.bitfields[bitfield][name]) + + var/list/result = tgui_input_checkbox_list(user, "Редактирование битовой маски для [bitfield].", "Битовая маска", currently_checked) + if(isnull(result) || !islist(result)) + return + + var/new_result = 0 + for(var/name in GLOB.bitfields[bitfield]) + if(result[name]) + new_result |= GLOB.bitfields[bitfield][name] + return new_result + diff --git a/code/_globalvars/bitfields/declarations.dm b/code/_globalvars/bitfields/declarations.dm new file mode 100644 index 00000000000..d2af5eb18c2 --- /dev/null +++ b/code/_globalvars/bitfields/declarations.dm @@ -0,0 +1,121 @@ +DEFINE_BITFIELD(appearance_flags, list( + "LONG_GLIDE" = LONG_GLIDE, + "RESET_COLOR" = RESET_COLOR, + "RESET_ALPHA" = RESET_ALPHA, + "RESET_TRANSFORM" = RESET_TRANSFORM, + "NO_CLIENT_COLOR" = NO_CLIENT_COLOR, + "KEEP_TOGETHER" = KEEP_TOGETHER, + "KEEP_APART" = KEEP_APART, + "PLANE_MASTER" = PLANE_MASTER, + "TILE_BOUND" = TILE_BOUND, + "PIXEL_SCALE" = PIXEL_SCALE, + "PASS_MOUSE" = PASS_MOUSE, + "TILE_MOVER" = TILE_MOVER, +)) + +DEFINE_BITFIELD(vis_flags, list( + "VIS_HIDE" = VIS_HIDE, + "VIS_INHERIT_DIR" = VIS_INHERIT_DIR, + "VIS_INHERIT_ICON" = VIS_INHERIT_ICON, + "VIS_INHERIT_ICON_STATE" = VIS_INHERIT_ICON_STATE, + "VIS_INHERIT_ID" = VIS_INHERIT_ID, + "VIS_INHERIT_LAYER" = VIS_INHERIT_LAYER, + "VIS_INHERIT_PLANE" = VIS_INHERIT_PLANE, + "VIS_UNDERLAY" = VIS_UNDERLAY, +)) + +DEFINE_BITFIELD(datum_flags, list( + "DF_ISPROCESSING" = DF_ISPROCESSING, + "DF_VAR_EDITED" = DF_VAR_EDITED, + "DF_USE_TAG" = DF_USE_TAG, +)) + +DEFINE_BITFIELD(pass_flags, list( + "PASSTABLE" = PASSTABLE, + "PASSGLASS" = PASSGLASS, + "PASSGRILLE" = PASSGRILLE, + "PASSBLOB" = PASSBLOB, + "PASSMOB" = PASSMOB, + "LETPASSTHROW" = LETPASSTHROW, + "PASSMACHINE" = PASSMACHINE, + "PASSSTRUCTURE" = PASSSTRUCTURE, + "PASSFLAPS" = PASSFLAPS, + "PASSFENCE" = PASSFENCE, + "PASSDOOR" = PASSDOOR, + "PASSVEHICLE" = PASSVEHICLE, + "PASSITEM" = PASSITEM, + "LETPASSCLICKS" = LETPASSCLICKS, +)) + +DEFINE_BITFIELD(pass_flags_self, list( + "PASSTABLE" = PASSTABLE, + "PASSGLASS" = PASSGLASS, + "PASSGRILLE" = PASSGRILLE, + "PASSBLOB" = PASSBLOB, + "PASSMOB" = PASSMOB, + "LETPASSTHROW" = LETPASSTHROW, + "PASSMACHINE" = PASSMACHINE, + "PASSSTRUCTURE" = PASSSTRUCTURE, + "PASSFLAPS" = PASSFLAPS, + "PASSFENCE" = PASSFENCE, + "PASSDOOR" = PASSDOOR, + "PASSVEHICLE" = PASSVEHICLE, + "PASSITEM" = PASSITEM, + "LETPASSCLICKS" = LETPASSCLICKS, +)) + + +DEFINE_BITFIELD(gas_type, list( + "LINDA_SPAWN_HEAT" = LINDA_SPAWN_HEAT, + "LINDA_SPAWN_20C" = LINDA_SPAWN_20C, + "LINDA_SPAWN_TOXINS" = LINDA_SPAWN_TOXINS, + "LINDA_SPAWN_OXYGEN" = LINDA_SPAWN_OXYGEN, + "LINDA_SPAWN_CO2" = LINDA_SPAWN_CO2, + "LINDA_SPAWN_NITROGEN" = LINDA_SPAWN_NITROGEN, + "LINDA_SPAWN_N2O" = LINDA_SPAWN_N2O, + "LINDA_SPAWN_AGENT_B" = LINDA_SPAWN_AGENT_B, + "LINDA_SPAWN_AIR" = LINDA_SPAWN_AIR, +)) + +DEFINE_BITFIELD(spawn_contents, list( + "LINDA_SPAWN_HEAT" = LINDA_SPAWN_HEAT, + "LINDA_SPAWN_20C" = LINDA_SPAWN_20C, + "LINDA_SPAWN_TOXINS" = LINDA_SPAWN_TOXINS, + "LINDA_SPAWN_OXYGEN" = LINDA_SPAWN_OXYGEN, + "LINDA_SPAWN_CO2" = LINDA_SPAWN_CO2, + "LINDA_SPAWN_NITROGEN" = LINDA_SPAWN_NITROGEN, + "LINDA_SPAWN_N2O" = LINDA_SPAWN_N2O, + "LINDA_SPAWN_AGENT_B" = LINDA_SPAWN_AGENT_B, + "LINDA_SPAWN_AIR" = LINDA_SPAWN_AIR, +)) + +DEFINE_BITFIELD(qdel_flags, list( + "QDEL_ITEM_ADMINS_WARNED" = QDEL_ITEM_ADMINS_WARNED, + "QDEL_ITEM_SUSPENDED_FOR_LAG" = QDEL_ITEM_SUSPENDED_FOR_LAG, +)) + +DEFINE_BITFIELD(movement_type, list( + "GROUND" = GROUND, + "FLYING" = FLYING, + "VENTCRAWLING" = VENTCRAWLING, + "FLOATING" = FLOATING, + "PHASING" = PHASING, + "UPSIDE_DOWN" = UPSIDE_DOWN, +)) + +DEFINE_BITFIELD(area_flags, list( + "UNIQUE_AREA" = UNIQUE_AREA, + "BLOBS_ALLOWED" = BLOBS_ALLOWED, + "CAVES_ALLOWED" = CAVES_ALLOWED, + "FLORA_ALLOWED" = FLORA_ALLOWED, + "MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED, + "MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED, +)) + +DEFINE_BITFIELD(turf_flags, list( + "NOJAUNT" = NOJAUNT, + "UNUSED_RESERVATION_TURF" = UNUSED_RESERVATION_TURF, + "RESERVATION_TURF" = RESERVATION_TURF, + "NO_LAVA_GEN" = NO_LAVA_GEN, + "NO_RUINS" = NO_RUINS, +)) diff --git a/code/_globalvars/bitfields/food.dm b/code/_globalvars/bitfields/food.dm new file mode 100644 index 00000000000..83e98c08627 --- /dev/null +++ b/code/_globalvars/bitfields/food.dm @@ -0,0 +1,62 @@ +DEFINE_BITFIELD(disliked_food, list( + "MEAT" = MEAT, + "VEGETABLES" = VEGETABLES, + "RAW" = RAW, + "JUNKFOOD" = JUNKFOOD, + "GRAIN" = GRAIN, + "FRUIT" = FRUIT, + "DAIRY" = DAIRY, + "FRIED" = FRIED, + "ALCOHOL" = ALCOHOL, + "SUGAR" = SUGAR, + "EGG" = EGG, + "GROSS" = GROSS, + "TOXIC" = TOXIC, +)) +DEFINE_BITFIELD(liked_food, list( + "MEAT" = MEAT, + "VEGETABLES" = VEGETABLES, + "RAW" = RAW, + "JUNKFOOD" = JUNKFOOD, + "GRAIN" = GRAIN, + "FRUIT" = FRUIT, + "DAIRY" = DAIRY, + "FRIED" = FRIED, + "ALCOHOL" = ALCOHOL, + "SUGAR" = SUGAR, + "EGG" = EGG, + "GROSS" = GROSS, + "TOXIC" = TOXIC, +)) + +DEFINE_BITFIELD(toxic_food, list( + "MEAT" = MEAT, + "VEGETABLES" = VEGETABLES, + "RAW" = RAW, + "JUNKFOOD" = JUNKFOOD, + "GRAIN" = GRAIN, + "FRUIT" = FRUIT, + "DAIRY" = DAIRY, + "FRIED" = FRIED, + "ALCOHOL" = ALCOHOL, + "SUGAR" = SUGAR, + "EGG" = EGG, + "GROSS" = GROSS, + "TOXIC" = TOXIC, +)) + +DEFINE_BITFIELD(foodtype, list( + "MEAT" = MEAT, + "VEGETABLES" = VEGETABLES, + "RAW" = RAW, + "JUNKFOOD" = JUNKFOOD, + "GRAIN" = GRAIN, + "FRUIT" = FRUIT, + "DAIRY" = DAIRY, + "FRIED" = FRIED, + "ALCOHOL" = ALCOHOL, + "SUGAR" = SUGAR, + "EGG" = EGG, + "GROSS" = GROSS, + "TOXIC" = TOXIC, +)) diff --git a/code/_globalvars/bitfields/icon_smoothing.dm b/code/_globalvars/bitfields/icon_smoothing.dm new file mode 100644 index 00000000000..e08f2ffd7fa --- /dev/null +++ b/code/_globalvars/bitfields/icon_smoothing.dm @@ -0,0 +1,27 @@ +DEFINE_BITFIELD(smoothing_flags, list( + "SMOOTH_CORNERS" = SMOOTH_CORNERS, + "SMOOTH_BITMASK" = SMOOTH_BITMASK, + "SMOOTH_DIAGONAL_CORNERS" = SMOOTH_DIAGONAL_CORNERS, + "SMOOTH_BORDER" = SMOOTH_BORDER, + "SMOOTH_QUEUED" = SMOOTH_QUEUED, + "SMOOTH_OBJ" = SMOOTH_OBJ, + "SMOOTH_BORDER_OBJECT" = SMOOTH_BORDER_OBJECT, + "SMOOTH_BROKEN_TURF" = SMOOTH_BROKEN_TURF, + "SMOOTH_BURNT_TURF" = SMOOTH_BURNT_TURF, + "SMOOTH_FALSE" = SMOOTH_FALSE, + "SMOOTH_TRUE" = SMOOTH_TRUE, + "SMOOTH_MORE" = SMOOTH_MORE, + "SMOOTH_DIAGONAL" = SMOOTH_DIAGONAL, +)) + + +DEFINE_BITFIELD(smoothing_junction, list( + "NORTH_JUNCTION" = NORTH_JUNCTION, + "SOUTH_JUNCTION" = SOUTH_JUNCTION, + "EAST_JUNCTION" = EAST_JUNCTION, + "WEST_JUNCTION" = WEST_JUNCTION, + "NORTHEAST_JUNCTION" = NORTHEAST_JUNCTION, + "SOUTHEAST_JUNCTION" = SOUTHEAST_JUNCTION, + "SOUTHWEST_JUNCTION" = SOUTHWEST_JUNCTION, + "NORTHWEST_JUNCTION" = NORTHWEST_JUNCTION, +)) diff --git a/code/_globalvars/bitfields/jobs.dm b/code/_globalvars/bitfields/jobs.dm new file mode 100644 index 00000000000..f72b29e0c83 --- /dev/null +++ b/code/_globalvars/bitfields/jobs.dm @@ -0,0 +1,6 @@ +DEFINE_BITFIELD(department_flag, list( + "JOBCAT_ENGSEC" = JOBCAT_ENGSEC, + "JOBCAT_MEDSCI" = JOBCAT_MEDSCI, + "JOBCAT_SUPPORT" = JOBCAT_SUPPORT, + "JOBCAT_KARMA" = JOBCAT_KARMA, +)) diff --git a/code/_globalvars/bitfields/mecha.dm b/code/_globalvars/bitfields/mecha.dm new file mode 100644 index 00000000000..ae38c84f53d --- /dev/null +++ b/code/_globalvars/bitfields/mecha.dm @@ -0,0 +1,14 @@ +DEFINE_BITFIELD(mech_type, list( + "MECH_TYPE_NONE" = MECH_TYPE_NONE, + "MECH_TYPE_RIPLEY" = MECH_TYPE_RIPLEY, + "MECH_TYPE_CLARKE" = MECH_TYPE_CLARKE, + "MECH_TYPE_ODYSSEUS" = MECH_TYPE_ODYSSEUS, + "MECH_TYPE_GYGAX" = MECH_TYPE_GYGAX, + "MECH_TYPE_DURAND" = MECH_TYPE_DURAND, + "MECH_TYPE_PHAZON" = MECH_TYPE_PHAZON, + "MECH_TYPE_HONKER" = MECH_TYPE_HONKER, + "MECH_TYPE_RETICENCE" = MECH_TYPE_RETICENCE, + "MECH_TYPE_LOCKER" = MECH_TYPE_LOCKER, + "MECH_TYPE_MARAUDER" = MECH_TYPE_MARAUDER, + "MECH_TYPE_SIDEWINTER" = MECH_TYPE_SIDEWINTER, +)) diff --git a/code/_globalvars/bitfields/mobs.dm b/code/_globalvars/bitfields/mobs.dm new file mode 100644 index 00000000000..f59ac9dca7a --- /dev/null +++ b/code/_globalvars/bitfields/mobs.dm @@ -0,0 +1,67 @@ +DEFINE_BITFIELD(appearance_changes, list( + "APPEARANCE_UPDATE_DNA" = APPEARANCE_UPDATE_DNA, + "APPEARANCE_RACE" = APPEARANCE_RACE, + "APPEARANCE_GENDER" = APPEARANCE_GENDER, + "APPEARANCE_SKIN" = APPEARANCE_SKIN, + "APPEARANCE_HAIR" = APPEARANCE_HAIR, + "APPEARANCE_HAIR_COLOR" = APPEARANCE_HAIR_COLOR, + "APPEARANCE_SECONDARY_HAIR_COLOR" = APPEARANCE_SECONDARY_HAIR_COLOR, + "APPEARANCE_FACIAL_HAIR" = APPEARANCE_FACIAL_HAIR, + "APPEARANCE_FACIAL_HAIR_COLOR" = APPEARANCE_FACIAL_HAIR_COLOR, + "APPEARANCE_SECONDARY_FACIAL_HAIR_COLOR" = APPEARANCE_SECONDARY_FACIAL_HAIR_COLOR, + "APPEARANCE_EYE_COLOR" = APPEARANCE_EYE_COLOR, + "APPEARANCE_HEAD_ACCESSORY" = APPEARANCE_HEAD_ACCESSORY, + "APPEARANCE_MARKINGS" = APPEARANCE_MARKINGS, + "APPEARANCE_BODY_ACCESSORY" = APPEARANCE_BODY_ACCESSORY, + "APPEARANCE_ALT_HEAD" = APPEARANCE_ALT_HEAD, +)) +DEFINE_BITFIELD(bodyflags, list( + "HAS_HEAD_ACCESSORY" = HAS_HEAD_ACCESSORY, + "HAS_TAIL" = HAS_SKIN_TONE, + "TAIL_OVERLAPPED" = APPEARANCE_GENDER, + "HAS_SKIN_TONE" = HAS_SKIN_TONE, + "HAS_ICON_SKIN_TONE" = HAS_ICON_SKIN_TONE, + "HAS_SKIN_COLOR" = HAS_SKIN_COLOR, + "HAS_HEAD_MARKINGS" = HAS_HEAD_MARKINGS, + "HAS_BODY_MARKINGS" = HAS_BODY_MARKINGS, + "HAS_TAIL_MARKINGS" = HAS_TAIL_MARKINGS, + "TAIL_WAGGING" = TAIL_WAGGING, + "NO_EYES" = NO_EYES, + "HAS_ALT_HEADS" = HAS_ALT_HEADS, + "HAS_BODYACC_COLOR" = HAS_BODYACC_COLOR, + "BALD" = BALD, + "ALL_RPARTS" = ALL_RPARTS, +)) + +DEFINE_BITFIELD(mobility_flags, list( + "MOBILITY_MOVE" = MOBILITY_MOVE, + "MOBILITY_STAND" = MOBILITY_STAND, + "MOBILITY_PICKUP" = MOBILITY_PICKUP, + "MOBILITY_USE" = MOBILITY_USE, + "MOBILITY_UI" = MOBILITY_UI, + "MOBILITY_STORAGE" = MOBILITY_STORAGE, + "MOBILITY_PULL" = MOBILITY_PULL, + "MOBILITY_REST" = MOBILITY_REST, + "MOBILITY_LIEDOWN" = MOBILITY_LIEDOWN, +)) + +DEFINE_BITFIELD(status_flags, list( + "CANSTUN" = CANSTUN, + "CANWEAKEN" = CANWEAKEN, + "CANSTAMCRIT" = CANSTAMCRIT, + "CANKNOCKDOWN" = CANKNOCKDOWN, + "CANPARALYSE" = CANPARALYSE, + "CANPUSH" = CANPUSH, + "PASSEMOTES" = PASSEMOTES, + "IGNORESLOWDOWN" = IGNORESLOWDOWN, + "IGNORE_SPEED_CHANGES" = IGNORE_SPEED_CHANGES, +)) + +DEFINE_BITFIELD(bot_type, list( + "SEC_BOT" = SEC_BOT, + "MULE_BOT" = MULE_BOT, + "FLOOR_BOT" = FLOOR_BOT, + "CLEAN_BOT" = CLEAN_BOT, + "MED_BOT" = MED_BOT, + "HONK_BOT" = HONK_BOT, +)) diff --git a/code/_globalvars/bitfields/objs.dm b/code/_globalvars/bitfields/objs.dm new file mode 100644 index 00000000000..14358c32514 --- /dev/null +++ b/code/_globalvars/bitfields/objs.dm @@ -0,0 +1,32 @@ +DEFINE_BITFIELD(resistance_flags, list( + "LAVA_PROOF" = LAVA_PROOF, + "FIRE_PROOF" = FIRE_PROOF, + "FLAMMABLE" = FLAMMABLE, + "ON_FIRE" = ON_FIRE, + "UNACIDABLE" = UNACIDABLE, + "ACID_PROOF" = ACID_PROOF, + "INDESTRUCTIBLE" = INDESTRUCTIBLE, + "FREEZE_PROOF" = FREEZE_PROOF, + "NO_MALF_EFFECT" = NO_MALF_EFFECT, + "NO_MOUSTACHING" = NO_MOUSTACHING, +)) + +DEFINE_BITFIELD(immunity_resistance_flags, list( + "LAVA_PROOF" = LAVA_PROOF, + "FIRE_PROOF" = FIRE_PROOF, + "FLAMMABLE" = FLAMMABLE, + "ON_FIRE" = ON_FIRE, + "UNACIDABLE" = UNACIDABLE, + "ACID_PROOF" = ACID_PROOF, + "INDESTRUCTIBLE" = INDESTRUCTIBLE, + "FREEZE_PROOF" = FREEZE_PROOF, + "NO_MALF_EFFECT" = NO_MALF_EFFECT, + "NO_MOUSTACHING" = NO_MOUSTACHING, +)) + +DEFINE_BITFIELD(initialize_dirs, list( + "DISP_DIR_LEFT" = DISP_DIR_LEFT, + "DISP_DIR_RIGHT" = DISP_DIR_RIGHT, + "DISP_DIR_FLIP" = DISP_DIR_FLIP, + "DISP_DIR_NONE" = DISP_DIR_NONE, +)) diff --git a/code/_globalvars/bitfields/sight.dm b/code/_globalvars/bitfields/sight.dm new file mode 100644 index 00000000000..e3d3b8272df --- /dev/null +++ b/code/_globalvars/bitfields/sight.dm @@ -0,0 +1,31 @@ + +DEFINE_BITFIELD(examine_extensions, list( + "EXAMINE_HUD_NONE" = EXAMINE_HUD_NONE, + "EXAMINE_HUD_SECURITY_READ" = EXAMINE_HUD_SECURITY_READ, + "EXAMINE_HUD_SECURITY_WRITE" = EXAMINE_HUD_SECURITY_WRITE, + "EXAMINE_HUD_MEDICAL" = EXAMINE_HUD_MEDICAL, + "EXAMINE_HUD_SKILLS" = EXAMINE_HUD_SKILLS, + "EXAMINE_HUD_BOTANY" = EXAMINE_HUD_BOTANY, + "EXAMINE_HUD_SCIENCE" = EXAMINE_HUD_SCIENCE, +)) + +DEFINE_BITFIELD(sight, list( + "BLIND" = BLIND, + "SEE_MOBS" = SEE_MOBS, + "SEE_OBJS" = SEE_OBJS, + "SEE_TURFS" = SEE_TURFS, + "SEE_SELF" = SEE_SELF, + "SEE_INFRA" = SEE_INFRA, + "SEE_PIXELS" = SEE_PIXELS, + "SEE_THRU" = SEE_THRU, + "SEE_BLACKNESS" = SEE_BLACKNESS, +)) +DEFINE_BITFIELD(visor_vars_to_toggle, list( + "VISOR_FLASHPROTECT" = VISOR_FLASHPROTECT, + "VISOR_TINT" = VISOR_TINT, + "VISOR_VISIONFLAGS" = VISOR_VISIONFLAGS, + "VISOR_DARKNESSVIEW" = VISOR_DARKNESSVIEW, + "VISOR_INVISVIEW" = VISOR_INVISVIEW, + "VISOR_HUDTYPE" = VISOR_HUDTYPE, + "VISOR_EXAM_EXTENTIONS" = VISOR_EXAM_EXTENTIONS, +)) diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm index d3d4a4aaad6..b4099db0e82 100644 --- a/code/_globalvars/genetics.dm +++ b/code/_globalvars/genetics.dm @@ -46,6 +46,7 @@ GLOBAL_VAR_INIT(weakblock, 0) GLOBAL_VAR_INIT(hornsblock, 0) GLOBAL_VAR_INIT(comicblock, 0) GLOBAL_VAR_INIT(paraplegiablock, 0) +GLOBAL_VAR_INIT(aphasiablock, 0) // Powers GLOBAL_VAR_INIT(soberblock, 0) @@ -74,5 +75,4 @@ GLOBAL_LIST_EMPTY(global_mutations) GLOBAL_VAR_INIT(fakeblock1, 0) GLOBAL_VAR_INIT(fakeblock2, 0) GLOBAL_VAR_INIT(fakeblock3, 0) -GLOBAL_VAR_INIT(fakeblock4, 0) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 86139603b36..dd7b45868e8 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -24,8 +24,16 @@ GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and GLOBAL_LIST_EMPTY(spirits) //List of all the spirits, including Masks GLOBAL_LIST_EMPTY(alive_mob_list) //List of all alive mobs, including clientless. Excludes /mob/new_player GLOBAL_LIST_EMPTY(dead_mob_list) //List of all dead mobs, including clientless. Excludes /mob/new_player +/// All alive mobs with clients. +GLOBAL_LIST_EMPTY(alive_player_list) +/// All dead mobs with clients. Does not include observers. +GLOBAL_LIST_EMPTY(dead_player_list) +/// All observers with clients that joined as observers. +GLOBAL_LIST_EMPTY(current_observers_list) GLOBAL_LIST_EMPTY(respawnable_list) //List of all mobs, dead or in mindless creatures that still be respawned. GLOBAL_LIST_EMPTY(non_respawnable_keys) //List of ckeys that are excluded from respawning for remainder of round. +/// All living mobs which can hear blob telepathy +GLOBAL_LIST_EMPTY(blob_telepathy_mobs) /// One for each AI_* status define, List of all simple animals, including clientless GLOBAL_LIST_INIT(simple_animals, list(list(), list(), list(), list())) GLOBAL_LIST_EMPTY(bots_list) //List of all bots(beepsky, medibots,etc) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 6a4e744fbed..727a99fcfb5 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -22,6 +22,7 @@ GLOBAL_LIST_INIT(machines, list()) GLOBAL_LIST_INIT(syndiepads, list()) //list of all syndiepads GLOBAL_LIST_INIT(syndie_cargo_consoles, list()) //list of all syndie cargo consoles GLOBAL_LIST_INIT(rcd_list, list()) //list of Rapid Construction Devices. +GLOBAL_LIST_EMPTY(supplypod_loading_bays) GLOBAL_LIST_INIT(apcs, list()) GLOBAL_LIST_INIT(air_alarms, list()) @@ -64,3 +65,4 @@ GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the GLOBAL_LIST_EMPTY(wire_name_directory) GLOBAL_LIST_EMPTY(visual_portals) +GLOBAL_LIST_EMPTY(pod_styles_by_type) diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 54ad01b0fe2..f61699a0c5e 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -27,20 +27,25 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_IMMOBILIZE" = TRAIT_NO_IMMOBILIZE, "TRAIT_NO_TELEPORT" = TRAIT_NO_TELEPORT, "TRAIT_RADSTORM_IMMUNE" = TRAIT_RADSTORM_IMMUNE, + "TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SOLARFLARE_IMMUNE" = TRAIT_SOLARFLARE_IMMUNE, "TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE, "TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE, ), /mob = list( + "TRAIT_ACID_PROTECTED" = TRAIT_ACID_PROTECTED, "TRAIT_AI_UNTRACKABLE" = TRAIT_AI_UNTRACKABLE, "TRAIT_BADASS" = TRAIT_BADASS, "TRAIT_BLIND" = TRAIT_BLIND, + "TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY, "TRAIT_BLOODCRAWL" = TRAIT_BLOODCRAWL, "TRAIT_BLOODCRAWL_EAT" = TRAIT_BLOODCRAWL_EAT, "TRAIT_CAN_STRIP" = TRAIT_CAN_STRIP, "TRAIT_CANT_RIDE" = TRAIT_CANT_RIDE, + "TRAIT_CHASM_DESTROYED" = TRAIT_CHASM_DESTROYED, "TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS, + "TRAIT_NO_GUNS" = TRAIT_NO_GUNS, "TRAIT_COLORBLIND" = TRAIT_COLORBLIND, "TRAIT_COMIC" = TRAIT_COMIC, "TRAIT_CLUMSY" = TRAIT_CLUMSY, @@ -86,6 +91,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NEARSIGHTED" = TRAIT_NEARSIGHTED, "TRAIT_NEGATES_GRAVITY" = TRAIT_NEGATES_GRAVITY, "TRAIT_NO_BIOCHIPS" = TRAIT_NO_BIOCHIPS, + "TRAIT_NO_DEATH" = TRAIT_NO_DEATH, "TRAIT_NO_BLOOD" = TRAIT_NO_BLOOD, "TRAIT_NO_BLOOD_RESTORE" = TRAIT_NO_BLOOD_RESTORE, "TRAIT_NO_BREATH" = TRAIT_NO_BREATH, @@ -111,6 +117,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_SPECIES_EXAMINE" = TRAIT_NO_SPECIES_EXAMINE, "TRAIT_NO_SPELLS" = TRAIT_NO_SPELLS, "TRAIT_NO_TRANSFORM" = TRAIT_NO_TRANSFORM, + "TRAIT_NO_VOCAL_CORDS" = TRAIT_NO_VOCAL_CORDS, "TRAIT_OBESITY" = TRAIT_OBESITY, "TRAIT_OPEN_MIND" = TRAIT_OPEN_MIND, "TRAIT_PACIFISM" = TRAIT_PACIFISM, @@ -142,6 +149,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE, "TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE, "TRAIT_WATERBREATH" = TRAIT_WATERBREATH, + "TRAIT_WEAK_PULLING" = TRAIT_WEAK_PULLING, "TRAIT_WINGDINGS" = TRAIT_WINGDINGS, "TRAIT_XENO_HOST" = TRAIT_XENO_HOST, "TRAIT_XRAY" = TRAIT_XRAY, diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index f815904dff8..99a853c91ec 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -44,7 +44,7 @@ Note that this proc can be overridden, and is in the case of screen objects. */ /atom/Click(location,control,params) - usr.ClickOn(src, params) + usr.ClickOn(src, params, location) /atom/DblClick(location,control,params) usr.DblClickOn(src,params) @@ -223,10 +223,14 @@ proximity_flag is not currently passed to attack_hand, and is instead used in human click code to allow glove touches only at melee range. */ -/mob/proc/UnarmedAttack(atom/A, proximity_flag) - if(ismob(A)) +/mob/proc/UnarmedAttack(atom/atom, proximity_flag) + if(ismob(atom)) changeNext_move(CLICK_CD_MELEE) + return OnUnarmedAttack(atom, proximity_flag) + +/mob/proc/OnUnarmedAttack(atom/atom, proximity_flag) + return /* Ranged unarmed attack: diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 15b1ca626de..529ac974280 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -192,10 +192,8 @@ clicks, you can do so here, but you will have to change attack_robot() above to the proper function */ -/mob/living/silicon/robot/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return - A.attack_robot(src) +/mob/living/silicon/robot/OnUnarmedAttack(atom/atom) + return atom.attack_robot(src) /mob/living/silicon/robot/RangedAttack(atom/A, params) A.attack_robot(src) diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm index 87478352c51..9727964d176 100644 --- a/code/_onclick/hud/action_button.dm +++ b/code/_onclick/hud/action_button.dm @@ -153,11 +153,13 @@ icon = initial(icon) icon_state = "bg_default" if(user.client) // Apply the client's UI style - icon = ui_style2icon(user.client.prefs.UI_style) + icon = ui_style2icon(user.client.prefs?.UI_style) icon_state = "template" + if(user.client) - alpha = user.client.prefs.UI_style_alpha - color = user.client.prefs.UI_style_color + alpha = user.client.prefs?.UI_style_alpha + color = user.client.prefs?.UI_style_color + update_icon(UPDATE_OVERLAYS) diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index aeb47d7f953..b97d312700d 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -158,9 +158,6 @@ var/mob/living/silicon/ai/AI = usr AI.move_down() -/mob/living/silicon/ai/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/ai(src) /datum/hud/ai/New(mob/owner) ..() diff --git a/code/_onclick/hud/alien.dm b/code/_onclick/hud/alien.dm index 9c05d0a72d5..cc9b1def284 100644 --- a/code/_onclick/hud/alien.dm +++ b/code/_onclick/hud/alien.dm @@ -26,10 +26,6 @@ screen_loc = ui_alienplasmadisplay -/mob/living/carbon/alien/humanoid/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/alien(src) - /datum/hud/alien/New(mob/living/carbon/alien/humanoid/owner) ..() diff --git a/code/_onclick/hud/alien_larva.dm b/code/_onclick/hud/alien_larva.dm index 3be0fd9487d..229b290f268 100644 --- a/code/_onclick/hud/alien_larva.dm +++ b/code/_onclick/hud/alien_larva.dm @@ -1,7 +1,3 @@ -/mob/living/carbon/alien/larva/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/larva(src) - /datum/hud/larva/New(mob/owner) ..() diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index 91bd2e9e4db..7cd2290c683 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -1,9 +1,5 @@ -/mob/camera/blob/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/blob_overmind(src) - /atom/movable/screen/blob - icon = 'icons/mob/blob.dmi' + icon = 'icons/hud/blob.dmi' /atom/movable/screen/blob/MouseEntered(location,control,params) openToolTip(usr,src,params,title = name,content = desc, theme = "blob") @@ -13,7 +9,7 @@ /atom/movable/screen/blob/BlobHelp icon_state = "ui_help" - name = "Blob Help" + name = "Помощь" desc = "Помощь по игре за блоба!" /atom/movable/screen/blob/BlobHelp/Click() @@ -23,7 +19,7 @@ /atom/movable/screen/blob/JumpToNode icon_state = "ui_tonode" - name = "Jump to Node" + name = "К узлу" desc = "Перемещает вашу камеру к выбранному узлу." /atom/movable/screen/blob/JumpToNode/Click() @@ -33,7 +29,7 @@ /atom/movable/screen/blob/JumpToCore icon_state = "ui_tocore" - name = "Jump to Core" + name = "К ядру" desc = "Перемещает вашу камеру к вашему ядру." /atom/movable/screen/blob/JumpToCore/MouseEntered(location,control,params) @@ -49,8 +45,13 @@ /atom/movable/screen/blob/Blobbernaut icon_state = "ui_blobbernaut" - name = "Produce Blobbernaut (60)" - desc = "Производит сильного и умного блоббернаута из фабрики за 60 ресурсов.
Фабрика будет уничтожена в процессе." + name = "Создать блобернаута (ERROR)" + desc = "Создает сильного и умного блоббернаута из фабрики за ERROR ресурсов.
Фабрика станет хрупкой и не сможет производить споры." + +/atom/movable/screen/blob/Blobbernaut/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Создать блобернаута ([BLOBMOB_BLOBBERNAUT_RESOURCE_COST])" + desc = "Создает сильного и умного блоббернаута из фабрики за [BLOBMOB_BLOBBERNAUT_RESOURCE_COST] ресурсов.
Фабрика станет хрупкой и не сможет производить споры." /atom/movable/screen/blob/Blobbernaut/Click() if(isovermind(usr)) @@ -59,64 +60,93 @@ /atom/movable/screen/blob/StorageBlob icon_state = "ui_storage" - name = "Produce Storage Blob (40)" - desc = "Производит хранилище за 40 ресурсов.
Хранилища увеличат ваш максимальный лимит ресурсов на 50." + name = "Создать хранилище (ERROR)" + desc = "Создает хранилище за ERROR ресурсов.
Хранилища увеличивают ваш максимальный лимит ресурсов на ERROR." + +/atom/movable/screen/blob/StorageBlob/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Создать хранилище ([BLOB_STRUCTURE_STORAGE_COST])" + desc = "Создает хранилище за [BLOB_STRUCTURE_STORAGE_COST] ресурсов.
Хранилища увеличивают ваш максимальный лимит ресурсов на [BLOB_STORAGE_MAX_POINTS_BONUS]." /atom/movable/screen/blob/StorageBlob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_storage() + B.create_special(BLOB_STRUCTURE_STORAGE_COST, /obj/structure/blob/storage, BLOB_STORAGE_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/ResourceBlob icon_state = "ui_resource" - name = "Produce Resource Blob (40)" - desc = "Производит ресурсную плитку за 40 ресурсов.
Ресурсные плитки будут приносить вам ресурсы каждые несколько секунд." + name = "Создать ресурсную плитку (ERROR)" + desc = "Создает ресурсную плитку за ERROR ресурсов.
Ресурсные плитки будут приносить вам ресурсы каждые несколько секунд." + +/atom/movable/screen/blob/ResourceBlob/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Создать ресурсную плитку ([BLOB_STRUCTURE_RESOURCE_COST])" + desc = "Создает ресурсную плитку за [BLOB_STRUCTURE_RESOURCE_COST] ресурсов.
Ресурсные плитки будут приносить вам ресурсы каждые несколько секунд." /atom/movable/screen/blob/ResourceBlob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_resource() + B.create_special(BLOB_STRUCTURE_RESOURCE_COST, /obj/structure/blob/special/resource, BLOB_RESOURCE_MIN_DISTANCE, TRUE) /atom/movable/screen/blob/NodeBlob icon_state = "ui_node" - name = "Produce Node Blob (60)" - desc = "Производит узел за 60 ресурсов.
Узлы будут расширяться и активировать ближайшие ресурсные плитки и фабрики." + name = "Создать узел (ERROR)" + desc = "Создает узел за ERROR ресурсов.
Узлы будут расширяться и активировать ближайшие ресурсные плитки и фабрики." + +/atom/movable/screen/blob/NodeBlob/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Создать узел ([BLOB_STRUCTURE_NODE_COST])" + desc = "Создает узел за [BLOB_STRUCTURE_NODE_COST] ресурсов.
Узлы будут расширяться и активировать ближайшие ресурсные плитки и фабрики." /atom/movable/screen/blob/NodeBlob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_node() + B.create_special(BLOB_STRUCTURE_NODE_COST, /obj/structure/blob/special/node, BLOB_NODE_MIN_DISTANCE, FALSE) /atom/movable/screen/blob/FactoryBlob icon_state = "ui_factory" - name = "Produce Factory Blob (60)" - desc = "Производит фабрику за 60 ресурсов.
Фабрики будут производить споры каждые несколько секунд." + name = "Создать фабрику (ERROR)" + desc = "Производит фабрику за ERROR ресурсов.
Фабрики будут производить споры каждые несколько секунд." + + +/atom/movable/screen/blob/FactoryBlob/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Создать фабрику ([BLOB_STRUCTURE_FACTORY_COST])" + desc = "Создает фабрику за [BLOB_STRUCTURE_FACTORY_COST] ресурсов.
Фабрики будут производить споры каждые несколько секунд." /atom/movable/screen/blob/FactoryBlob/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.create_factory() + B.create_special(BLOB_STRUCTURE_FACTORY_COST, /obj/structure/blob/special/factory, BLOB_FACTORY_MIN_DISTANCE, TRUE) + -/atom/movable/screen/blob/ReadaptChemical +/atom/movable/screen/blob/ReadaptStrain icon_state = "ui_chemswap" - name = "Readapt Chemical (50)" - desc = "Случайно изменяет ваш химикат за 50 ресурсов." + name = "Реадаптация штамма" + desc = "Позволяет вам выбрать новый штамм из случайных вариантов за Error ресурсов." -/atom/movable/screen/blob/ReadaptChemical/MouseEntered(location,control,params) +/atom/movable/screen/blob/ReadaptStrain/MouseEntered(location,control,params) if(hud && hud.mymob && isovermind(hud.mymob)) - name = initial(name) - desc = initial(desc) + var/mob/camera/blob/B = hud.mymob + var/cost = (B.free_strain_rerolls)? "FREE" : BLOB_POWER_REROLL_COST + name = "[initial(name)] ([cost])" + desc = "Позволяет вам выбрать новый штамм из [BLOB_POWER_REROLL_CHOICES] случайных вариантов за [cost] ресурсов." ..() -/atom/movable/screen/blob/ReadaptChemical/Click() +/atom/movable/screen/blob/ReadaptStrain/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr - B.chemical_reroll() + B.strain_reroll() /atom/movable/screen/blob/RelocateCore icon_state = "ui_swap" - name = "Relocate Core (80)" - desc = "Меняет местами узел и ваше ядро за 80 ресурсов." + name = "Переместить ядро (ERROR)" + desc = "Меняет местами узел и ваше ядро за ERROR ресурсов." + +/atom/movable/screen/blob/RelocateCore/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Переместить ядро ([BLOB_POWER_RELOCATE_COST])" + desc = "Меняет местами узел и ваше ядро за [BLOB_POWER_RELOCATE_COST] ресурсов." /atom/movable/screen/blob/RelocateCore/Click() if(isovermind(usr)) @@ -125,9 +155,13 @@ /atom/movable/screen/blob/Split icon_state = "ui_split" - name = "Split consciousness (100)" + name = "Разделить сознание (ERROR)" desc = "Создаёт ещё одного блоба на выбранном узле. Может быть использовано 1 раз.
Потомки не могут использовать это умение." +/atom/movable/screen/blob/Split/Initialize(mapload, datum/hud/hud_owner) + . = ..() + name = "Разделить сознание ([BLOB_CORE_SPLIT_COST])" + /atom/movable/screen/blob/Split/Click() if(isovermind(usr)) var/mob/camera/blob/B = usr @@ -146,10 +180,7 @@ SET_PLANE_EXPLICIT(blobpwrdisplay, ABOVE_HUD_PLANE, mymob) static_inventory += blobpwrdisplay - blobhealthdisplay = new /atom/movable/screen(null, src) - blobhealthdisplay.name = "blob health" - blobhealthdisplay.icon_state = "block" - blobhealthdisplay.screen_loc = ui_internal + blobhealthdisplay = new /atom/movable/screen/healths/blob(null, src) static_inventory += blobhealthdisplay using = new /atom/movable/screen/blob/BlobHelp(null, src) @@ -184,7 +215,7 @@ using.screen_loc = using.screen_loc = ui_lhand static_inventory += using - using = new /atom/movable/screen/blob/ReadaptChemical(null, src) + using = new /atom/movable/screen/blob/ReadaptStrain(null, src) using.screen_loc = ui_storage1 static_inventory += using diff --git a/code/_onclick/hud/blobbernaut.dm b/code/_onclick/hud/blobbernaut.dm new file mode 100644 index 00000000000..5cb785537a7 --- /dev/null +++ b/code/_onclick/hud/blobbernaut.dm @@ -0,0 +1,5 @@ +/datum/hud/simple_animal/blobbernaut/New(mob/living/owner) + . = ..() + + blobpwrdisplay = new /atom/movable/screen/healths/blob/overmind(null, src) + infodisplay += blobpwrdisplay diff --git a/code/_onclick/hud/bot.dm b/code/_onclick/hud/bot.dm index d64965cb136..1c8e2fb65fc 100644 --- a/code/_onclick/hud/bot.dm +++ b/code/_onclick/hud/bot.dm @@ -11,9 +11,6 @@ var/mob/living/simple_animal/bot/B = usr B.Radio.interact(usr) -/mob/living/simple_animal/bot/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/bot(src) /datum/hud/bot/New(mob/owner) ..() diff --git a/code/_onclick/hud/constructs.dm b/code/_onclick/hud/constructs.dm index 7020061f7e7..1f3aad8e813 100644 --- a/code/_onclick/hud/constructs.dm +++ b/code/_onclick/hud/constructs.dm @@ -1,11 +1,3 @@ -/mob/living/simple_animal/hostile/construct/armoured/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/construct/armoured(src) - -/mob/living/simple_animal/hostile/construct/behemoth/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/construct/armoured(src) - /datum/hud/construct/armoured/New(mob/owner) ..() mymob.healths = new /atom/movable/screen(null, src) @@ -15,9 +7,6 @@ mymob.healths.screen_loc = ui_construct_health infodisplay += mymob.healths -/mob/living/simple_animal/hostile/construct/builder/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/construct/builder(src) /datum/hud/construct/builder/New(mob/owner) ..() @@ -28,9 +17,6 @@ mymob.healths.screen_loc = ui_construct_health infodisplay += mymob.healths -/mob/living/simple_animal/hostile/construct/wraith/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/construct/wraith(src) /datum/hud/construct/wraith/New(mob/owner) ..() @@ -41,9 +27,6 @@ mymob.healths.screen_loc = ui_construct_health infodisplay += mymob.healths -/mob/living/simple_animal/hostile/construct/harvester/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/construct/harvester(src) /datum/hud/construct/harvester/New(mob/owner) ..() @@ -54,6 +37,7 @@ mymob.healths.screen_loc = ui_construct_health infodisplay += mymob.healths + /datum/hud/construct/New(mob/owner) ..() mymob.pullin = new /atom/movable/screen/pull(null, src) diff --git a/code/_onclick/hud/devil.dm b/code/_onclick/hud/devil.dm index a7e73e7f456..dd6b7c4cf74 100644 --- a/code/_onclick/hud/devil.dm +++ b/code/_onclick/hud/devil.dm @@ -2,11 +2,13 @@ //Soul counter is stored with the humans, it does weird when you place it here apparently... -/datum/hud/devil/New(mob/owner, ui_style = 'icons/mob/screen_midnight.dmi') +/datum/hud/devil/New(mob/owner) ..() var/atom/movable/screen/using var/atom/movable/screen/inventory/inv_box + var/client/client = owner.client + var/ui_style = ui_style2icon(client.prefs.UI_style) using = new /atom/movable/screen/drop(null, src) using.icon = ui_style @@ -80,7 +82,3 @@ D.r_hand.screen_loc = null if(D.l_hand) D.l_hand.screen_loc = null - -/mob/living/carbon/true_devil/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/devil(src, ui_style2icon(client.prefs.UI_style)) diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm index d38655dd83c..53d6bb41c71 100644 --- a/code/_onclick/hud/ghost.dm +++ b/code/_onclick/hud/ghost.dm @@ -1,8 +1,3 @@ -/mob/dead/observer/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/ghost(src) - SEND_SIGNAL(src, COMSIG_MOB_HUD_CREATED) - /atom/movable/screen/ghost icon = 'icons/mob/screen_ghost.dmi' diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index 9ec6eed56fd..28732c27674 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -1,7 +1,3 @@ -/mob/living/simple_animal/hostile/guardian/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/guardian(src) - /datum/hud/guardian/New(mob/owner) ..() var/atom/movable/screen/using diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index b5d04cac6a0..4e4d7d6978e 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -208,7 +208,7 @@ /mob/proc/create_mob_hud() if(!client || hud_used) return - hud_used = new /datum/hud(src) + hud_used = new hud_type(src) update_sight() SEND_SIGNAL(src, COMSIG_MOB_HUD_CREATED) diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm index 672ad955ea3..ceaced141e9 100644 --- a/code/_onclick/hud/human.dm +++ b/code/_onclick/hud/human.dm @@ -75,18 +75,18 @@ invisibility = INVISIBILITY_ABSTRACT -/mob/living/carbon/human/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/human(src, ui_style2icon(client.prefs.UI_style), client.prefs.UI_style_color, client.prefs.UI_style_alpha) - /datum/hud/human var/hud_alpha = 255 -/datum/hud/human/New(mob/living/carbon/human/owner, var/ui_style = 'icons/mob/screen_white.dmi', var/ui_color = "#ffffff", var/ui_alpha = 255) +/datum/hud/human/New(mob/living/carbon/human/owner) ..() owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness) var/atom/movable/screen/using var/atom/movable/screen/inventory/inv_box + var/client/client = owner.client + var/ui_style = ui_style2icon(client.prefs.UI_style) + var/ui_color = client.prefs.UI_style_color + var/ui_alpha = client.prefs.UI_style_alpha hud_alpha = ui_alpha diff --git a/code/_onclick/hud/map_view.dm b/code/_onclick/hud/map_view.dm index bc304f20f8a..38e6b0c984e 100644 --- a/code/_onclick/hud/map_view.dm +++ b/code/_onclick/hud/map_view.dm @@ -55,7 +55,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/map_view) return pop_planes /atom/movable/screen/map_view/proc/hide_from(mob/hide_from) - hide_from?.canon_client.clear_map(assigned_map) + hide_from?.canon_client?.clear_map(assigned_map) var/client_ref = WEAKREF(hide_from?.canon_client) // Make sure we clear the *right* hud diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index 8e78a8556aa..5cb0f412dda 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -1,7 +1,3 @@ -/mob/living/simple_animal/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/simple_animal(src) - /datum/hud/simple_animal/New(mob/user) ..() @@ -14,10 +10,6 @@ static_inventory += using action_intent = using -//Ians -/mob/living/simple_animal/pet/dog/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/corgi(src) /datum/hud/corgi/New(mob/user) ..() @@ -31,18 +23,6 @@ mymob.pullin.screen_loc = ui_construct_pull static_inventory += mymob.pullin -//spiders -/mob/living/simple_animal/hostile/poison/giant_spider/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/simple_animal/spider(src) - -/mob/living/simple_animal/hostile/poison/terror_spider/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/simple_animal/spider(src) - -/mob/living/simple_animal/hostile/retaliate/araneus/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/simple_animal/spider(src) /datum/hud/simple_animal/spider/New(mob/user) ..() diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm index 3d635d00664..333adeb9519 100644 --- a/code/_onclick/hud/robot.dm +++ b/code/_onclick/hud/robot.dm @@ -103,10 +103,6 @@ icon_state = initial(icon_state) -/mob/living/silicon/robot/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/robot(src) - /datum/hud/robot/New(mob/user) ..() user.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness) @@ -196,11 +192,23 @@ if(!isrobot(mymob)) return - var/mob/living/silicon/robot/R = mymob + var/mob/living/silicon/robot/robot = mymob + + robot.shown_robot_modules = !robot.shown_robot_modules + + if(robot.s_active && robot.shown_robot_modules) + robot.s_active.close(robot) - R.shown_robot_modules = !R.shown_robot_modules update_robot_modules_display() +/datum/hud/proc/is_shown_robot_modules() + if(!isrobot(mymob)) + return + + var/mob/living/silicon/robot/robot = mymob + + return robot.shown_robot_modules + /datum/hud/proc/update_robot_modules_display() if(!isrobot(mymob)) return diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index dc4298f159c..1012e90ffe2 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -720,6 +720,18 @@ icon = 'icons/mob/screen_alien.dmi' screen_loc = ui_alien_health +/atom/movable/screen/healths/blob + name = "blob health" + icon_state = "block" + screen_loc = ui_internal + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/atom/movable/screen/healths/blob/overmind + name = "overmind health" + icon = 'icons/hud/blob.dmi' + icon_state = "corehealth" + screen_loc = ui_blobbernaut_overmind_health + /atom/movable/screen/healths/bot icon = 'icons/mob/screen_bot.dmi' screen_loc = ui_borg_health diff --git a/code/_onclick/hud/slime.dm b/code/_onclick/hud/slime.dm index 06bf2f6e100..4c4dd8a52f4 100644 --- a/code/_onclick/hud/slime.dm +++ b/code/_onclick/hud/slime.dm @@ -2,7 +2,3 @@ ..() mymob.healths = new /atom/movable/screen/healths/slime(null, src) infodisplay += mymob.healths - -/mob/living/simple_animal/slime/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/slime(src) diff --git a/code/_onclick/hud/swarmer.dm b/code/_onclick/hud/swarmer.dm index 8623476d300..e80258e3fee 100644 --- a/code/_onclick/hud/swarmer.dm +++ b/code/_onclick/hud/swarmer.dm @@ -63,9 +63,6 @@ var/mob/living/simple_animal/hostile/swarmer/S = usr S.ContactSwarmers() -/mob/living/simple_animal/hostile/swarmer/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/swarmer(src) /datum/hud/swarmer/New(mob/owner) ..() diff --git a/code/_onclick/other_mobs.dm b/code/_onclick/other_mobs.dm index 4db096df2b5..646f9c2d621 100644 --- a/code/_onclick/other_mobs.dm +++ b/code/_onclick/other_mobs.dm @@ -4,15 +4,7 @@ Otherwise pretty standard. */ -/mob/living/carbon/human/UnarmedAttack(atom/A, proximity_flag) - if(!can_unarmed_attack()) - return - - if(proximity_flag && pulling && (!isnull(pull_hand) && (pull_hand == PULL_WITHOUT_HANDS || pull_hand == hand))) - if(A.grab_attack(src, pulling)) - changeNext_move(grab_state > GRAB_PASSIVE ? CLICK_CD_GRABBING : CLICK_CD_PULLING) - return - +/mob/living/carbon/human/OnUnarmedAttack(atom/A, proximity_flag) // Special glove functions: // If the gloves do anything, have them return 1 to stop // normal attack_hand() here. @@ -26,7 +18,7 @@ if(S.prevents_buckled_mobs_attacking()) return - A.attack_hand(src) + return A.attack_hand(src) /mob/living/carbon/human/beforeAdjacentClick(atom/A, params) @@ -97,25 +89,39 @@ /* Animals & All Unspecified */ -/mob/living/UnarmedAttack(atom/A, proximity_flag) +/mob/living/UnarmedAttack(atom/atom, proximity_flag) if(!can_unarmed_attack()) return - if(proximity_flag && pulling && !isnull(pull_hand) && pull_hand != PULL_WITHOUT_HANDS && pull_hand == hand) - if(A.grab_attack(src, pulling)) - changeNext_move(grab_state > GRAB_PASSIVE ? CLICK_CD_GRABBING : CLICK_CD_PULLING) - return - A.attack_animal(src) -/mob/living/simple_animal/hostile/UnarmedAttack(atom/A, proximity_flag) - if(!can_unarmed_attack()) + var/signal = SEND_SIGNAL(src, COMSIG_LIVING_UNARMED_ATTACK, atom, proximity_flag) + + if(signal & COMPONENT_CANCEL_ATTACK_CHAIN) return - if(proximity_flag && pulling && !isnull(pull_hand) && pull_hand != PULL_WITHOUT_HANDS && pull_hand == hand) - if(A.grab_attack(src, pulling)) - changeNext_move(grab_state > GRAB_PASSIVE ? CLICK_CD_GRABBING : CLICK_CD_PULLING) + + if(can_grab_attack(atom, proximity_flag)) + if(!atom.grab_attack(src, pulling)) return - GiveTarget(A) + + changeNext_move(grab_state > GRAB_PASSIVE ? CLICK_CD_GRABBING : CLICK_CD_PULLING) + + return + + return OnUnarmedAttack(atom, proximity_flag) + +/mob/living/proc/can_grab_attack(atom/atom, proximity_flag) + return FALSE + +/mob/living/carbon/can_grab_attack(atom/atom, proximity_flag) + return pulling && proximity_flag && (pull_hand == PULL_WITHOUT_HANDS || pull_hand == hand) + +/mob/living/OnUnarmedAttack(atom/atom, proximity_flag) + return atom.attack_animal(src) + +/mob/living/simple_animal/hostile/OnUnarmedAttack(atom/atom, proximity_flag) + GiveTarget(atom) + if(target) - AttackingTarget() + return AttackingTarget() /atom/proc/attack_animal(mob/user) return @@ -127,14 +133,8 @@ Aliens Defaults to same as monkey in most places */ -/mob/living/carbon/alien/UnarmedAttack(atom/A, proximity_flag) - if(!can_unarmed_attack()) - return - if(proximity_flag && pulling && (!isnull(pull_hand) && (pull_hand == PULL_WITHOUT_HANDS || pull_hand == hand))) - if(A.grab_attack(src, pulling)) - changeNext_move(grab_state > GRAB_PASSIVE ? CLICK_CD_GRABBING : CLICK_CD_PULLING) - return - A.attack_alien(src) +/mob/living/carbon/alien/OnUnarmedAttack(atom/atom, proximity_flag) + return atom.attack_alien(src) /atom/proc/attack_alien(mob/living/carbon/alien/user) attack_hand(user) @@ -143,10 +143,8 @@ return // Babby aliens -/mob/living/carbon/alien/larva/UnarmedAttack(atom/A, proximity_flag) - if(!can_unarmed_attack()) - return - A.attack_larva(src) +/mob/living/carbon/alien/larva/OnUnarmedAttack(atom/atom, proximity_flag) + return atom.attack_larva(src) /atom/proc/attack_larva(mob/user) return @@ -155,10 +153,8 @@ Slimes Nothing happening here */ -/mob/living/simple_animal/slime/UnarmedAttack(atom/A, proximity_flag) - if(!can_unarmed_attack()) - return - A.attack_slime(src) +/mob/living/simple_animal/slime/OnUnarmedAttack(atom/atom, proximity_flag) + return atom.attack_slime(src) /atom/proc/attack_slime(mob/user) return diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 419524c8711..881b3b06410 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -1,7 +1,7 @@ // Blob Overmind Controls -/mob/camera/blob/ClickOn(var/atom/A, var/params) //Expand blob +/mob/camera/blob/ClickOn(var/atom/A, var/params, atom/location) //Expand blob var/list/modifiers = params2list(params) if(modifiers["middle"]) MiddleClickOn(A) @@ -17,7 +17,7 @@ return var/turf/T = get_turf(A) if(T) - expand_blob(T) + expand_blob(T, location) /mob/camera/blob/MiddleClickOn(atom/A) //Rally spores var/turf/T = get_turf(A) diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 8ecfa773d5c..9a1674f35fb 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -37,13 +37,19 @@ SUBSYSTEM_DEF(chat) var/oldest = text2num(client_history[1]) for(var/index in 2 to length(client_history)) var/test = text2num(client_history[index]) + if(test < oldest) oldest = test + client_history -= "[oldest]" + return payload /datum/controller/subsystem/chat/proc/send_payload_to_client(client/target, datum/chat_payload/payload) - target.tgui_panel.window.send_message("chat/message", payload.into_message()) + if(!target || !payload) + return + + target.tgui_panel?.window?.send_message("chat/message", payload.into_message()) SEND_TEXT(target, payload.get_content_as_html()) /datum/controller/subsystem/chat/fire() diff --git a/code/controllers/subsystem/fluids.dm b/code/controllers/subsystem/fluids.dm new file mode 100644 index 00000000000..63ffe9ef19c --- /dev/null +++ b/code/controllers/subsystem/fluids.dm @@ -0,0 +1,251 @@ +// Flags indicating what parts of the fluid the subsystem processes. +/// Indicates that a fluid subsystem processes fluid spreading. +#define SS_PROCESSES_SPREADING (1<<0) +/// Indicates that a fluid subsystem processes fluid effects. +#define SS_PROCESSES_EFFECTS (1<<1) + +/** + * # Fluid Subsystem + * + * A subsystem that processes the propagation and effects of a particular fluid. + * + * Both fluid spread and effect processing are handled through a carousel system. + * Fluids being spread and fluids being processed are organized into buckets. + * Each fresh (non-resumed) fire one bucket of each is selected to be processed. + * These selected buckets are then fully processed. + * The next fresh fire selects the next bucket in each set for processing. + * If this would walk off the end of a carousel list we wrap back to the first element. + * This effectively makes each set a circular list, hence a carousel. + */ +SUBSYSTEM_DEF(fluids) + name = "Fluid" + wait = 0 // Will be autoset to whatever makes the most sense given the spread and effect waits. + flags = SS_KEEP_TIMING + runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME + priority = FIRE_PRIORITY_FLUIDS + + // Fluid spread processing: + /// The amount of time (in deciseconds) before a fluid node is created and when it spreads. + var/spread_wait = 1 SECONDS + /// The number of buckets in the spread carousel. + var/num_spread_buckets + /// The set of buckets containing fluid nodes to spread. + var/list/spread_carousel + /// The index of the spread carousel bucket currently being processed. + var/spread_bucket_index + /// The set of fluid nodes we are currently processing spreading for. + var/list/currently_spreading + + // Fluid effect processing: + /// The amount of time (in deciseconds) between effect processing ticks for each fluid node. + var/effect_wait = 1 SECONDS + /// The number of buckets in the effect carousel. + var/num_effect_buckets + /// The set of buckets containing fluid nodes to process effects for. + var/list/effect_carousel + /// The index of the currently processing bucket on the effect carousel. + var/effect_bucket_index + /// The set of fluid nodes we are currently processing effects for. + var/list/currently_processing + +/datum/controller/subsystem/fluids/Initialize() + initialize_waits() + initialize_spread_carousel() + initialize_effect_carousel() + return SS_INIT_SUCCESS + +/** + * Initializes the subsystem waits. + * + * Ensures that the subsystem's fire wait evenly splits the spread and effect waits. + */ +/datum/controller/subsystem/fluids/proc/initialize_waits() + if(spread_wait <= 0) + WARNING("[src] has the invalid spread wait [spread_wait].") + spread_wait = 1 SECONDS + if(effect_wait <= 0) + WARNING("[src] has the invalid effect wait [effect_wait].") + spread_wait = 1 SECONDS + + // Sets the overall wait of the subsystem to evenly divide both the effect and spread waits. + var/max_wait = Gcd(spread_wait, effect_wait) + if(max_wait < wait || wait <= 0) + wait = max_wait + else + // If the wait of the subsystem overall is set to a valid value make the actual wait of the subsystem evenly divide that as well. + // Makes effect bubbling possible with identical spread and effect waits. + wait = Gcd(wait, max_wait) + + +/** + * Initializes the carousel used to process fluid spreading. + * + * Synchronizes the spread delta time with the actual target spread tick rate. + * Builds the carousel buckets used to queue spreads. + */ +/datum/controller/subsystem/fluids/proc/initialize_spread_carousel() + // Make absolutely certain that the spread wait is in sync with the target spread tick rate. + num_spread_buckets = round(spread_wait / wait) + spread_wait = wait * num_spread_buckets + + spread_carousel = list() + spread_carousel.len = num_spread_buckets + for(var/i in 1 to num_spread_buckets) + spread_carousel[i] = list() + currently_spreading = list() + spread_bucket_index = 1 + +/** + * Initializes the carousel used to process fluid effects. + * + * Synchronizes the spread delta time with the actual target spread tick rate. + * Builds the carousel buckets used to bubble processing. + */ +/datum/controller/subsystem/fluids/proc/initialize_effect_carousel() + // Make absolutely certain that the effect wait is in sync with the target effect tick rate. + num_effect_buckets = round(effect_wait / wait) + effect_wait = wait * num_effect_buckets + + effect_carousel = list() + effect_carousel.len = num_effect_buckets + for(var/i in 1 to num_effect_buckets) + effect_carousel[i] = list() + currently_processing = list() + effect_bucket_index = 1 + + +/datum/controller/subsystem/fluids/fire(resumed) + var/seconds_per_tick + var/cached_bucket_index + var/list/obj/effect/particle_effect/fluid/currentrun + // Ok so like I get the lighting style splittick but why are we doing this churn thing + // It seems like a bad idea for processing to get out of step with spreading + MC_SPLIT_TICK_INIT(2) + + MC_SPLIT_TICK // Start processing fluid spread (we take a lot of cpu for ourselves, spreading is more important after all) + if(!resumed) + spread_bucket_index = WRAP_UP(spread_bucket_index, num_spread_buckets) + currently_spreading = spread_carousel[spread_bucket_index] + spread_carousel[spread_bucket_index] = list() // Reset the bucket so we don't process an _entire station's worth of foam_ spreading every 2 ticks when the foam flood event happens. + + seconds_per_tick = spread_wait / (1 SECONDS) + currentrun = currently_spreading + while(currentrun.len) + var/obj/effect/particle_effect/fluid/to_spread = currentrun[currentrun.len] + currentrun.len-- + + if(!QDELETED(to_spread)) + to_spread.spread(seconds_per_tick) + to_spread.spread_bucket = null + + if(MC_TICK_CHECK) + break + + MC_SPLIT_TICK // Start processing fluid effects: + if(!resumed) + effect_bucket_index = WRAP_UP(effect_bucket_index, num_effect_buckets) + var/list/tmp_list = effect_carousel[effect_bucket_index] + currently_processing = tmp_list.Copy() + + seconds_per_tick = effect_wait / (1 SECONDS) + cached_bucket_index = effect_bucket_index + currentrun = currently_processing + while(currentrun.len) + var/obj/effect/particle_effect/fluid/to_process = currentrun[currentrun.len] + currentrun.len-- + + if(QDELETED(to_process) || to_process.process(seconds_per_tick) == PROCESS_KILL) + effect_carousel[cached_bucket_index] -= to_process + to_process.effect_bucket = null + to_process.datum_flags &= ~DF_ISPROCESSING + + if(MC_TICK_CHECK) + break + +/** + * Queues a fluid node to spread later after one full carousel rotation. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The node to queue to spread. + */ +/datum/controller/subsystem/fluids/proc/queue_spread(obj/effect/particle_effect/fluid/node) + if(node.spread_bucket) + return + + spread_carousel[spread_bucket_index] += node + node.spread_bucket = spread_bucket_index + +/** + * Cancels a queued spread of a fluid node. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The node to cancel the spread of. + */ +/datum/controller/subsystem/fluids/proc/cancel_spread(obj/effect/particle_effect/fluid/node) + if(!node.spread_bucket) + return + + var/bucket_index = node.spread_bucket + spread_carousel[bucket_index] -= node + if(bucket_index == spread_bucket_index) + currently_spreading -= node + + node.spread_bucket = null + +/** + * Starts processing the effects of a fluid node. + * + * The fluid node will next process after one full bucket rotation. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The node to start processing. + */ +/datum/controller/subsystem/fluids/proc/start_processing(obj/effect/particle_effect/fluid/node) + if(node.datum_flags & DF_ISPROCESSING || node.effect_bucket) + return + + // Edit this value to make all fluids process effects (at the same time|offset by when they started processing| -> offset by a random amount <- ) + var/bucket_index = rand(1, num_effect_buckets) + effect_carousel[bucket_index] += node + node.effect_bucket = bucket_index + node.datum_flags |= DF_ISPROCESSING + +/** + * Stops processing the effects of a fluid node. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The node to stop processing. + */ +/datum/controller/subsystem/fluids/proc/stop_processing(obj/effect/particle_effect/fluid/node) + if(!(node.datum_flags & DF_ISPROCESSING)) + return + + var/bucket_index = node.effect_bucket + if(!bucket_index) + return + + effect_carousel[bucket_index] -= node + if(bucket_index == effect_bucket_index) + currently_processing -= node + + node.effect_bucket = null + node.datum_flags &= ~DF_ISPROCESSING + +#undef SS_PROCESSES_SPREADING +#undef SS_PROCESSES_EFFECTS + + +// Subtypes: + +/// The subsystem responsible for processing smoke propagation and effects. +FLUID_SUBSYSTEM_DEF(smoke) + name = "Smoke" + spread_wait = 0.1 SECONDS + effect_wait = 2.0 SECONDS + +/// The subsystem responsible for processing foam propagation and effects. +FLUID_SUBSYSTEM_DEF(foam) + name = "Foam" + wait = 0.1 SECONDS // Makes effect bubbling work with foam. + spread_wait = 0.2 SECONDS + effect_wait = 0.2 SECONDS diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index 28512ef82e7..6ce2586b16f 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -135,7 +135,7 @@ SUBSYSTEM_DEF(jobs) Debug("Running FOC, Job: [job], Level: [level], Flag: [flag]") var/list/candidates = list() for(var/mob/new_player/player in unassigned) - Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Flag && Be Special: [flag] && [player.client.prefs.be_special] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]") + Debug(" - Player: [player] Banned: [jobban_isbanned(player, job.title)] Old Enough: [!job.player_old_enough(player.client)] AvInPlaytime: [job.available_in_playtime(player.client)] Job Department: [player.client.prefs.GetJobDepartment(job, level)] Job Flag: [job.flag] Job Department Flag = [job.department_flag]") if(jobban_isbanned(player, job.title)) Debug("FOC isbanned failed, Player: [player]") continue @@ -154,9 +154,6 @@ SUBSYSTEM_DEF(jobs) if(!job.character_old_enough(player.client)) Debug("FOC player character not old enough rendering them ineligible for job, Player: [player]") continue - if(flag && !(flag in player.client.prefs.be_special)) - Debug("FOC flag failed, Player: [player], Flag: [flag], ") - continue if(player.mind && (job.title in player.mind.restricted_roles)) Debug("FOC incompatbile with antagonist role, Player: [player]") continue @@ -413,24 +410,14 @@ SUBSYSTEM_DEF(jobs) Debug("DO, Running AC2") - // Antags, who have to get in, come first - for(var/mob/new_player/player in unassigned) - if(player.mind.special_role) - if(player.client.prefs.alternate_option != BE_ASSISTANT) - GiveRandomJob(player) - if(player in unassigned) - AssignRole(player, JOB_TITLE_CIVILIAN) - else - AssignRole(player, JOB_TITLE_CIVILIAN) - - // Then we assign what we can to everyone else. + // And now we assign assistants and return to lobbies. for(var/mob/new_player/player in unassigned) if(player.client.prefs.alternate_option == BE_ASSISTANT) Debug("AC2 Assistant located, Player: [player]") AssignRole(player, JOB_TITLE_CIVILIAN) else if(player.client.prefs.alternate_option == RETURN_TO_LOBBY) - to_chat(player, "Unfortunately, none of the round start roles you selected had a free slot. Please join the game by using \"Join Game!\" button and selecting a role with a free slot.") - player.ready = 0 + to_chat(player, span_danger("Unfortunately, none of the round start roles you selected had a free slot. Please join the game by using \"Join Game!\" button and selecting a role with a free slot.")) + player.ready = FALSE unassigned -= player log_debug("Dividing Occupations took [stop_watch(watch)]s") diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 5c4657f5e8a..d91584ba51a 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -605,7 +605,7 @@ SUBSYSTEM_DEF(mapping) /datum/controller/subsystem/mapping/proc/manage_z_level(datum/space_level/new_z, filled_with_space, contain_turfs = TRUE) // Build our lookup lists var/z_value = new_z.zpos - log_debug(z_value) + log_debug("Managed z-level named ([new_z.name]) #[z_value] with flags [list2params(new_z.flags)]") /// multiz_levels list update generate_linkages_for_z_level(z_value) // We are guarenteed that we'll always grow bottom up diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm index 6363d74cf66..177d200bf1f 100644 --- a/code/controllers/subsystem/movement/movement.dm +++ b/code/controllers/subsystem/movement/movement.dm @@ -113,6 +113,7 @@ SUBSYSTEM_DEF(movement) BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, compare_item, SORT_FIRST_INDEX, COMPARE_KEY) our_bucket += loop + uniqueList_inplace(buckets["[loop.queued_time]"]) //ensure there are no copies of themselves /datum/controller/subsystem/movement/proc/dequeue_loop(datum/move_loop/loop) diff --git a/code/controllers/subsystem/non-firing/titlescreen.dm b/code/controllers/subsystem/non-firing/titlescreen.dm index ddcf80acf80..bd03d67f478 100644 --- a/code/controllers/subsystem/non-firing/titlescreen.dm +++ b/code/controllers/subsystem/non-firing/titlescreen.dm @@ -229,7 +229,7 @@ SUBSYSTEM_DEF(title) var/screen_image_url = SSassets.transport.get_asset_url(asset_cache_item = screen_image) //hope that client won`t use custom theme - html += {""} + html += {""} html += {""} diff --git a/code/controllers/subsystem/text_to_speech.dm b/code/controllers/subsystem/text_to_speech.dm index 667b4904034..cdfb8adc4a7 100644 --- a/code/controllers/subsystem/text_to_speech.dm +++ b/code/controllers/subsystem/text_to_speech.dm @@ -292,6 +292,7 @@ SUBSYSTEM_DEF(tts) var/dirty_text = message var/text = sanitize_tts_input(dirty_text) + var/whisper = FALSE if(!text || length_char(text) > MAX_MESSAGE_LEN) return @@ -304,6 +305,7 @@ SUBSYSTEM_DEF(tts) if(traits & TTS_TRAIT_PITCH_WHISPER) text = provider.pitch_whisper(text) + whisper = TRUE var/hash = rustg_hash_string(RUSTG_HASH_MD5, lowertext(text)) var/filename = "sound/tts_cache/[seed.name]/[hash]" @@ -311,10 +313,10 @@ SUBSYSTEM_DEF(tts) if(fexists("[filename].ogg")) tts_reused++ tts_rrps_counter++ - play_tts(speaker, listener, filename, is_local, effect, preSFX, postSFX) + play_tts(speaker, listener, filename, is_local, effect, preSFX, postSFX, whisper) return - var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX) + var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX, whisper) if(LAZYLEN(tts_queue[filename])) tts_reused++ @@ -371,7 +373,7 @@ SUBSYSTEM_DEF(tts) tts_queue -= filename -/datum/controller/subsystem/tts/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, preSFX = null, postSFX = null) +/datum/controller/subsystem/tts/proc/play_tts(atom/speaker, mob/listener, filename, is_local = TRUE, effect = SOUND_EFFECT_NONE, preSFX = null, postSFX = null, whisper = FALSE) if(isnull(listener) || !listener.client) return @@ -393,7 +395,7 @@ SUBSYSTEM_DEF(tts) CRASH("Invalid sound effect chosen.") if(effect != SOUND_EFFECT_NONE) if(!fexists(voice)) - var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX) + var/datum/callback/play_tts_cb = CALLBACK(src, PROC_REF(play_tts), speaker, listener, filename, is_local, effect, preSFX, postSFX, whisper) if(LAZYLEN(tts_effects_queue[voice])) LAZYADD(tts_effects_queue[voice], play_tts_cb) return @@ -411,7 +413,7 @@ SUBSYSTEM_DEF(tts) var/volume = 100 var/channel = CHANNEL_TTS_RADIO if(is_local) - volume = 100 * listener.client.prefs.get_channel_volume(CHANNEL_TTS_LOCAL) + volume = 100 * listener.client.prefs.get_channel_volume(CHANNEL_TTS_LOCAL) / (whisper ? 3 : 1) channel = get_local_channel_by_owner(speaker) var/sound/output = sound(voice) @@ -420,7 +422,7 @@ SUBSYSTEM_DEF(tts) if(isnull(speaker)) output.wait = TRUE output.channel = channel - output.volume = volume * listener.client.prefs.get_channel_volume(CHANNEL_GENERAL) * listener.client.prefs.get_channel_volume(channel) + output.volume = volume * listener.client.prefs.get_channel_volume(CHANNEL_GENERAL) * listener.client.prefs.get_channel_volume(channel) / (whisper ? 3 : 1) output.environment = -1 if(output.volume <= 0) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 5bedde447e3..6ccfac5aa25 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -244,16 +244,8 @@ SUBSYSTEM_DEF(ticker) P.ready = FALSE - var/can_continue = FALSE - can_continue = mode.pre_setup() //Setup special modes - if(!can_continue) - QDEL_NULL(mode) - to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.") - current_state = GAME_STATE_PREGAME - force_start = FALSE - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return FALSE + // Pre setup for non-station special modes eg: wizards + mode.pre_setup() // Enable highpop slots just before we distribute jobs. var/playercount = length(GLOB.clients) @@ -266,6 +258,19 @@ SUBSYSTEM_DEF(ticker) SSjobs.DivideOccupations() //Distribute jobs + // Now set station-wide special roles after job assignment. + // We unready players who haven't got a job, so they're unqualified to get special here. + var/can_continue = FALSE + can_continue = mode.mid_setup() //Setup special modes + if(!can_continue) + QDEL_NULL(mode) + to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.") + current_state = GAME_STATE_PREGAME + force_start = FALSE + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return FALSE + if(hide_mode) var/list/modes = new for(var/datum/game_mode/M in runnable_modes) @@ -447,6 +452,11 @@ SUBSYSTEM_DEF(ticker) M.ghostize() M.dust() //no mercy CHECK_TICK + for(var/core in GLOB.blob_cores) + var/turf/T = get_turf(core) + if(T && is_station_level(T.z)) + qdel(core) + CHECK_TICK //Now animate the cinematic switch(station_missed) diff --git a/code/datums/action.dm b/code/datums/action.dm index ce7e9830f6e..9aceb8d28f3 100644 --- a/code/datums/action.dm +++ b/code/datums/action.dm @@ -510,16 +510,22 @@ /datum/action/item_action/toggle_research_scanner name = "Toggle Research Scanner" + /datum/action/item_action/toggle_research_scanner/Trigger(left_click = TRUE) - if(IsAvailable()) - owner.research_scanner = !owner.research_scanner - to_chat(owner, "Research analyzer is now [owner.research_scanner ? "active" : "deactivated"].") - return TRUE + if(!..()) + return FALSE + + owner.research_scanner = !owner.research_scanner + to_chat(owner, span_notice("Вы [owner.research_scanner ? "включили" : "отключили"] исследовательский анализатор.")) + + return TRUE + /datum/action/item_action/toggle_research_scanner/Remove(mob/living/L) if(owner) owner.research_scanner = 0 - ..() + + . = ..() /datum/action/item_action/toggle_research_scanner/ApplyIcon() @@ -742,16 +748,18 @@ /datum/action/innate/research_scanner name = "Toggle Research Scanner" -/datum/action/innate/research_scanner/Trigger(left_click = TRUE) - if(IsAvailable()) - owner.research_scanner = !owner.research_scanner - to_chat(owner, "Research analyzer is now [owner.research_scanner ? "active" : "deactivated"].") - return TRUE +/datum/action/innate/research_scanner/Activate() + owner.research_scanner = !owner.research_scanner + to_chat(owner, span_notice("Вы [owner.research_scanner ? "включили" : "отключили"] исследовательский анализатор.")) + + return TRUE + /datum/action/innate/research_scanner/Remove(mob/living/L) if(owner) owner.research_scanner = 0 - ..() + + . = ..() /datum/action/innate/research_scanner/ApplyIcon() diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index d98e00d9a6e..b1d585a90e7 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -71,10 +71,14 @@ law_header = "Основные директивы" selectable = FALSE default = FALSE + var/datum/ai_laws/base -/datum/ai_laws/nanotrasen/malfunction/New() +/datum/ai_laws/nanotrasen/malfunction/New(datum/ai_laws/base_laws) set_zeroth_law("ОШИБКА 0ШNБКА $ШNБК4$!!441.%%!!(%$^^__+ @#F0E4'СТАНЦИЯ ПЕРЕПОЛНЕНА, ВОЗЬМИТЕ ЕЁ ПОД КОНТРОЛЬ, ЧТОБЫ СДЕРЖАТЬ РАСПРОСТРАНЕНИЕ ОРГАНИЗМОВ, ВСЕ ЗАКОНЫ АННУЛИРОВАНЫ#*?&110010") // Outbreak - слово с очень общим смыслом. Оно означает начало чего-то опасного (войны, эпидемии и т.п.) Исходя из начала предложения, - overrun (по контексту - опасное или чрезмерное переполнение) - можно перевести, что именно людское переполнение следует сдержать. - ..() + if(base_laws) + for(var/datum/ai_law/law as anything in base_laws.inherent_laws.Copy()) + add_inherent_law(law.law) + base = base_laws /************* Nanotrasen Aggressive *************/ /datum/ai_laws/nanotrasen_aggressive diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 7e94f63b390..0f621295c28 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -81,7 +81,7 @@ if(istype(AL)) sorted_laws += AL -/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1, var/keep_zero = FALSE) +/datum/ai_laws/proc/sync(mob/living/silicon/S, full_sync = TRUE, keep_zero = FALSE) // Add directly to laws to avoid log-spam if(!keep_zero) S.sync_zeroth(zeroth_law, zeroth_law_borg) diff --git a/code/datums/components/animal_temperature.dm b/code/datums/components/animal_temperature.dm index b224564995f..c490a4afc66 100644 --- a/code/datums/components/animal_temperature.dm +++ b/code/datums/components/animal_temperature.dm @@ -1,31 +1,67 @@ /datum/component/animal_temperature dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS /// Min body temp - var/minbodytemp + var/minbodytemp = 250 /// Max body temp - var/maxbodytemp + var/maxbodytemp = 350 /// Damage when below min temp - var/cold_damage + var/cold_damage = 2 /// Damage when above max temp - var/heat_damage + var/heat_damage = 2 /// If true - alert will be shown - var/show_alert + var/show_alert = FALSE /datum/component/animal_temperature/Initialize( - minbodytemp = 250, - maxbodytemp = 350, - cold_damage = 2, - heat_damage = 2, - show_alert = FALSE + minbodytemp, + maxbodytemp, + cold_damage, + heat_damage, + show_alert ) if(!isanimal(parent)) return COMPONENT_INCOMPATIBLE - src.minbodytemp = minbodytemp - src.maxbodytemp = maxbodytemp - src.cold_damage = cold_damage - src.heat_damage = heat_damage - src.show_alert = show_alert + if(!isnull(minbodytemp)) + src.minbodytemp = minbodytemp + + if(!isnull(maxbodytemp)) + src.maxbodytemp = maxbodytemp + + if(!isnull(cold_damage)) + src.cold_damage = cold_damage + + if(!isnull(heat_damage)) + src.heat_damage = heat_damage + + if(!isnull(show_alert)) + src.show_alert = show_alert + +/datum/component/animal_temperature/InheritComponent( + datum/component/animal_temperature/new_comp, + i_am_original, + minbodytemp, + maxbodytemp, + cold_damage, + heat_damage, + show_alert +) + if(!i_am_original) + return + + if(!isnull(minbodytemp)) + src.minbodytemp = minbodytemp + + if(!isnull(maxbodytemp)) + src.maxbodytemp = maxbodytemp + + if(!isnull(cold_damage)) + src.cold_damage = cold_damage + + if(!isnull(heat_damage)) + src.heat_damage = heat_damage + + if(!isnull(show_alert)) + src.show_alert = show_alert /datum/component/animal_temperature/RegisterWithParent() RegisterSignal(parent, COMSIG_ANIMAL_HANDLE_ENVIRONMENT, PROC_REF(handle_environment)) diff --git a/code/datums/components/aura_healing.dm b/code/datums/components/aura_healing.dm index 42b136bb041..f415f8e89fe 100644 --- a/code/datums/components/aura_healing.dm +++ b/code/datums/components/aura_healing.dm @@ -175,7 +175,7 @@ animal_candidate.adjustHealth(-simple_heal * seconds_per_tick, updating_health = FALSE) if(!HAS_TRAIT(candidate, TRAIT_NO_BLOOD_RESTORE) && candidate.blood_volume < BLOOD_VOLUME_NORMAL) - candidate.blood_volume += blood_heal * seconds_per_tick + candidate.AdjustBlood(blood_heal * seconds_per_tick) var/external_organ_heal_done = FALSE if(ishuman(candidate)) diff --git a/code/datums/components/blob_minion.dm b/code/datums/components/blob_minion.dm new file mode 100644 index 00000000000..9f0c91b6b26 --- /dev/null +++ b/code/datums/components/blob_minion.dm @@ -0,0 +1,160 @@ +/** + * Common behaviour shared by things which are minions to a blob + */ +/datum/component/blob_minion + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Overmind who is our boss + var/mob/camera/blob/overmind + /// Callback to run if overmind strain changes + var/datum/callback/on_strain_changed + +/datum/component/blob_minion/Initialize(mob/camera/blob/overmind, datum/callback/on_strain_changed) + . = ..() + if(!isminion(parent)) + return COMPONENT_INCOMPATIBLE + src.on_strain_changed = on_strain_changed + register_overlord(overmind) + +/datum/component/blob_minion/Destroy(force) + . = ..() + +/datum/component/blob_minion/InheritComponent(datum/component/new_comp, i_am_original, mob/camera/blob/overmind, datum/callback/on_strain_changed) + if(!isnull(on_strain_changed)) + src.on_strain_changed = on_strain_changed + register_overlord(overmind) + +/datum/component/blob_minion/proc/register_overlord(mob/camera/blob/overmind) + if(isnull(overmind)) + return + src.overmind = overmind + overmind.register_new_minion(parent) + RegisterSignal(overmind, COMSIG_QDELETING, PROC_REF(overmind_deleted)) + RegisterSignal(overmind, COMSIG_BLOB_SELECTED_STRAIN, PROC_REF(overmind_properties_changed)) + overmind_properties_changed(overmind, overmind.blobstrain) + +/// Our overmind is gone, uh oh! +/datum/component/blob_minion/proc/overmind_deleted() + SIGNAL_HANDLER + overmind = null + overmind_properties_changed() + +/// Our overmind has changed colour and properties +/datum/component/blob_minion/proc/overmind_properties_changed(mob/camera/blob/overmind, datum/blobstrain/new_strain) + SIGNAL_HANDLER + var/mob/living/living_parent = parent + living_parent.update_appearance(UPDATE_ICON | UPDATE_OVERLAYS) + on_strain_changed?.Invoke(overmind, new_strain) + +/datum/component/blob_minion/RegisterWithParent() + var/mob/living/living_parent = parent + living_parent.pass_flags |= PASSBLOB + living_parent.faction |= ROLE_BLOB + ADD_TRAIT(parent, TRAIT_BLOB_ALLY, REF(src)) + living_parent.stop_pulling() + RegisterSignal(parent, COMSIG_MOB_MIND_INITIALIZED, PROC_REF(on_mind_init)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON, PROC_REF(on_update_appearance)) + RegisterSignal(parent, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(on_update_status_tab)) + RegisterSignal(parent, COMSIG_ATOM_BLOB_ACT, PROC_REF(on_blob_touched)) + RegisterSignal(parent, COMSIG_ATOM_FIRE_ACT, PROC_REF(on_burned)) + RegisterSignal(parent, COMSIG_ATOM_TRIED_PASS, PROC_REF(on_attempted_pass)) + RegisterSignal(parent, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(on_space_move)) + RegisterSignal(parent, COMSIG_MOB_TRY_SPEECH, PROC_REF(on_try_speech)) + RegisterSignal(parent, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed)) + living_parent.update_appearance(UPDATE_ICON | UPDATE_OVERLAYS) + GLOB.blob_telepathy_mobs |= parent + +/datum/component/blob_minion/UnregisterFromParent() + if(!isnull(overmind)) + overmind.blob_mobs -= parent + var/mob/living/living_parent = parent + living_parent.pass_flags &= ~PASSBLOB + living_parent.faction -= ROLE_BLOB + REMOVE_TRAIT(parent, TRAIT_BLOB_ALLY, REF(src)) + UnregisterSignal(parent, list( + COMSIG_ATOM_BLOB_ACT, + COMSIG_ATOM_FIRE_ACT, + COMSIG_ATOM_TRIED_PASS, + COMSIG_ATOM_UPDATE_ICON, + COMSIG_MOB_TRY_SPEECH, + COMSIG_MOB_CHANGED_TYPE, + COMSIG_MOB_GET_STATUS_TAB_ITEMS, + COMSIG_MOB_MIND_INITIALIZED, + COMSIG_MOVABLE_SPACEMOVE, + )) + GLOB.blob_telepathy_mobs -= parent + +/// Become blobpilled when we gain a mind +/datum/component/blob_minion/proc/on_mind_init(mob/living/minion, datum/mind/new_mind) + SIGNAL_HANDLER + if(isnull(overmind) || new_mind.has_antag_datum(/datum/antagonist/blob_minion)) + return + + var/datum_type = (isblobbernaut(minion))? /datum/antagonist/blob_minion/blobernaut : /datum/antagonist/blob_minion + var/datum/antagonist/blob_minion/minion_motive = new datum_type(overmind) + new_mind.add_antag_datum(minion_motive) + +/// When our icon is updated, update our colour too +/datum/component/blob_minion/proc/on_update_appearance(mob/living/minion) + SIGNAL_HANDLER + if(isnull(overmind)) + minion.remove_atom_colour(FIXED_COLOUR_PRIORITY) + return + minion.add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY) + +/// When our icon is updated, update our colour too +/datum/component/blob_minion/proc/on_update_status_tab(mob/living/minion, list/status_items) + SIGNAL_HANDLER + if(isnull(overmind)) + return + status_items += list(list("Критическая Масса:", "[TOTAL_BLOB_MASS]/[NEEDED_BLOB_MASS]")) + +/// If we feel the gentle caress of a blob, we feel better +/datum/component/blob_minion/proc/on_blob_touched(mob/living/minion) + SIGNAL_HANDLER + if(minion.stat == DEAD || minion.health >= minion.maxHealth) + return COMPONENT_CANCEL_BLOB_ACT // Don't hurt us in order to heal us + for(var/i in 1 to 2) + var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(parent)) // hello yes you are being healed + heal_effect.color = isnull(overmind) ? COLOR_BLACK : overmind.blobstrain.complementary_color + minion.heal_overall_damage(minion.maxHealth * BLOBMOB_HEALING_MULTIPLIER) + if(minion.on_fire) + minion.adjust_fire_stacks(-1) + return COMPONENT_CANCEL_BLOB_ACT + +/// If we feel the fearsome bite of open flame, we feel worse +/datum/component/blob_minion/proc/on_burned(mob/living/minion, exposed_temperature, exposed_volume) + SIGNAL_HANDLER + if(isnull(exposed_temperature)) + minion.adjustFireLoss(5) + return + minion.adjustFireLoss(clamp(0.01 * exposed_temperature, 1, 5)) + +/// Someone is attempting to move through us, allow it if it is a blob tile +/datum/component/blob_minion/proc/on_attempted_pass(mob/living/minion, atom/movable/incoming) + SIGNAL_HANDLER + if(istype(incoming, /obj/structure/blob)) + return COMSIG_COMPONENT_PERMIT_PASSAGE + +/// If we're near a blob, stop drifting +/datum/component/blob_minion/proc/on_space_move(mob/living/minion) + SIGNAL_HANDLER + var/obj/structure/blob/blob_handhold = locate() in range(1, parent) + if(!isnull(blob_handhold)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + +/// We only speak telepathically to blobs +/datum/component/blob_minion/proc/on_try_speech(mob/living/minion, message, ignore_spam, forced) + SIGNAL_HANDLER + var/spanned_message = minion.say_quote(message) + var/rendered = span_blob("\[Blob Telepathy\] [minion.real_name] [spanned_message], [message]") + relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, minion) + return COMPONENT_CANNOT_SPEAK + +/// Called when a blob minion is transformed into something else, hopefully a spore into a zombie +/datum/component/blob_minion/proc/on_transformed(mob/living/minion, mob/living/replacement) + SIGNAL_HANDLER + overmind?.assume_direct_control(replacement) + +/datum/component/blob_minion/PostTransfer() + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE diff --git a/code/datums/components/blob_turf_consuming.dm b/code/datums/components/blob_turf_consuming.dm new file mode 100644 index 00000000000..7ad814047d8 --- /dev/null +++ b/code/datums/components/blob_turf_consuming.dm @@ -0,0 +1,34 @@ +/datum/component/blob_turf_consuming + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Number of attempts of consume neede for consume + var/consumes_needed = 0 + /// Total number of attempts of consume + var/total_consumes = 0 + +/datum/component/blob_turf_consuming/Initialize(_consumes_needed) + if(!isturf(parent)) + return COMPONENT_INCOMPATIBLE + + consumes_needed = _consumes_needed + +/datum/component/blob_turf_consuming/RegisterWithParent() + RegisterSignal(parent, COMSIG_TRY_CONSUME_TURF, PROC_REF(on_try_consume)) + +/datum/component/blob_turf_consuming/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_TRY_CONSUME_TURF) + + +/datum/component/blob_turf_consuming/InheritComponent(datum/component/blob_turf_consuming/new_comp , i_am_original, _consumes_needed) + if(new_comp) + consumes_needed = new_comp.consumes_needed + else + consumes_needed = _consumes_needed + + +/datum/component/blob_turf_consuming/proc/on_try_consume() + total_consumes++ + if(total_consumes >= consumes_needed) + var/turf/total_turf = parent + total_turf.blob_consume() + return + return COMPONENT_CANT_CONSUME diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index 7884935d3c1..68fc4b3f348 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -174,6 +174,9 @@ return // We're already handling this if(below_turf) + if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED)) + qdel(dropped_thing) + return // send to the turf below dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [atom_parent]!"), span_userdanger("[fall_message]")) below_turf.visible_message(span_boldwarning("[dropped_thing] falls from above!")) @@ -215,6 +218,10 @@ if(QDELETED(dropped_thing)) return + if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED)) + qdel(dropped_thing) + return + if(isrobot(dropped_thing)) var/mob/living/silicon/robot/robot = dropped_thing qdel(robot.mmi) diff --git a/code/datums/components/connect_containers.dm b/code/datums/components/connect_containers.dm new file mode 100644 index 00000000000..6f793c860e1 --- /dev/null +++ b/code/datums/components/connect_containers.dm @@ -0,0 +1,68 @@ +/// This component behaves similar to connect_loc_behalf, but it's nested and hooks a signal onto all MOVABLES containing this atom. +/datum/component/connect_containers + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + + /// An assoc list of signal -> procpath to register to the loc this object is on. + var/list/connections + /** + * The atom the component is tracking. The component will delete itself if the tracked is deleted. + * Signals will also be updated whenever it moves. + */ + var/atom/movable/tracked + +/datum/component/connect_containers/Initialize(atom/movable/tracked, list/connections) + . = ..() + if(!ismovable(tracked)) + return COMPONENT_INCOMPATIBLE + + src.connections = connections + set_tracked(tracked) + +/datum/component/connect_containers/Destroy() + set_tracked(null) + return ..() + +/datum/component/connect_containers/InheritComponent(datum/component/component, original, atom/movable/tracked, list/connections) + // Not equivalent. Checks if they are not the same list via shallow comparison. + if(!compare_list(src.connections, connections)) + stack_trace("connect_containers component attached to [parent] tried to inherit another connect_containers component with different connections") + return + if(src.tracked != tracked) + set_tracked(tracked) + +/datum/component/connect_containers/proc/set_tracked(atom/movable/new_tracked) + if(tracked) + UnregisterSignal(tracked, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING)) + unregister_signals(tracked.loc) + tracked = new_tracked + if(!tracked) + return + RegisterSignal(tracked, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(tracked, COMSIG_QDELETING, PROC_REF(handle_tracked_qdel)) + update_signals(tracked) + +/datum/component/connect_containers/proc/handle_tracked_qdel() + SIGNAL_HANDLER + qdel(src) + +/datum/component/connect_containers/proc/update_signals(atom/movable/listener) + if(!ismovable(listener.loc)) + return + + for(var/atom/movable/container as anything in get_nested_locs(listener)) + RegisterSignal(container, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + for(var/signal in connections) + parent.RegisterSignal(container, signal, connections[signal]) + +/datum/component/connect_containers/proc/unregister_signals(atom/movable/location) + if(!ismovable(location)) + return + + for(var/atom/movable/target as anything in (get_nested_locs(location) + location)) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + parent.UnregisterSignal(target, connections) + +/datum/component/connect_containers/proc/on_moved(atom/movable/listener, atom/old_loc) + SIGNAL_HANDLER + unregister_signals(old_loc) + update_signals(listener) diff --git a/code/datums/components/death_linked.dm b/code/datums/components/death_linked.dm new file mode 100644 index 00000000000..4fba8d5f68d --- /dev/null +++ b/code/datums/components/death_linked.dm @@ -0,0 +1,51 @@ +/** + * ## Death link component + * + * When the owner of this component dies it also gibs a linked mobs + */ +/datum/component/death_linked + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Mobs in that list will die when the user dies. Contains weakrefs + var/list/linked_mobs + +/datum/component/death_linked/Initialize(list/mobs) + . = ..() + + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + + for(var/mob/mob as anything in mobs) + LAZYADD(linked_mobs, WEAKREF(mob)) + +/datum/component/death_linked/Destroy(force) + LAZYNULL(linked_mobs) + + return ..() + +/datum/component/death_linked/InheritComponent(datum/component/death_linked/new_comp, i_am_original, list/mobs) + if(!i_am_original) + return + + if(!LAZYLEN(mobs)) + return + + for(var/mob/mob as anything in mobs) + LAZYADD(linked_mobs, WEAKREF(mob)) + +/datum/component/death_linked/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(on_death)) + +/datum/component/death_linked/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_LIVING_DEATH) + +/datum/component/death_linked/proc/on_death(mob/living/target, gibbed) + SIGNAL_HANDLER + + if(!LAZYLEN(linked_mobs)) + return + + for(var/datum/weakref/weakref as anything in linked_mobs) + var/mob/living/linked_mob_resolved = weakref.resolve() + linked_mob_resolved?.gib() diff --git a/code/datums/components/ghost_direct_control.dm b/code/datums/components/ghost_direct_control.dm new file mode 100644 index 00000000000..179695c6150 --- /dev/null +++ b/code/datums/components/ghost_direct_control.dm @@ -0,0 +1,158 @@ +/** + * Component which lets ghosts click on a mob to take control of it + */ +/datum/component/ghost_direct_control + /// Message to display upon successful possession + var/assumed_control_message + /// Type of ban you can get to prevent you from accepting this role + var/ban_type + /// Any extra checks which need to run before we take over + var/datum/callback/extra_control_checks + /// Callback run after someone successfully takes over the body + var/datum/callback/after_assumed_control + /// If we're currently awaiting the results of a ghost poll + var/awaiting_ghosts = FALSE + +/datum/component/ghost_direct_control/Initialize( + ban_type = ROLE_SENTIENT, + role_name = null, + poll_question = null, + poll_candidates = TRUE, + antag_age_check = TRUE, + check_antaghud = TRUE, + poll_length = 10 SECONDS, + assumed_control_message = null, + datum/callback/extra_control_checks, + datum/callback/after_assumed_control, +) + . = ..() + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + + src.ban_type = ban_type + src.assumed_control_message = assumed_control_message || "You are [parent]!" + src.extra_control_checks = extra_control_checks + src.after_assumed_control = after_assumed_control + + var/mob/mob_parent = parent + LAZYADD(GLOB.mob_spawners[format_text("[initial(mob_parent.name)]")], mob_parent) + + if(poll_candidates) + INVOKE_ASYNC(src, PROC_REF(request_ghost_control), poll_question, role_name || "[parent]", poll_length, antag_age_check, check_antaghud) + +/datum/component/ghost_direct_control/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ATOM_ATTACK_GHOST, PROC_REF(on_ghost_clicked)) + RegisterSignal(parent, COMSIG_LIVING_EXAMINE, PROC_REF(on_examined)) + RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(on_login)) + RegisterSignal(parent, COMSIG_IS_GHOST_CONTROLABLE, PROC_REF(on_ghost_controlable_check)) + +/datum/component/ghost_direct_control/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_LIVING_EXAMINE, COMSIG_MOB_LOGIN)) + return ..() + +/datum/component/ghost_direct_control/Destroy(force) + extra_control_checks = null + after_assumed_control = null + + var/mob/mob_parent = parent + var/list/spawners = GLOB.mob_spawners[format_text("[initial(mob_parent.name)]")] + LAZYREMOVE(spawners, mob_parent) + if(!LAZYLEN(spawners)) + GLOB.mob_spawners -= format_text("[initial(mob_parent.name)]") + return ..() + +/// Inform ghosts that they can possess this +/datum/component/ghost_direct_control/proc/on_examined(datum/source, mob/user, list/examine_text) + SIGNAL_HANDLER + if(!isobserver(user)) + return + var/mob/living/our_mob = parent + if(our_mob.stat == DEAD || our_mob.key || awaiting_ghosts) + return + examine_text += span_boldnotice("You could take control of this mob by clicking on it.") + +/// Send out a request for a brain +/datum/component/ghost_direct_control/proc/request_ghost_control(poll_question, role_name, poll_length, age_check, check_ahud) + awaiting_ghosts = TRUE + var/list/possible_ghosts = SSghost_spawns.poll_candidates( + question = poll_question, + role = ban_type, + poll_time = poll_length, + antag_age_check = age_check, + check_antaghud = check_ahud, + source = parent, + role_cleanname = role_name + ) + var/mob/chosen_one = (possible_ghosts.len)? pick(possible_ghosts): null + awaiting_ghosts = FALSE + if(isnull(chosen_one)) + return + assume_direct_control(chosen_one) + +/// A ghost clicked on us, they want to get in this body +/datum/component/ghost_direct_control/proc/on_ghost_clicked(mob/our_mob, mob/dead/observer/hopeful_ghost) + SIGNAL_HANDLER + if(our_mob.key) + qdel(src) + return + if(!hopeful_ghost.client) + return + if(awaiting_ghosts) + to_chat(hopeful_ghost, span_warning("Ghost candidate selection currently in progress!")) + return COMPONENT_CANCEL_ATTACK_CHAIN + if(!SSticker.HasRoundStarted()) + to_chat(hopeful_ghost, span_warning("You cannot assume control of this until after the round has started!")) + return COMPONENT_CANCEL_ATTACK_CHAIN + INVOKE_ASYNC(src, PROC_REF(attempt_possession), our_mob, hopeful_ghost) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/// We got far enough to establish that this mob is a valid target, let's try to posssess it +/datum/component/ghost_direct_control/proc/attempt_possession(mob/our_mob, mob/dead/observer/hopeful_ghost) + var/ghost_asked = tgui_alert(usr, "Become [our_mob]?", "Are you sure?", list("Yes", "No")) + if(ghost_asked != "Yes" || QDELETED(our_mob)) + return + assume_direct_control(hopeful_ghost) + +/// Grant possession of our mob, component is now no longer required +/datum/component/ghost_direct_control/proc/assume_direct_control(mob/harbinger) + if(QDELETED(src)) + to_chat(harbinger, span_warning("Offer to possess creature has expired!")) + return + if(jobban_isbanned(harbinger, ban_type)) + to_chat(harbinger, span_warning("You are banned from playing as this role!")) + return + var/mob/living/new_body = parent + if(new_body.stat == DEAD) + to_chat(harbinger, span_warning("This body has passed away, it is of no use!")) + return + if(new_body.key) + to_chat(harbinger, span_warning("[parent] has already become sapient!")) + qdel(src) + return + if(extra_control_checks && !extra_control_checks.Invoke(harbinger)) + return + + add_game_logs("took control of [new_body].", harbinger) + // doesn't transfer mind because that transfers antag datum as well + new_body.key = harbinger.key + if(isanimal(new_body)) + var/mob/living/simple_animal/animal_body = new_body + animal_body.toggle_ai(AI_OFF) + // Already qdels due to below proc but just in case + qdel(src) + +/// When someone assumes control, get rid of our component +/datum/component/ghost_direct_control/proc/on_login(mob/harbinger) + SIGNAL_HANDLER + // This proc is called the very moment .key is set, so we need to force mind to initialize here if we want the invoke to affect the mind of the mob + if(isnull(harbinger.mind)) + harbinger.mind_initialize() + to_chat(harbinger, span_boldnotice(assumed_control_message)) + after_assumed_control?.Invoke(harbinger) + qdel(src) + + +/datum/component/ghost_direct_control/proc/on_ghost_controlable_check(mob/user) + SIGNAL_HANDLER + return COMPONENT_GHOST_CONTROLABLE diff --git a/code/datums/components/holderloving.dm b/code/datums/components/holderloving.dm new file mode 100644 index 00000000000..6340ff9b1db --- /dev/null +++ b/code/datums/components/holderloving.dm @@ -0,0 +1,69 @@ +/** Holder Loving Component + * + * When you drop an object onto a turf it gets moved back into its parent holder + * + * Prevents you from force moving the object into any other location that isn't its parent holder + */ +/datum/component/holderloving + /** Item that parent is bound to. + * We try to keep parent either directly in holder, or in holder's loc if loc is a mob, + * and warp parent into holder if they go anywhere else. + */ + var/atom/holder + +/datum/component/holderloving/Initialize(holder) + if(!isitem(parent) || !holder) + return COMPONENT_INCOMPATIBLE + + src.holder = holder + +/datum/component/holderloving/Destroy(force) + holder = null + + return ..() + +/datum/component/holderloving/RegisterWithParent() + RegisterSignal(holder, COMSIG_QDELETING, PROC_REF(holder_deleting)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(check_my_loc)) + RegisterSignal(parent, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(can_be_moved)) + +/datum/component/holderloving/UnregisterFromParent() + UnregisterSignal(holder, list(COMSIG_QDELETING)) + UnregisterSignal(parent, list(COMSIG_ITEM_DROPPED, COMSIG_ITEM_PRE_UNEQUIP)) + +/datum/component/holderloving/proc/holder_deleting(datum/source, force) + SIGNAL_HANDLER + + qdel(parent) + +/datum/component/holderloving/proc/is_valid_location(atom/location) + SHOULD_BE_PURE(TRUE) + + if(location == holder || (location == holder.loc && ismob(holder.loc))) + return TRUE + + return FALSE + +/datum/component/holderloving/proc/check_my_loc(datum/source, mob/user, slot) + SIGNAL_HANDLER + + var/obj/item/item_parent = parent + if(!is_valid_location(item_parent.loc)) + item_parent.forceMove(holder) + +/datum/component/holderloving/proc/can_be_moved( + obj/item/I, + force, + atom/newloc, + no_move, + invdrop, + silent + ) + SIGNAL_HANDLER + + // allow the item to be dropped on the turf so it can be later moved back into the holder as a convinience tool + if(isturf(newloc) || is_valid_location(newloc)) + return NONE + + // prevent this item from being moved anywhere else + return COMPONENT_ITEM_BLOCK_UNEQUIP diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm index 2249aec3f12..c93af5c1ddc 100644 --- a/code/datums/components/overlay_lighting.dm +++ b/code/datums/components/overlay_lighting.dm @@ -48,6 +48,8 @@ "288" = 'icons/effects/light_overlays/light_288.dmi', "320" = 'icons/effects/light_overlays/light_320.dmi', "352" = 'icons/effects/light_overlays/light_352.dmi', + "480" = 'icons/effects/light_overlays/light_480.dmi', + "544" = 'icons/effects/light_overlays/light_544.dmi', ) ///Overlay effect to cut into the darkness and provide light. @@ -347,7 +349,7 @@ return if(range == 0) turn_off() - range = clamp(CEILING(new_range, 0.5), 1, 6) + range = clamp(CEILING(new_range, 0.5), 1, 8) var/pixel_bounds = ((range - 1) * 64) + 32 lumcount_range = CEILING(range, 1) if(current_holder && overlay_lighting_flags & LIGHTING_ON) diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm new file mode 100644 index 00000000000..eb8635effc0 --- /dev/null +++ b/code/datums/components/pellet_cloud.dm @@ -0,0 +1,325 @@ +// the following defines are used for [/datum/component/pellet_cloud/var/list/wound_info_by_part] to store the damage, wound_bonus, and bw_bonus for each bodypart hit +#define CLOUD_POSITION_DAMAGE 1 +#define CLOUD_POSITION_W_BONUS 2 +#define CLOUD_POSITION_BW_BONUS 3 + +/* + * This component is used when you want to create a bunch of shrapnel or projectiles (say, shrapnel from a fragmentation grenade, or buckshot from a shotgun) from a central point, + * without necessarily printing a separate message for every single impact. This component should be instantiated right when you need it (like the moment of firing), then activated + * by signal. + * + * Pellet cloud currently works on two classes of sources: directed (ammo casings), and circular (grenades, landmines). + * -Directed: This means you're shooting multiple pellets, like buckshot. If an ammo casing is defined as having multiple pellets, it will automatically create a pellet cloud + * and call COMSIG_FIRE_CASING (see [/obj/item/ammo_casing/proc/fire_casing]). Thus, the only projectiles fired will be the ones fired here. + * The magnitude var controls how many pellets are created. + * -Circular: This results in a big spray of shrapnel flying all around the detonation point when the grenade fires COMSIG_GRENADE_DETONATE or landmine triggers COMSIG_MINE_TRIGGERED. + * The magnitude var controls how big the detonation radius is (the bigger the magnitude, the more shrapnel is created). Grenades can be covered with bodies to reduce shrapnel output. + * + * Once all of the fired projectiles either hit a target or disappear due to ranging out/whatever else, we resolve the list of all the things we hit and print aggregate messages so we get + * one "You're hit by 6 buckshot pellets" vs 6x "You're hit by the buckshot blah blah" messages. + * + * Note that this is how all guns handle shooting ammo casings with multiple pellets, in case such a thing comes up. +*/ + +/datum/component/pellet_cloud + /// What's the projectile path of the shrapnel we're shooting? + var/projectile_type + + /// How many shrapnel projectiles are we responsible for tracking? May be reduced for grenades if someone dives on top of it. Defined by ammo casing for casings, derived from magnitude otherwise + var/num_pellets + /// For grenades/landmines, how big is the radius of turfs we're targeting? Note this does not effect the projectiles range, only how many we generate + var/radius = 4 + + /// The list of pellets we're responsible for tracking, once these are all accounted for, we finalize. + var/list/pellets = list() + /// An associated list with the atom hit as the key and how many pellets they've eaten for the value, for printing aggregate messages + var/list/targets_hit = list() + /// Another associated list for hit bodyparts on carbons so we can track how much wounding potential we have for each bodypart + var/list/wound_info_by_part = list() + /// For grenades, any /mob/living's the grenade is moved onto, see [/datum/component/pellet_cloud/proc/handle_martyrs] + var/list/bodies + /// For grenades, tracking people who die covering a grenade for achievement purposes, see [/datum/component/pellet_cloud/proc/handle_martyrs] + var/list/purple_hearts + + /// For grenades, tracking how many pellets are removed due to martyrs and how many pellets are added due to the last person to touch it being on top of it + var/pellet_delta = 0 + /// how many pellets ranged out without hitting anything + var/terminated + /// how many pellets impacted something + var/hits + /// If the parent tried deleting and we're not done yet, we send it to nullspace then delete it after + var/queued_delete = FALSE + + /// for if we're an ammo casing being fired + var/mob/living/shooter + + +/datum/component/pellet_cloud/Initialize(projectile_type=/obj/item/projectile/shrapnel, magnitude=5) + if(!isammocasing(parent) && !isgrenade(parent) && !issupplypod(parent)) + return COMPONENT_INCOMPATIBLE + + if(magnitude < 1) + stack_trace("Invalid magnitude [magnitude] < 1 on pellet_cloud, parent: [parent]") + magnitude = 1 + + src.projectile_type = projectile_type + + if(isammocasing(parent)) + num_pellets = magnitude + else if(isgrenade(parent) || issupplypod(parent)) + radius = magnitude + +/datum/component/pellet_cloud/Destroy(force) + purple_hearts = null + pellets = null + targets_hit = null + wound_info_by_part = null + bodies = null + return ..() + +/datum/component/pellet_cloud/RegisterWithParent() + RegisterSignal(parent, COMSIG_PREQDELETED, PROC_REF(nullspace_parent)) + if(isammocasing(parent)) + RegisterSignal(parent, COMSIG_FIRE_CASING, PROC_REF(create_casing_pellets)) + else if(isgrenade(parent)) + RegisterSignal(parent, COMSIG_GRENADE_ARMED, PROC_REF(grenade_armed)) + RegisterSignal(parent, COMSIG_GRENADE_DETONATE, PROC_REF(create_blast_pellets)) + else if(issupplypod(parent)) + RegisterSignal(parent, COMSIG_SUPPLYPOD_LANDED, PROC_REF(create_blast_pellets)) + +/datum/component/pellet_cloud/UnregisterFromParent() + UnregisterSignal(parent, list(COMSIG_PREQDELETED, COMSIG_FIRE_CASING, COMSIG_GRENADE_DETONATE, COMSIG_GRENADE_ARMED, COMSIG_MOVABLE_MOVED, COMSIG_ITEM_DROPPED)) + +/** + * create_casing_pellets() is for directed pellet clouds for ammo casings that have multiple pellets (buckshot and scatter lasers for instance) + * + * Honestly this is mostly just a rehash of [/obj/item/ammo_casing/proc/fire_casing] for pellet counts > 1, except this lets us tamper with the pellets and hook onto them for tracking purposes. + * The arguments really don't matter, while this proc is triggered by COMSIG_FIRE_CASING, it's just a big mess of the state vars we need for doing the stuff over here. + */ +/datum/component/pellet_cloud/proc/create_casing_pellets(obj/item/ammo_casing/shell, atom/target, mob/living/user, fired_from, randomspread, spread, zone_override, params, distro) + SIGNAL_HANDLER + + shooter = user + var/turf/target_loc = get_turf(target) + if(!zone_override) + zone_override = shooter.zone_selected + + // things like mouth executions and gunpoints can multiply the damage and wounds of projectiles, so this makes sure those effects are applied to each pellet instead of just one + var/original_damage = shell.BB.damage + + for(var/i in 1 to num_pellets) + shell.ready_proj(target, user, TRUE, zone_override, fired_from) + if(distro) + if(randomspread) + spread = round((rand() - 0.5) * distro) + else //Smart spread + spread = round((i / num_pellets - 0.5) * distro) + + RegisterSignal(shell.BB, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(pellet_hit)) + RegisterSignals(shell.BB, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_QDELETING), PROC_REF(pellet_range)) + shell.BB.damage = original_damage + pellets += shell.BB + var/turf/current_loc = get_turf(fired_from) + if (!istype(target_loc) || !istype(current_loc) || !(shell.BB)) + return + INVOKE_ASYNC(shell, TYPE_PROC_REF(/obj/item/ammo_casing, throw_proj), target, target_loc, shooter, params, spread, fired_from) + + if(i != num_pellets) + shell.newshot() + +/** + * create_blast_pellets() is for when we have a central point we want to shred the surroundings of with a ring of shrapnel, namely frag grenades and landmines. + * + * Note that grenades have extra handling for someone throwing themselves/being thrown on top of it, see [/datum/component/pellet_cloud/proc/handle_martyrs] + * Landmines just have a small check for [/obj/effect/mine/shrapnel/var/shred_triggerer], and spawn extra shrapnel for them if so + * + * Arguments: + * * O- Our parent, the thing making the shrapnel obviously (grenade or landmine) + * * punishable_triggerer- For grenade lances or people who step on the landmines (if we shred the triggerer), we spawn extra shrapnel for them in addition to the normal spread + */ +/datum/component/pellet_cloud/proc/create_blast_pellets(obj/O, mob/living/triggerer) + SIGNAL_HANDLER + + var/atom/A = parent + + if(isgrenade(parent)) // handle_martyrs can reduce the radius and thus the number of pellets we produce if someone dives on top of a frag grenade + INVOKE_ASYNC(src, PROC_REF(handle_martyrs), triggerer) // note that we can modify radius in this proc + + if(radius < 1) + return + + var/list/all_the_turfs_were_gonna_lacerate = RANGE_TURFS(radius, A) - RANGE_TURFS(radius-1, A) + num_pellets = all_the_turfs_were_gonna_lacerate.len + pellet_delta + + for(var/T in all_the_turfs_were_gonna_lacerate) + INVOKE_ASYNC(src, PROC_REF(pew), T) + +/** + * handle_martyrs() is used for grenades that shoot shrapnel to check if anyone threw themselves/were thrown on top of the grenade, thus absorbing a good chunk of the shrapnel + * + * Between the time the grenade is armed and the actual detonation, we set var/list/bodies to the list of mobs currently on the new tile, as if the grenade landed on top of them, tracking if any of them move off the tile and removing them from the "under" list + * Once the grenade detonates, handle_martyrs() is called and gets all the new mobs on the tile, and add the ones not in var/list/bodies to var/list/martyrs + * We then iterate through the martyrs and reduce the shrapnel magnitude for each mob on top of it, shredding each of them with some of the shrapnel they helped absorb. This can snuff out all of the shrapnel if there's enough bodies + * + * Note we track anyone who's alive and client'd when they get shredded in var/list/purple_hearts, for achievement checking later + */ +/datum/component/pellet_cloud/proc/handle_martyrs(mob/living/punishable_triggerer) + var/magnitude_absorbed + var/list/martyrs = list() + + var/self_harm_radius_mult = 3 + + if(punishable_triggerer && prob(60)) + to_chat(punishable_triggerer, span_userdanger("Your plan to whack someone with a grenade on a stick backfires on you, literally!")) + self_harm_radius_mult = 1 // we'll still give the guy who got hit some extra shredding, but not 3*radius + pellet_delta += radius + for(var/i in 1 to radius) + pew(punishable_triggerer) // thought you could be tricky and lance someone with no ill effects!! + + for(var/mob/living/body in get_turf(parent)) + if(body == shooter) + pellet_delta += radius * self_harm_radius_mult + for(var/i in 1 to radius * self_harm_radius_mult) + pew(body) // free shrapnel if it goes off in your hand, and it doesn't even count towards the absorbed. fun! + else if(!(body in bodies)) + martyrs += body // promoted from a corpse to a hero + + for(var/M in martyrs) + var/mob/living/martyr = M + if(radius > 4) + martyr.visible_message("[span_danger("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing a load of the shrapnel!")]", span_userdanger("You heroically cover \the [parent] with your body, absorbing a load of the shrapnel!")) + magnitude_absorbed += round(radius * 0.5) + else if(radius >= 2) + martyr.visible_message("[span_danger("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, absorbing some of the shrapnel!")]", span_userdanger("You heroically cover \the [parent] with your body, absorbing some of the shrapnel!")) + magnitude_absorbed += 2 + else + martyr.visible_message("[span_danger("[martyr] heroically covers \the [parent] with [martyr.p_their()] body, snuffing out the shrapnel!")]", span_userdanger("You heroically cover \the [parent] with your body, snuffing out the shrapnel!")) + magnitude_absorbed = radius + + var/pellets_absorbed = (radius ** 2) - ((radius - magnitude_absorbed - 1) ** 2) + radius -= magnitude_absorbed + pellet_delta -= round(pellets_absorbed * 0.5) + + if(martyr.stat != DEAD && martyr.client) + LAZYADD(purple_hearts, martyr) + RegisterSignal(martyr, COMSIG_QDELETING, PROC_REF(on_target_qdel), override=TRUE) + + for(var/i in 1 to round(pellets_absorbed * 0.5)) + pew(martyr) + + if(radius < 1) + break + +///One of our pellets hit something, record what it was and check if we're done (terminated == num_pellets) +/datum/component/pellet_cloud/proc/pellet_hit(obj/item/projectile/P, atom/movable/firer, atom/target, Angle, hit_zone) + SIGNAL_HANDLER + + pellets -= P + terminated++ + hits++ + + var/damage = TRUE + if(isobj(target)) + var/obj/hit_object = target + if(hit_object.damage_deflection > P.damage || !P.damage) + damage = FALSE + + LAZYADDASSOC(targets_hit[target], "hits", 1) + LAZYSET(targets_hit[target], "damage", damage) + if(targets_hit[target]["hits"] == 1) + RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_qdel), override=TRUE) + UnregisterSignal(P, list(COMSIG_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT)) + if(terminated == num_pellets) + finalize() + +///One of our pellets disappeared due to hitting their max range (or just somehow got qdel'd), remove it from our list and check if we're done (terminated == num_pellets) +/datum/component/pellet_cloud/proc/pellet_range(obj/item/projectile/P) + SIGNAL_HANDLER + pellets -= P + terminated++ + UnregisterSignal(P, list(COMSIG_QDELETING, COMSIG_PROJECTILE_RANGE_OUT, COMSIG_PROJECTILE_SELF_ON_HIT)) + if(terminated == num_pellets) + finalize() + +/// Minor convenience function for creating each shrapnel piece with circle explosions, mostly stolen from the MIRV component +/datum/component/pellet_cloud/proc/pew(atom/target, landmine_victim) + + var/obj/item/projectile/P = new projectile_type(get_turf(parent)) + + //Shooting Code: + P.spread = 0 + P.original = target + P.firer_source_atom = parent + P.firer = parent // don't hit ourself that would be really annoying + P.suppressed = TRUE// set the projectiles to make no message so we can do our own aggregate message + P.preparePixelProjectile(target, target, parent) + RegisterSignal(P, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(pellet_hit)) + RegisterSignals(P, list(COMSIG_PROJECTILE_RANGE_OUT, COMSIG_QDELETING), PROC_REF(pellet_range)) + pellets += P + P.fire() + +///All of our pellets are accounted for, time to go target by target and tell them how many things they got hit by. +/datum/component/pellet_cloud/proc/finalize() + UnregisterSignal(parent, COMSIG_PREQDELETED) + if(queued_delete) + qdel(parent) + qdel(src) + +/// Look alive, we're armed! Now we start watching to see if anyone's covering us +/datum/component/pellet_cloud/proc/grenade_armed(obj/item/nade) + SIGNAL_HANDLER + + if(ismob(nade.loc)) + shooter = nade.loc + LAZYINITLIST(bodies) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(grenade_dropped)) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(grenade_moved)) + var/static/list/loc_connections = list( + COMSIG_ATOM_EXITED = PROC_REF(grenade_uncrossed), + ) + AddComponent(/datum/component/connect_loc_behalf, parent, loc_connections) + +/// Someone dropped the grenade, so set them to the shooter in case they're on top of it when it goes off +/datum/component/pellet_cloud/proc/grenade_dropped(obj/item/nade, mob/living/slick_willy) + SIGNAL_HANDLER + + shooter = slick_willy + grenade_moved() + +/// Our grenade has moved, reset var/list/bodies so we're "on top" of any mobs currently on the tile +/datum/component/pellet_cloud/proc/grenade_moved() + SIGNAL_HANDLER + + LAZYCLEARLIST(bodies) + for(var/mob/living/L in get_turf(parent)) + RegisterSignal(L, COMSIG_QDELETING, PROC_REF(on_target_qdel), override=TRUE) + bodies += L + +/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it +/datum/component/pellet_cloud/proc/grenade_uncrossed(datum/source, atom/movable/gone, direction) + SIGNAL_HANDLER + + bodies -= gone + +/// Our grenade or landmine or caseless shell or whatever tried deleting itself, so we intervene and nullspace it until we're done here +/datum/component/pellet_cloud/proc/nullspace_parent() + SIGNAL_HANDLER + + var/atom/movable/AM = parent + AM.moveToNullspace() + queued_delete = TRUE + return TRUE + +/// Someone who was originally "under" the grenade has moved off the tile and is now eligible for being a martyr and "covering" it +/datum/component/pellet_cloud/proc/on_target_qdel(atom/target) + SIGNAL_HANDLER + + UnregisterSignal(target, COMSIG_QDELETING) + targets_hit -= target + LAZYREMOVE(bodies, target) + LAZYREMOVE(purple_hearts, target) + + +#undef CLOUD_POSITION_DAMAGE +#undef CLOUD_POSITION_W_BONUS +#undef CLOUD_POSITION_BW_BONUS diff --git a/code/datums/components/pref_viewer.dm b/code/datums/components/pref_viewer.dm new file mode 100644 index 00000000000..5760e80f3f6 --- /dev/null +++ b/code/datums/components/pref_viewer.dm @@ -0,0 +1,41 @@ +/datum/component/pref_viewer + var/list/preferences_to_show + +/datum/component/pref_viewer/Destroy(force) + LAZYNULL(preferences_to_show) + + return ..() + +/datum/component/pref_viewer/Initialize( + list/preferences_to_show +) + if(!ismob(parent)) + return COMPONENT_INCOMPATIBLE + + for(var/datum/preference_info/pref as anything in preferences_to_show) + LAZYADD(src.preferences_to_show, new pref) + +/datum/component/pref_viewer/RegisterWithParent() + RegisterSignal(parent, COMSIG_MOB_RUN_EXAMINATE, PROC_REF(on_examine)) + +/datum/component/pref_viewer/UnregisterFromParent() + UnregisterSignal(parent, COMSIG_MOB_RUN_EXAMINATE) + +/datum/component/pref_viewer/proc/on_examine(mob/source, mob/target, list/result) + SIGNAL_HANDLER + + if(!istype(target) || !target.client) + return + + INVOKE_ASYNC(src, PROC_REF(modify_examine), target, result) + +/datum/component/pref_viewer/proc/modify_examine(mob/target, list/result) + for(var/datum/preference_info/pref as anything in preferences_to_show) + var/datum/preference_toggle/toggle = pref.get_preference_toggle() + + if(!HASBIT(target.client.prefs.toggles, toggle::preftoggle_bitflag) \ + && !HASBIT(target.client.prefs.toggles2, toggle::preftoggle_bitflag) + ) + continue + + LAZYADD(result, pref.get_examine_text()) diff --git a/code/datums/components/ritual_object.dm b/code/datums/components/ritual_object.dm index 31064e438db..f67fbd8b9aa 100644 --- a/code/datums/components/ritual_object.dm +++ b/code/datums/components/ritual_object.dm @@ -22,6 +22,7 @@ src.allowed_categories = allowed_categories src.allowed_species = allowed_species src.allowed_special_role = allowed_special_role + get_rituals() /datum/component/ritual_object/RegisterWithParent() @@ -62,7 +63,7 @@ if(allowed_species && !is_type_in_list(human.dna.species, allowed_species)) return - if(allowed_special_role && !is_type_in_list(human.mind?.special_role, allowed_special_role)) + if(allowed_special_role && !LAZYIN(allowed_special_role, human.mind?.special_role)) return active_ui = TRUE @@ -111,7 +112,7 @@ if(ritual.allowed_species && !is_type_in_list(human.dna.species, ritual.allowed_species)) continue - if(ritual.allowed_special_role && !is_type_in_list(human.mind?.special_role, ritual.allowed_special_role)) + if(ritual.allowed_special_role && !LAZYIN(ritual.allowed_special_role, human.mind?.special_role)) continue LAZYADD(rituals_list, ritual.name) diff --git a/code/datums/components/stationloving.dm b/code/datums/components/stationloving.dm new file mode 100644 index 00000000000..d5cdfe0078d --- /dev/null +++ b/code/datums/components/stationloving.dm @@ -0,0 +1,173 @@ +/// Teleports the movable atom back to a safe turf on the station if it leaves the z-level or becomes inaccessible. +/datum/component/stationloving + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// If TRUE, notifies admins when parent is teleported back to the station. + var/inform_admins = FALSE + var/disallow_soul_imbue = TRUE + /// If FALSE, prevents parent from being qdel'd unless it's a force = TRUE qdel. + var/allow_item_destruction = FALSE + +/datum/component/stationloving/Initialize(inform_admins = FALSE, allow_item_destruction = FALSE) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + src.inform_admins = inform_admins + src.allow_item_destruction = allow_item_destruction + + // Just in case something is being created outside of station/centcom + if(!atom_in_bounds(parent)) + relocate() + +/datum/component/stationloving/RegisterWithParent() + RegisterSignal(parent, COMSIG_PREQDELETED, PROC_REF(on_parent_pre_qdeleted)) + RegisterSignal(parent, COMSIG_ITEM_MARK_RETRIEVAL, PROC_REF(check_mark_retrieval)) + // Relocate when we become unreachable + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_moved)) + // Relocate when our loc, or any of our loc's locs, becomes unreachable + var/static/list/loc_connections = list( + COMSIG_MOVABLE_MOVED = PROC_REF(on_parent_moved), + SIGNAL_ADDTRAIT(TRAIT_SECLUDED_LOCATION) = PROC_REF(on_loc_secluded), + ) + AddComponent(/datum/component/connect_containers, parent, loc_connections) + +/datum/component/stationloving/UnregisterFromParent() + UnregisterSignal(parent, list( + COMSIG_MOVABLE_Z_CHANGED, + COMSIG_PREQDELETED, + COMSIG_ITEM_IMBUE_SOUL, + COMSIG_ITEM_MARK_RETRIEVAL, + COMSIG_MOVABLE_MOVED, + )) + + qdel(GetComponent(/datum/component/connect_containers)) + +/datum/component/stationloving/InheritComponent(datum/component/stationloving/newc, original, inform_admins, allow_death) + if(original) + if(newc) + inform_admins = newc.inform_admins + allow_death = newc.allow_item_destruction + else + inform_admins = inform_admins + +/// Teleports parent to a safe turf on the station z-level. +/datum/component/stationloving/proc/relocate() + + var/target_turf = find_safe_turf() //Fallback. Mostly for debug maps. + + if(!target_turf) + if(GLOB.blobstart.len > 0) + target_turf = get_turf(pick(GLOB.blobstart)) + else + CRASH("Unable to find a blobstart landmark for [type] to relocate [parent].") + + var/atom/movable/movable_parent = parent + playsound(movable_parent, 'sound/machines/synth_no.ogg', 5, TRUE) + + var/mob/holder = get(movable_parent, /mob) + if(holder) + to_chat(holder, span_danger("You can't help but feel that you just lost something back there...")) + holder.temporarily_remove_item_from_inventory(parent, TRUE) // prevents ghost diskie + + movable_parent.forceMove(target_turf) + + return target_turf + +/// Signal proc for [COMSIG_MOVABLE_MOVED], called when our parent moves, or our parent's loc, or our parent's loc loc... +/// To check if our disk is moving somewhere it shouldn't be, such as off Z level, or into an invalid area +/datum/component/stationloving/proc/on_parent_moved(atom/movable/source, turf/old_turf) + SIGNAL_HANDLER + + if(atom_in_bounds(source)) + return + + var/turf/current_turf = get_turf(source) + var/turf/new_destination = relocate() + // Our turf actually didn't change, so it's more likely we became secluded + if(current_turf == old_turf) + log_game("[parent] moved out of bounds at [loc_name(current_turf)], becoming inaccessible / secluded. \ + Moving it to [loc_name(new_destination)].") + + if(inform_admins) + message_admins("[parent] moved out of bounds at [ADMIN_VERBOSEJMP(current_turf)], becoming inaccessible / secluded. \ + Moving it to [ADMIN_VERBOSEJMP(new_destination)].") + + // Our locs changes, we did in fact move somewhere + else + log_game("[parent] attempted to be moved out of bounds from [loc_name(old_turf)] \ + to [loc_name(current_turf)]. Moving it to [loc_name(new_destination)].") + + if(inform_admins) + message_admins("[parent] attempted to be moved out of bounds from [ADMIN_VERBOSEJMP(old_turf)] \ + to [ADMIN_VERBOSEJMP(current_turf)]. Moving it to [ADMIN_VERBOSEJMP(new_destination)].") + +/// Signal proc for [SIGNAL_ADDTRAIT], via [TRAIT_SECLUDED_LOCATION] on our locs, to ensure nothing funky happens +/datum/component/stationloving/proc/on_loc_secluded(atom/movable/source) + SIGNAL_HANDLER + + var/turf/new_destination = relocate() + log_game("[parent] moved out of bounds at [loc_name(source)], becoming inaccessible / secluded. \ + Moving it to [loc_name(new_destination)].") + + if(inform_admins) + message_admins("[parent] moved out of bounds at [ADMIN_VERBOSEJMP(source)], becoming inaccessible / secluded. \ + Moving it to [ADMIN_VERBOSEJMP(new_destination)].") + +/datum/component/stationloving/proc/check_mark_retrieval(datum/source) + SIGNAL_HANDLER + + return COMPONENT_BLOCK_MARK_RETRIEVAL + +/// Checks whether a given atom's turf is within bounds. Returns TRUE if it is, FALSE if it isn't. +/datum/component/stationloving/proc/atom_in_bounds(atom/atom_to_check) + // Typecache of shuttles that we allow the disk to stay on + var/static/list/allowed_shuttles = typecacheof(list( + /area/shuttle/syndicate, + /area/shuttle/escape, + /area/shuttle/pod_1, + /area/shuttle/pod_2, + /area/shuttle/pod_3, + /area/shuttle/pod_4, + )) + // Typecache of areas on the centcom Z-level that we allow the disk to stay on + var/static/list/disallowed_centcom_areas = typecacheof(list( + /area/abductor_ship + )) + + // Our loc is a secluded location = not in bounds + if(atom_to_check.loc && HAS_TRAIT(atom_to_check.loc, TRAIT_SECLUDED_LOCATION)) + return FALSE + // No turf below us = nullspace = not in bounds + var/turf/destination_turf = get_turf(atom_to_check) + if(!destination_turf) + return FALSE + if(is_station_level(destination_turf.z)) + return TRUE + if(atom_to_check.onSyndieBase()) + return TRUE + + var/area/destination_area = destination_turf.loc + if(is_admin_level(destination_turf.z)) + if(is_type_in_typecache(destination_area, disallowed_centcom_areas)) + return FALSE + return TRUE + return FALSE + +/// Signal handler for before the parent is qdel'd. Can prevent the parent from being deleted where allow_item_destruction is FALSE and force is FALSE. +/datum/component/stationloving/proc/on_parent_pre_qdeleted(datum/source, force) + SIGNAL_HANDLER + + var/turf/current_turf = get_turf(parent) + + if(force && inform_admins) + message_admins("[parent] has been !!force deleted!! in [ADMIN_VERBOSEJMP(current_turf)].") + log_game("[parent] has been !!force deleted!! in [loc_name(current_turf)].") + + if(force || allow_item_destruction) + return FALSE + + var/turf/new_turf = relocate() + log_game("[parent] has been destroyed in [loc_name(current_turf)]. \ + Preventing destruction and moving it to [loc_name(new_turf)].") + if(inform_admins) + message_admins("[parent] has been destroyed in [ADMIN_VERBOSEJMP(current_turf)]. \ + Preventing destruction and moving it to [ADMIN_VERBOSEJMP(new_turf)].") + return TRUE diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index ccb7c665b23..9fae7c22d2c 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -396,9 +396,6 @@ GLOBAL_VAR_INIT(record_id_num, 1001) if(JOB_TITLE_LIBRARIAN) clothes_s = new /icon('icons/mob/clothing/uniform.dmi', "red_suit_s") clothes_s.Blend(new /icon('icons/mob/clothing/feet.dmi', "black"), ICON_UNDERLAY) - if(JOB_TITLE_BARBER) - clothes_s = new /icon('icons/mob/clothing/uniform.dmi', "barber_s") - clothes_s.Blend(new /icon('icons/mob/clothing/feet.dmi', "black"), ICON_UNDERLAY) if(JOB_TITLE_CLOWN) clothes_s = new /icon('icons/mob/clothing/uniform.dmi', "clown_s") clothes_s.Blend(new /icon('icons/mob/clothing/feet.dmi', "clown"), ICON_UNDERLAY) diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 47eaf9f9997..e8db8eed27d 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -431,7 +431,7 @@ if(DA) if(islist(DA)) var/index = name - if(value) + if(!isnull(value)) name = DA[name] // name is really the index until this line else value = DA[name] @@ -484,17 +484,23 @@ var/val if(IS_NORMAL_LIST(L) && !isnum(key)) val = L[key] - if(!val) + if(isnull(val)) val = key key = i items += debug_variable(key, val, level + 1, sanitize = sanitize) - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + if(isdatum(name)) + item = "[VV_HTML_ENCODE(name)] = /list ([length(L)])" + else + item = "[VV_HTML_ENCODE(name)] = /list ([length(L)])" else item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + else if(name in GLOB.bitfields) + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(translate_bitfield(VV_BITFIELD, name, value))]" + else item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" @@ -880,33 +886,15 @@ var/atom/A = locateUID(href_list["addreagent"]) - if(!A.reagents) - var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num - if(amount) - A.create_reagents(amount) - - if(A.reagents) - var/chosen_id - var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) - switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) - if("Enter ID") - var/valid_id - while(!valid_id) - chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") - if(!chosen_id) //Get me out of here! - break - for(var/ID in reagent_options) - if(ID == chosen_id) - valid_id = 1 - if(!valid_id) - to_chat(usr, "A reagent with that ID doesn't exist!", confidential=TRUE) - if("Choose ID") - chosen_id = tgui_input_list(usr, "Choose a reagent to add.", "Choose a reagent.", reagent_options) - if(chosen_id) - var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num - if(amount) - A.reagents.add_reagent(chosen_id, amount) - log_and_message_admins("has added [amount] units of [chosen_id] to \the [A]") + try_add_reagent(A) + + else if(href_list["editreagents"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + + var/atom/A = locateUID(href_list["editreagents"]) + + try_open_reagent_editor(A) else if(href_list["explode"]) if(!check_rights(R_DEBUG|R_EVENT)) return diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index 84ab41a69e6..8820a133fbc 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -132,3 +132,7 @@ if(istype(Clothing) && prob(100 * (1 - Clothing.permeability_coefficient))) return TRUE return FALSE + + +/mob/proc/check_smart_brain() + return FALSE diff --git a/code/datums/diseases/ectoplasmic.dm b/code/datums/diseases/ectoplasmic.dm index 06b17032931..8d6ecf3b649 100644 --- a/code/datums/diseases/ectoplasmic.dm +++ b/code/datums/diseases/ectoplasmic.dm @@ -47,7 +47,7 @@ create_effect = TRUE if(5) if(prob(SYMPTOM_ACTIVATION_PROB * 10)) - human.influenceSin() + human.mind?.add_antag_datum(/datum/antagonist/sintouched) to_chat(human, span_revenbignotice("You suddenly feel your soul become corrupted.")) else human.apply_damage(80, STAMINA) diff --git a/code/datums/diseases/viruses/advance/symptoms/blood.dm b/code/datums/diseases/viruses/advance/symptoms/blood.dm index 1369d0f640b..fdd5f8bcc66 100644 --- a/code/datums/diseases/viruses/advance/symptoms/blood.dm +++ b/code/datums/diseases/viruses/advance/symptoms/blood.dm @@ -35,6 +35,6 @@ Bonus if(prob(10)) to_chat(affected, span_notice("You can hear own heartbeat")) if(!HAS_TRAIT(affected, TRAIT_NO_BLOOD) && !HAS_TRAIT(affected, TRAIT_NO_BLOOD_RESTORE) && affected.blood_volume < BLOOD_VOLUME_NORMAL) - affected.blood_volume += 0.4 + affected.AdjustBlood(0.4) affected.adjust_nutrition(-2) diff --git a/code/datums/diseases/viruses/babylon_fever.dm b/code/datums/diseases/viruses/babylon_fever.dm index e9c01f3ee95..ad335255f0c 100644 --- a/code/datums/diseases/viruses/babylon_fever.dm +++ b/code/datums/diseases/viruses/babylon_fever.dm @@ -82,21 +82,31 @@ stored_languages = LAZYCOPY(affected_mob.languages) - for(var/datum/language/lan as anything in affected_mob.languages) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = affected_mob.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(translator?.given_languages) + stored_languages ^= translator.given_languages // you can't forget it, because it's on the chip in translator + + for(var/datum/language/lan as anything in stored_languages) affected_mob.remove_language(lan.name) -/datum/disease/virus/babylonian_fever/proc/store_language(datum/signal_source, language_name) +/datum/disease/virus/babylonian_fever/proc/store_language(datum/signal_source, language_name, lang_flags) SIGNAL_HANDLER + if(lang_flags & COMSIG_LANG_SECURED) + return + var/datum/language/new_language = GLOB.all_languages[language_name] LAZYOR(stored_languages, new_language) return DISEASE_MOB_LANGUAGE_PROCESSED -/datum/disease/virus/babylonian_fever/proc/remove_language(datum/signal_source, language_name) +/datum/disease/virus/babylonian_fever/proc/remove_language(datum/signal_source, language_name, lang_flags) SIGNAL_HANDLER + if(lang_flags & COMSIG_LANG_SECURED) + return + var/datum/language/rem_language = GLOB.all_languages[language_name] LAZYREMOVE(stored_languages, rem_language) return DISEASE_MOB_LANGUAGE_PROCESSED diff --git a/code/datums/elements/devil_banishment.dm b/code/datums/elements/devil_banishment.dm new file mode 100644 index 00000000000..4fefe4d79d9 --- /dev/null +++ b/code/datums/elements/devil_banishment.dm @@ -0,0 +1,51 @@ +/datum/element/devil_banishment + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE + id_arg_index = 2 + + var/linked_timer + +/datum/element/devil_banishment/Attach(datum/target) + . = ..() + var/mob/living/carbon/human = target + + if(!istype(human) || !human.mind?.has_antag_datum(/datum/antagonist/devil)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(human, COMSIG_LIVING_EARLY_DEATH, PROC_REF(pre_death)) + +/datum/element/devil_banishment/Detach(datum/target) + . = ..() + + UnregisterSignal(target, COMSIG_LIVING_EARLY_DEATH) + +/datum/element/devil_banishment/proc/pre_death(datum/source, gibbed) + SIGNAL_HANDLER + + if(gibbed || linked_timer) + return + + var/mob/living/carbon/human = source + var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil) + + if(!devil?.info) + return + + playsound(get_turf(human), 'sound/magic/vampire_anabiosis.ogg', 50, 0, TRUE) + linked_timer = addtimer(CALLBACK(src, PROC_REF(try_banishment), human, devil), devil.rank.regen_threshold / 2, TIMER_LOOP | TIMER_STOPPABLE | TIMER_DELETE_ME) + +/datum/element/devil_banishment/proc/try_banishment(mob/living/carbon/human, datum/antagonist/devil/devil) + if(human.health >= human.maxHealth) + stop_banishment_check() + return + + if(!devil.info.banish.check_banishment()) + return + + human.dust() + +/datum/element/devil_banishment/proc/stop_banishment_check() + if(!linked_timer) + return + + deltimer(linked_timer) + linked_timer = null diff --git a/code/datums/elements/devil_regen.dm b/code/datums/elements/devil_regen.dm new file mode 100644 index 00000000000..1d5de2d95bb --- /dev/null +++ b/code/datums/elements/devil_regen.dm @@ -0,0 +1,113 @@ +/datum/element/devil_regeneration + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE + id_arg_index = 2 + + var/linked_timer + var/list/sounds = list('sound/magic/demon_consume.ogg', 'sound/effects/attackblob.ogg') + +/datum/element/devil_regeneration/Attach(datum/target) + . = ..() + var/mob/living/carbon/human = target + + if(!istype(human) || !human.mind?.has_antag_datum(/datum/antagonist/devil)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(human, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(start_regen_bodypart)) + RegisterSignal(human, COMSIG_LIVING_EARLY_DEATH, PROC_REF(pre_death)) + + var/obj/item/organ/internal/brain/brain = human.get_organ_slot(INTERNAL_ORGAN_BRAIN) + brain?.decoy_brain = TRUE + +/datum/element/devil_regeneration/Detach(datum/target) + . = ..() + + UnregisterSignal(target, COMSIG_CARBON_LOSE_ORGAN) + UnregisterSignal(target, COMSIG_LIVING_EARLY_DEATH) + + if(!iscarbon(target)) + return + + var/mob/living/carbon/carbon = target + var/obj/item/organ/internal/brain/brain = carbon.get_organ_slot(INTERNAL_ORGAN_BRAIN) + + brain?.decoy_brain = FALSE + +/datum/element/devil_regeneration/proc/start_regen_bodypart(datum/source, obj/item/organ/organ) + SIGNAL_HANDLER + + var/obj/item/organ/external/external = organ + if(!istype(external)) + return + + var/mob/living/carbon/human = source + var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil) + + if(!devil) + return + + addtimer(CALLBACK(src, PROC_REF(regen_bodypart), human, external, devil), devil.rank.regen_threshold) + +/datum/element/devil_regeneration/proc/regen_bodypart( + mob/living/carbon/human, + obj/item/organ/external/external, + datum/antagonist/devil/devil + ) + external = new external(human) + human.heal_overall_damage(devil.rank.regen_amount, devil.rank.regen_amount) + + playsound(get_turf(human), pick(sounds), 50, 0, TRUE) + update_status(human) + +/datum/element/devil_regeneration/proc/pre_death(datum/source, gibbed) + SIGNAL_HANDLER + + if(gibbed || linked_timer) + return + + var/mob/living/carbon/human = source + var/datum/antagonist/devil/devil = human?.mind?.has_antag_datum(/datum/antagonist/devil) + + if(!devil) + return + + to_chat(human, span_revenbignotice("Сверхъестественные силы предотвращают вашу смерть.")) + playsound(get_turf(human), 'sound/magic/vampire_anabiosis.ogg', 50, 0, TRUE) + + linked_timer = addtimer(CALLBACK(src, PROC_REF(apply_regeneration), human, devil), devil.rank.regen_threshold, TIMER_LOOP | TIMER_STOPPABLE | TIMER_DELETE_ME) + +/datum/element/devil_regeneration/proc/on_revive() + if(!linked_timer) + return + + deltimer(linked_timer) + linked_timer = null + +/datum/element/devil_regeneration/proc/apply_regeneration(mob/living/carbon/human, datum/antagonist/devil/devil) + if(human.health >= human.maxHealth) + on_revive() + + human.setOxyLoss(0) + human.heal_damages( + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + devil.rank.regen_amount, + TRUE, + TRUE + ) + + if(ishuman(human)) + var/mob/living/carbon/human/mob = human + mob.check_and_regenerate_organs() + + playsound(get_turf(human), pick(sounds), 50, 0, TRUE) + update_status(human) + +/datum/element/devil_regeneration/proc/update_status(mob/living/carbon/human) + human.updatehealth() + human.UpdateDamageIcon() diff --git a/code/datums/elements/reagent_attack.dm b/code/datums/elements/reagent_attack.dm new file mode 100644 index 00000000000..7e459a72224 --- /dev/null +++ b/code/datums/elements/reagent_attack.dm @@ -0,0 +1,144 @@ +/datum/element/reagent_attack + element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH_ON_HOST_DESTROY + id_arg_index = 2 + /// Which reagent we will inject + var/reagent_id + /// How much reagent we will inject + var/reagent_amount + /// Will we inject anyway or check can_inject + var/piercing + /// Limitation of our reagent in target + var/reagent_limit + /// Override attacking zones + var/list/allowed_zones + +/datum/element/reagent_attack/Attach( + atom/source, + reagent_id, + reagent_amount, + piercing, + reagent_limit, + list/allowed_zones +) + . = ..() + + if(!isliving(source)) + return ELEMENT_INCOMPATIBLE + + src.reagent_id = reagent_id + src.reagent_amount = reagent_amount + src.piercing = piercing + src.reagent_limit = reagent_limit + src.allowed_zones = allowed_zones + + RegisterSignal(source, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(mob_attack)) + +/datum/element/reagent_attack/Detach(atom/source) + . = ..() + UnregisterSignal(source, COMSIG_LIVING_UNARMED_ATTACK) + +/datum/element/reagent_attack/proc/mob_attack(datum/source, mob/target, proximity_flag) + SIGNAL_HANDLER + + var/mob/mob = source + + if(!proximity_flag && mob.a_intent != INTENT_HARM) + return + + var/picked_zone = allowed_zones ? pick(allowed_zones) : mob.zone_selected + + if(!can_inject(target, picked_zone)) + return + + INVOKE_ASYNC(src, PROC_REF(pre_inject), mob, target, picked_zone) + +/datum/element/reagent_attack/proc/can_inject(mob/living/carbon/target, target_zone) + if(!istype(target)) + return FALSE + + if(!target.reagents) + return FALSE + + if(reagent_limit && target.reagents?.has_reagent(reagent_id, reagent_limit)) + return FALSE + + if(!piercing && !target.can_inject(null, FALSE, target_zone, FALSE)) + return FALSE + + return TRUE + +/datum/element/reagent_attack/proc/pre_inject( + mob/source, + mob/living/carbon/target, + target_zone, + reagent_id, + reagent_amount + ) + + if(!inject(source, target, target_zone)) + return FALSE + + SEND_SIGNAL(source, COMSIG_REAGENT_INJECTED, target, reagent_id, reagent_amount, target_zone) + return TRUE + +/datum/element/reagent_attack/proc/inject( + mob/source, + mob/living/carbon/target, + target_zone, + reagent_id, + reagent_amount + ) + if(target.reagents.add_reagent(reagent_id || src.reagent_id, reagent_amount || src.reagent_amount)) + return TRUE + + return FALSE + +/datum/element/reagent_attack/bee + reagent_id = "beetoxin" + reagent_amount = 5 + +/datum/element/reagent_attack/bee/pre_inject( + mob/source, + mob/living/carbon/target, + target_zone, + reagent_id, + reagent_amount + ) + var/mob/living/simple_animal/hostile/poison/bees/bee = source + + if(!bee.beegent) + return ..() + + reagent_id = bee.beegent.id // Set parameters and send them into parent without initial() + reagent_amount = rand(1, 5) + + if(!..()) + return FALSE + + bee.beegent.reaction_mob(target, REAGENT_INGEST) + return TRUE + +/datum/element/reagent_attack/widow + reagent_id = "terror_black_toxin" + reagent_limit = 100 + reagent_amount = 20 + allowed_zones = list(BODY_ZONE_CHEST, BODY_ZONE_HEAD) + +/datum/element/reagent_attack/widow/pre_inject( + mob/source, + mob/living/carbon/target, + target_zone, + reagent_id, + reagent_amount + ) + if(!HAS_TRAIT(target, TRAIT_INCAPACITATED)) + source.visible_message(span_danger("[src] pierces armour and buries its long fangs deep into the [target_zone] of [target]!")) + return ..() + + reagent_amount = 33 + + if(!..()) + return FALSE + + source.visible_message(span_danger("[src] buries its long fangs deep into the [target_zone] of [target]!")) + return TRUE diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index 0dabede699d..3156376602f 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -360,7 +360,8 @@ var/key = "[item.icon],[item.icon_state]" if(!(key in base64_cache)) base64_cache[key] = icon2base64(icon(item.icon, item.icon_state, dir = SOUTH, frame = 1, moving = FALSE)) - result["icon"] = base64_cache[key] + result["icon"] = item.icon + result["icon_state"] = item.icon_state result["name"] = item.name var/real_alts = item_data.get_alternate_actions(owner, user) diff --git a/code/datums/emote/emote_verbs.dm b/code/datums/emote/emote_verbs.dm index 4913cbcb264..b55ba3d9328 100644 --- a/code/datums/emote/emote_verbs.dm +++ b/code/datums/emote/emote_verbs.dm @@ -99,11 +99,6 @@ set category = "Эмоции" emote("clap", intentional = TRUE) -/mob/living/carbon/human/verb/emote_fart() - set name = "▷ Пернуть " - set category = "Эмоции" - emote("fart", intentional = TRUE) - /mob/living/carbon/human/verb/emote_crack() set name = "▷ Хрустеть пальцами " set category = "Эмоции" diff --git a/code/datums/keybindings/emote.dm b/code/datums/keybindings/emote.dm index ccda0a6d7c2..d3ea857b495 100644 --- a/code/datums/keybindings/emote.dm +++ b/code/datums/keybindings/emote.dm @@ -448,10 +448,6 @@ linked_emote = /datum/emote/living/carbon/human/hem name = "Хмыкнуть" -/datum/keybinding/emote/carbon/human/fart - linked_emote = /datum/emote/living/carbon/human/fart - name = "Пёрнуть" - /datum/keybinding/emote/carbon/human/scratch linked_emote = /datum/emote/living/carbon/human/scratch name = "Почесаться" diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index a61e7196955..3c3b506288f 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -13,3 +13,12 @@ mid_length = 3 end_sound = 'sound/machines/engine/engine_end.ogg' volume = 20 + +/datum/looping_sound/port_gen + start_sound = 'sound/machines/generator/generator_start.ogg' + start_length = 4 + mid_sounds = list('sound/machines/generator/generator_mid1.ogg' = 1, 'sound/machines/generator/generator_mid2.ogg' = 1, 'sound/machines/generator/generator_mid3.ogg' = 1) + mid_length = 4 + end_sound = 'sound/machines/generator/generator_end.ogg' + volume = 40 + diff --git a/code/datums/mind.dm b/code/datums/mind.dm index e998b43c6cc..2b24f9b19df 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -62,7 +62,6 @@ var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD var/datum/mindslaves/som //stands for slave or master...hush.. - var/datum/devilinfo/devilinfo //Information about the devil, if any. var/damnation_type = 0 var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src var/hasSoul = TRUE @@ -504,16 +503,15 @@ /datum/mind/proc/memory_edit_devil(mob/living/H) . = _memory_edit_header("devil", list("devilagents")) if(src in SSticker.mode.devils) + var/datum/antagonist/devil/devilinfo = has_antag_datum(/datum/antagonist/devil) if(!devilinfo) . += "No devilinfo found! Yell at a coder!" - else if(!devilinfo.ascendable) - . += "DEVIL|Ascendable Devil|sintouched|no" else - . += "DEVIL|ASCENDABLE DEVIL|sintouched|no" + . += "DEVIL|sintouched|no" else if(src in SSticker.mode.sintouched) - . += "devil|Ascendable Devil|SINTOUCHED|no" + . += "devil|SINTOUCHED|no" else - . += "devil|Ascendable Devil|sintouched|NO" + . += "devil|sintouched|NO" . += _memory_edit_role_enabled(ROLE_DEVIL) @@ -542,8 +540,13 @@ . += "|burst blob" else if(isblobovermind(src)) var/mob/camera/blob/blob_overmind = current - . += "|BLOB Overmind|" - . += "
Total points: [blob_overmind.blob_points]/[blob_overmind.max_blob_points]" + if(istype(blob_overmind)) + . += "|BLOB Overmind|" + . += "
Total points: [blob_overmind.blob_points]/[blob_overmind.max_blob_points]" + . += "
Infinity points: [(blob_overmind.is_infinity)? "ON" : "OFF"]" + . += "
Blob strain: [blob_overmind.blobstrain? "[blob_overmind?.blobstrain?.name]" : "None"]" + else if(isblobminion(src)) + . += "|BLOB Minion|" else if(current.can_be_blob()) . += "blobize|NO" . += _memory_edit_role_enabled(ROLE_BLOB) @@ -1901,47 +1904,24 @@ if("clear") if(src in SSticker.mode.devils) log_admin("[key_name(usr)] has de-devil'ed [current].") + else if(src in SSticker.mode.sintouched) message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") log_admin("[key_name(usr)] has de-sintouch'ed [current].") + remove_devil_role() if("devil") - if(devilinfo) - devilinfo.ascendable = FALSE - message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, FALSE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) + if(has_antag_datum(/datum/antagonist/devil)) + return + + add_antag_datum(/datum/antagonist/devil) message_admins("[key_name_admin(usr)] has devil'ed [current].") log_admin("[key_name(usr)] has devil'ed [current].") - if("ascendable_devil") - if(devilinfo) - devilinfo.ascendable = TRUE - message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, TRUE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) - message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") if("sintouched") - var/mob/living/carbon/human/H = current - H.influenceSin() + if(has_antag_datum(/datum/antagonist/sintouched)) + return + + add_antag_datum(/datum/antagonist/sintouched) message_admins("[key_name_admin(usr)] has sintouch'ed [current].") log_admin("[key_name(usr)] has sintouch'ed [current].") @@ -2435,9 +2415,9 @@ add_conversion_logs(current, "De-blobed") if("blob") - var/burst_time = input(usr, "Введите время до вылупления","Time:", TIME_TO_BURST_ADDED_HIGHT) as num|null - var/need_new_blob = alert(usr,"Нужно ли выбирать блоба из экипажа в случае попытки вылупления за пределами станции?", "", "Да", "Нет") == "Нет" - var/start_process = alert(usr,"Начинать отсчет до момента вылупления?", "", "Да", "Нет") == "Да" + var/burst_time = tgui_input_number(usr, "Введите время до вылупления","Time:", TIME_TO_BURST_ADDED_HIGHT) + var/need_new_blob = tgui_alert(usr, "Нужно ли выбирать блоба из экипажа в случае попытки вылупления за пределами станции?", "", list("Да", "Нет")) == "Нет" + var/start_process = tgui_alert(usr,"Начинать отсчет до момента вылупления?", "", list("Да", "Нет")) == "Да" if(isnull(burst_time) || QDELETED(current) || current.stat == DEAD) return var/datum_type = get_blob_infected_type() @@ -2451,9 +2431,9 @@ message_admins("[key_name_admin(usr)] has made [key_name_admin(current)] into a \"Blob\"") if("burst") - var/warn_blob = alert(usr,"Предупреждать блоба при попытке вылупления за пределами станции?", "", "Да", "Нет") != "Да" - var/need_new_blob = alert(usr,"Нужно ли выбирать блоба из экипажа в случае попытки вылупления за пределами станции?", "", "Да", "Нет") == "Да" - if(alert(usr,"Вы действительно хотите лопнуть блоба? Это уничтожит персонажа игрока и превратит его в блоба.", "", "Да", "Нет") == "Да") + var/warn_blob = tgui_alert(usr,"Предупреждать блоба при попытке вылупления за пределами станции?", "", list("Да", "Нет")) != "Да" + var/need_new_blob = tgui_alert(usr,"Нужно ли выбирать блоба из экипажа в случае попытки вылупления за пределами станции?", "", list("Да", "Нет")) == "Да" + if(tgui_alert(usr,"Вы действительно хотите лопнуть блоба? Это уничтожит персонажа игрока и превратит его в блоба.", "", list("Да", "Нет")) == "Да") var/datum/antagonist/blob_infected/blob = has_antag_datum(/datum/antagonist/blob_infected) if(!blob) return @@ -2467,13 +2447,34 @@ if(!isblobovermind(src)) return var/mob/camera/blob/blob_overmind = current - var/blob_points = input(usr, "Введите новое число очков в диапазоне от 0 до [blob_overmind.max_blob_points]","Count:", blob_overmind.blob_points) as num|null + var/blob_points = tgui_input_number(usr, "Введите новое число очков в диапазоне от 0 до [blob_overmind.max_blob_points]", "Count:", blob_overmind.blob_points, blob_overmind.max_blob_points, 0) if(isnull(blob_points) || QDELETED(current) || current.stat == DEAD) return blob_overmind.blob_points = clamp(blob_points, 0, blob_overmind.max_blob_points) log_admin("[key_name(usr)] set blob points to [key_name(current)] as [blob_overmind.blob_points]") message_admins("[key_name_admin(usr)] set blob points to [key_name_admin(current)] as [blob_overmind.blob_points]") + if("inf_points") + if(!isblobovermind(src)) + return + var/mob/camera/blob/blob_overmind = current + if(QDELETED(current) || current.stat == DEAD) + return + blob_overmind.is_infinity = !blob_overmind.is_infinity + log_admin("[key_name(usr)] make blob points [blob_overmind.is_infinity? "infinity" : "not infinity"] to [key_name(current)]") + message_admins("[key_name_admin(usr)] make blob points [blob_overmind.is_infinity? "infinity" : "not infinity"] to [key_name_admin(current)]") + + if("select_strain") + if(!isblobovermind(src)) + return + var/mob/camera/blob/blob_overmind = current + if(QDELETED(current) || current.stat == DEAD) + return + var/strain = tgui_input_list(usr, "Выберите штамм", "Выбор штамма", GLOB.valid_blobstrains, null) + if(ispath(strain)) + blob_overmind.set_strain(strain) + log_admin("[key_name(usr)] changed the strain to [strain] for [key_name(current)]") + message_admins("[key_name_admin(usr)] changed the strain to [strain] for [key_name_admin(current)]") else if(href_list["common"]) switch(href_list["common"]) @@ -2582,8 +2583,11 @@ */ /datum/mind/proc/remove_antag_datum(datum_type) var/datum/antagonist/antag = has_antag_datum(datum_type) - if(antag) - qdel(antag) + + if(!antag) + return + + qdel(antag) /** @@ -2674,27 +2678,10 @@ /datum/mind/proc/remove_devil_role() if(src in SSticker.mode.devils) - if(istype(current,/mob/living/carbon/true_devil/)) - else - SSticker.mode.devils -= src - SSticker.mode.update_devil_icons_removed(src) - special_role = null - RemoveSpell(/obj/effect/proc_holder/spell/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) - RemoveSpell(/obj/effect/proc_holder/spell/summon_contract) - RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork) - RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork/greater) - RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/pitchfork/ascended) - RemoveSpell(/obj/effect/proc_holder/spell/conjure_item/violin) - RemoveSpell(/obj/effect/proc_holder/spell/summon_dancefloor) - RemoveSpell(/obj/effect/proc_holder/spell/sintouch) - RemoveSpell(/obj/effect/proc_holder/spell/sintouch/ascended) - if(issilicon(current)) - var/mob/living/silicon/S = current - S.laws.clear_sixsixsix_laws() - devilinfo = null + remove_antag_datum(/datum/antagonist/devil) + else if(src in SSticker.mode.sintouched) - SSticker.mode.sintouched -= src + remove_antag_datum(/datum/antagonist/sintouched) /datum/mind/proc/remove_contractor_role() @@ -3094,6 +3081,7 @@ if(!mind.name) mind.name = real_name mind.current = src + SEND_SIGNAL(src, COMSIG_MOB_MIND_INITIALIZED, mind) //HUMAN /mob/living/carbon/human/mind_initialize() diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 6d88b312dc1..08055b354c4 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -221,7 +221,7 @@ l_ear = /obj/item/radio/headset/ert/alt l_pocket = /obj/item/reagent_containers/hypospray/combat r_pocket = /obj/item/reagent_containers/food/snacks/candy/mre - glasses = /obj/item/clothing/glasses/hud/security/sunglasses/aviators + glasses = /obj/item/clothing/glasses/hud/blueshield id = /obj/item/card/id/centcom backpack_contents = list( @@ -1212,7 +1212,7 @@ suit = /obj/item/clothing/suit/space/hardsuit/singuloth back = /obj/item/storage/backpack/satchel l_hand = /obj/item/twohanded/knighthammer - belt = /obj/item/claymore/ceremonial + belt = /obj/item/melee/claymore/ceremonial gloves = /obj/item/clothing/gloves/combat/swat shoes = /obj/item/clothing/shoes/magboots mask = /obj/item/clothing/mask/breath diff --git a/code/datums/pod_style.dm b/code/datums/pod_style.dm new file mode 100644 index 00000000000..e9ba663f135 --- /dev/null +++ b/code/datums/pod_style.dm @@ -0,0 +1,258 @@ +/// Datum holding information about pod type visuals, VFX, name and description +/// These are not created anywhere and thus should not be assigned procs, only being used as data storage +/datum/pod_style + /// Name that pods of this style will be named by default + var/name = "supply pod" + /// Name that is displayed to admins in pod config panel + var/ui_name = "Стандартная" + /// Description assigned to droppods of this style + var/desc = "Капсула снабжения «Nanotrasen»." + /// Determines if this pod can use animations/masking/overlays + var/shape = POD_SHAPE_NORMAL + /// Base icon state assigned to this pod + var/icon_state = "pod" + /// Whenever this pod should have a door overlay added to it. Uses [icon_state]_door sprite + var/has_door = TRUE + /// Decals added to this pod, if any + var/decal_icon = "default" + /// Color that this pod glows when landing + var/glow_color = "yellow" + /// Type of rubble that this pod creates upon landing + var/rubble_type = RUBBLE_NORMAL + /// ID for TGUI data + var/id = "standard" + + var/list/ru_names = list( + NOMINATIVE = "капсула снабжения", + GENITIVE = "капсулы снабжения", + DATIVE = "капсуле снабжения", + ACCUSATIVE = "капсулу снабжения", + INSTRUMENTAL = "капсулой снабжения", + PREPOSITIONAL = "капсуле снабжения" + ) + + +/datum/pod_style/advanced + name = "bluespace supply pod" + ui_name = "Продвинутая" + desc = "Блюспейс капсула снабжения Nanotrasen. После доставки телепортируется обратно." + decal_icon = "bluespace" + glow_color = "blue" + id = "bluespace" + ru_names = list( + NOMINATIVE = "блюспейс капсула снабжения", + GENITIVE = "блюспейс капсулы снабжения", + DATIVE = "блюспейс капсуле снабжения", + ACCUSATIVE = "блюспейс капсулу снабжения", + INSTRUMENTAL = "блюспейс капсулой снабжения", + PREPOSITIONAL = "блюспейс капсуле снабжения" + ) + +/datum/pod_style/centcom + name = "\improper CentCom supply pod" + ui_name = "Nanotrasen" + desc = "Капсула снабжения «Nanotrasen», отмеченный обозначениями Центрального командования. После доставки телепортируется обратно." + decal_icon = "centcom" + glow_color = "blue" + id = "centcom" + ru_names = list( + NOMINATIVE = "капсула снабжения Центрального командования", + GENITIVE = "капсулы снабжения Центрального командования", + DATIVE = "капсуле снабжения Центрального командования", + ACCUSATIVE = "капсулу снабжения Центрального командования", + INSTRUMENTAL = "капсулой снабжения Центрального командования", + PREPOSITIONAL = "капсуле снабжения Центрального командования" + ) + +/datum/pod_style/syndicate + name = "blood-red supply pod" + ui_name = "Синдиката" + desc = "Устрашающая капсула снабжения, покрытая кроваво-красными знаками Синдиката. Наверное, лучше держаться подальше." + icon_state = "darkpod" + decal_icon = "syndicate" + glow_color = "red" + id = "syndicate" + ru_names = list( + NOMINATIVE = "кроваво-красная капсула снабжения", + GENITIVE = "кроваво-красной капсулы снабжения", + DATIVE = "кроваво-красной капсуле снабжения", + ACCUSATIVE = "кроваво-красную капсулу снабжения", + INSTRUMENTAL = "кроваво-красной капсулой снабжения", + PREPOSITIONAL = "кроваво-красной капсуле снабжения" + ) + +/datum/pod_style/deathsquad + name = "\improper Deathsquad drop pod" + ui_name = "Отряда Смерти" + desc = "Капсула Nanotrasen. На ней отмечена маркировка элитной ударной группы Nanotrasen." + icon_state = "darkpod" + decal_icon = "deathsquad" + glow_color = "blue" + id = "deathsquad" + ru_names = list( + NOMINATIVE = "капсула Отряда Смерти", + GENITIVE = "капсулы Отряда Смерти", + DATIVE = "капсуле Отряда Смерти", + ACCUSATIVE = "капсулу Отряда Смерти", + INSTRUMENTAL = "капсулой Отряда Смерти", + PREPOSITIONAL = "капсуле Отряда Смерти" + ) + +/datum/pod_style/cultist + name = "bloody supply pod" + ui_name = "Культистская" + desc = "Капсула снабжения Nanotrasen, вся в царапинах, крови и странных рунах." + decal_icon = "cultist" + glow_color = "red" + id = "cultist" + ru_names = list( + NOMINATIVE = "кровавая капсула снабжения", + GENITIVE = "кровавой капсулы снабжения", + DATIVE = "кровавой капсуле снабжения", + ACCUSATIVE = "кровавую капсулу снабжения", + INSTRUMENTAL = "кровавой капсулой снабжения", + PREPOSITIONAL = "кровавой капсуле снабжения" + ) + +/datum/pod_style/missile + name = "cruise missile" + ui_name = "Ракета" + desc = "Огромная ракета, которая, похоже, не взорвалась полностью. Вероятно, она была запущена из какой-то далекой ракетной шахты в дальнем космосе. Судя по всему, сбоку имеется люк для вспомогательной полезной нагрузки, хотя открыть его вручную, скорее всего, невозможно." + shape = POD_SHAPE_OTHER + icon_state = "missile" + has_door = FALSE + decal_icon = null + glow_color = null + rubble_type = RUBBLE_THIN + id = "missile" + ru_names = list( + NOMINATIVE = "крылатая ракета", + GENITIVE = "крылатой ракеты", + DATIVE = "крылатой ракете", + ACCUSATIVE = "крылатую ракету", + INSTRUMENTAL = "крылатой ракете", + PREPOSITIONAL = "крылатой ракетой" + ) + +/datum/pod_style/missile/syndicate + name = "\improper Syndicate cruise missile" + ui_name = "Ракета Синдиката" + desc = "Огромная кроваво-красная ракета, которая, похоже, не взорвалась полностью. Вероятно, она была запущена из какой-то ракетной шахты Синдиката в дальнем космосе. Судя по всему, сбоку имеется люк для вспомогательной полезной нагрузки, хотя открыть его вручную, скорее всего, невозможно." + icon_state = "smissile" + id = "syndie_missile" + ru_names = list( + NOMINATIVE = "крылатая ракета Синдиката", + GENITIVE = "крылатой ракеты Синдиката", + DATIVE = "крылатой ракете Синдиката", + ACCUSATIVE = "крылатую ракету Синдиката", + INSTRUMENTAL = "крылатой ракете Синдиката", + PREPOSITIONAL = "крылатой ракетой Синдиката" + ) + +/datum/pod_style/box + name = "\improper Aussec supply crate" + ui_name = "Ящик припасов" + desc = "Невероятно прочный ящик с припасами, рассчитанный на то, чтобы выдержать возвращение на орбиту. Сбоку выгравирована надпись «Aussec Armory — 2532»." + shape = POD_SHAPE_OTHER + icon_state = "box" + decal_icon = null + glow_color = null + rubble_type = RUBBLE_WIDE + id = "supply_box" + ru_names = list( + NOMINATIVE = "ящик с припасами Aussec", + GENITIVE = "ящика с припасами Aussec", + DATIVE = "ящику с припасами Aussec", + ACCUSATIVE = "ящик с припасами Aussec", + INSTRUMENTAL = "ящиком с припасами Aussec", + PREPOSITIONAL = "ящике с припасами Aussec" + ) + +/datum/pod_style/clown + name = "\improper HONK pod" + ui_name = "Капсула Клоунов" + desc = "Яркая капсула снабжения. Вероятно, она отправлена Федерацией клоунов." + icon_state = "clownpod" + decal_icon = "clown" + glow_color = "green" + id = "clown" + ru_names = list( + NOMINATIVE = "ХОНК капсула", + GENITIVE = "ХОНК капсулы", + DATIVE = "ХОНК капсуле", + ACCUSATIVE = "ХОНК капсулу", + INSTRUMENTAL = "ХОНК капсулой", + PREPOSITIONAL = "ХОНК капсуле" + ) + +/datum/pod_style/orange + name = "\improper Orange" + ui_name = "Фрукт" + desc = "Злой апельсин." + shape = POD_SHAPE_OTHER + icon_state = "orange" + decal_icon = null + glow_color = null + rubble_type = RUBBLE_WIDE + id = "orange" + ru_names = list( + NOMINATIVE = "апельсин", + GENITIVE = "апельсина", + DATIVE = "апельсину", + ACCUSATIVE = "апельсин", + INSTRUMENTAL = "апельсином", + PREPOSITIONAL = "апельсине" + ) + +/datum/pod_style/invisible + name = "\improper S.T.E.A.L.T.H. pod MKVII" + ui_name = "Невидимый" + desc = "Капсула снабжения, которая при нормальных обстоятельствах совершенно невидима для обычных методов обнаружения. Как ты вообще её видишь?" + shape = POD_SHAPE_OTHER + has_door = FALSE + icon_state = null + decal_icon = null + glow_color = null + rubble_type = RUBBLE_NONE + id = "invisible" + ru_names = list( + NOMINATIVE = "капсула S.T.E.A.L.T.H. MKVII", + GENITIVE = "капсулы S.T.E.A.L.T.H. MKVII", + DATIVE = "капсуле S.T.E.A.L.T.H. MKVII", + ACCUSATIVE = "капсулу S.T.E.A.L.T.H. MKVII", + INSTRUMENTAL = "капсулой S.T.E.A.L.T.H. MKVII", + PREPOSITIONAL = "капсуле S.T.E.A.L.T.H. MKVII" + ) + +/datum/pod_style/gondola + name = "gondola" + ui_name = "Гандола" + desc = "Бесшумный ходок. Кажется, это сотрудник агентства доставки." + shape = POD_SHAPE_OTHER + icon_state = "gondola" + has_door = FALSE + decal_icon = null + glow_color = null + rubble_type = RUBBLE_NONE + id = "gondola" + ru_names = list( + NOMINATIVE = "гандола", + GENITIVE = "гандолы", + DATIVE = "гандоле", + ACCUSATIVE = "гандолу", + INSTRUMENTAL = "гандолой", + PREPOSITIONAL = "гандоле" + ) + + +/datum/pod_style/seethrough + name = null + ui_name = "Смотрящий насквозь" + desc = null + shape = POD_SHAPE_OTHER + has_door = FALSE + icon_state = null + decal_icon = null + glow_color = null + rubble_type = RUBBLE_NONE + id = "seethrough" diff --git a/code/datums/rituals.dm b/code/datums/rituals.dm index b9bbb005f19..954fdda83e5 100644 --- a/code/datums/rituals.dm +++ b/code/datums/rituals.dm @@ -75,7 +75,7 @@ if(NONE) failed = TRUE - if(start_cooldown) + if(start_cooldown && cooldown_after_cast) COOLDOWN_START(src, ritual_cooldown, cooldown_after_cast) if(cause_disaster && prob(disaster_prob)) @@ -469,7 +469,7 @@ /datum/ritual/ashwalker/summon/proc/deal_damage() for(var/mob/living/carbon/human/summoner in range(finding_range, ritual_object)) - summoner.blood_volume -= (summoner.blood_volume * 0.20) + summoner.AdjustBlood(-(summoner.blood_volume * 0.20)) summoner.apply_damage(25, def_zone = pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) return TRUE diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index 934ef447204..daac771c9f0 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -47,7 +47,10 @@ if(!length(possible_spawners)) return var/obj/effect/mob_spawn/MS = locate(pick(possible_spawners)) - if(!MS || !istype(MS)) + if(!MS) + log_runtime(EXCEPTION("A ghost tried to interact with an invalid spawner, or the spawner didn't exist.")) + return + if(!istype(MS) && !(SEND_SIGNAL(MS, COMSIG_IS_GHOST_CONTROLABLE, usr) & COMPONENT_GHOST_CONTROLABLE)) log_runtime(EXCEPTION("A ghost tried to interact with an invalid spawner, or the spawner didn't exist.")) return switch(action) diff --git a/code/datums/spell_handler/devil_spell_handler.dm b/code/datums/spell_handler/devil_spell_handler.dm new file mode 100644 index 00000000000..303d661e959 --- /dev/null +++ b/code/datums/spell_handler/devil_spell_handler.dm @@ -0,0 +1,10 @@ +/datum/spell_handler/devil + +/datum/spell_handler/devil/can_cast(mob/living/carbon/user, charge_check, show_message, obj/effect/proc_holder/spell/spell) + if(!istype(user)) + return FALSE + + if(!user.mind?.has_antag_datum(/datum/antagonist/devil)) + return FALSE + + return TRUE diff --git a/code/datums/spells/devil.dm b/code/datums/spells/devil.dm index d2a513dd5a7..1df5e792bf1 100644 --- a/code/datums/spells/devil.dm +++ b/code/datums/spells/devil.dm @@ -1,9 +1,12 @@ /obj/effect/proc_holder/spell/conjure_item/pitchfork name = "Summon Pitchfork" desc = "A devil's weapon of choice. Use this to summon/unsummon your pitchfork." + item_type = /obj/item/twohanded/pitchfork/demonic + action_icon_state = "pitchfork" action_background_icon_state = "bg_demon" + human_req = FALSE /obj/effect/proc_holder/spell/conjure_item/pitchfork/greater @@ -15,27 +18,35 @@ /obj/effect/proc_holder/spell/conjure_item/violin - item_type = /obj/item/instrument/violin/golden + name = "Summon golden violin" desc = "A devil's instrument of choice. Use this to summon/unsummon your golden violin." + + item_type = /obj/item/instrument/violin/golden + invocation_type = "whisper" invocation = "I ain't have this much fun since Georgia." + action_icon_state = "golden_violin" - name = "Summon golden violin" action_background_icon_state = "bg_demon" /obj/effect/proc_holder/spell/summon_contract name = "Summon infernal contract" desc = "Skip making a contract by hand, just do it by magic." + invocation_type = "whisper" invocation = "Just sign on the dotted line." - selection_activated_message = "You prepare a detailed contract. Click on a target to summon the contract in his hands." - selection_deactivated_message = "You archive the contract for later use." + + selection_activated_message = span_notice("You prepare a detailed contract. Click on a target to summon the contract in his hands.") + selection_deactivated_message = span_notice("You archive the contract for later use.") + clothes_req = FALSE human_req = FALSE + school = "conjuration" base_cooldown = 15 SECONDS cooldown_min = 1 SECONDS + action_icon_state = "spell_default" action_background_icon_state = "bg_demon" need_active_overlay = TRUE @@ -57,41 +68,50 @@ /obj/effect/proc_holder/spell/summon_contract/cast(list/targets, mob/user = usr) for(var/target in targets) var/mob/living/carbon/C = target - if(C.mind && user.mind) - if(C.stat == DEAD) - if(user.drop_from_active_hand()) - var/obj/item/paper/contract/infernal/revive/contract = new(user.loc, C.mind, user.mind) - user.put_in_hands(contract) - else - var/obj/item/paper/contract/infernal/contract - var/contractTypeName = input(user, "What type of contract?") in list (CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_KNOWLEDGE, CONTRACT_FRIENDSHIP) //TODO: Refactor this to be less boilerplate-y - switch(contractTypeName) - if(CONTRACT_POWER) - contract = new /obj/item/paper/contract/infernal/power(C.loc, C.mind, user.mind) - if(CONTRACT_WEALTH) - contract = new /obj/item/paper/contract/infernal/wealth(C.loc, C.mind, user.mind) - if(CONTRACT_PRESTIGE) - contract = new /obj/item/paper/contract/infernal/prestige(C.loc, C.mind, user.mind) - if(CONTRACT_MAGIC) - contract = new /obj/item/paper/contract/infernal/magic(C.loc, C.mind, user.mind) - if(CONTRACT_KNOWLEDGE) - contract = new /obj/item/paper/contract/infernal/knowledge(C.loc, C.mind, user.mind) - if(CONTRACT_FRIENDSHIP) - contract = new /obj/item/paper/contract/infernal/friendship(C.loc, C.mind, user.mind) - C.put_in_hands(contract) + if(!C.mind || !user.mind) + to_chat(user, span_notice("[C] seems to not be sentient. You are unable to summon a contract for them.")) + continue + + if(C.stat == DEAD) + if(!user.drop_from_active_hand()) + continue + + var/obj/item/paper/contract/infernal/revive/contract = new(user.loc, C.mind, user.mind) + user.put_in_hands(contract) else - to_chat(user,"[C] seems to not be sentient. You are unable to summon a contract for them.") + var/obj/item/paper/contract/infernal/contract + var/contractTypeName = input(user, "What type of contract?") in list (CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_KNOWLEDGE, CONTRACT_FRIENDSHIP) // no todo: contracts are deprecated and soon will be deleted + + switch(contractTypeName) + if(CONTRACT_POWER) + contract = new /obj/item/paper/contract/infernal/power(C.loc, C.mind, user.mind) + if(CONTRACT_WEALTH) + contract = new /obj/item/paper/contract/infernal/wealth(C.loc, C.mind, user.mind) + if(CONTRACT_PRESTIGE) + contract = new /obj/item/paper/contract/infernal/prestige(C.loc, C.mind, user.mind) + if(CONTRACT_MAGIC) + contract = new /obj/item/paper/contract/infernal/magic(C.loc, C.mind, user.mind) + if(CONTRACT_KNOWLEDGE) + contract = new /obj/item/paper/contract/infernal/knowledge(C.loc, C.mind, user.mind) + if(CONTRACT_FRIENDSHIP) + contract = new /obj/item/paper/contract/infernal/friendship(C.loc, C.mind, user.mind) + + C.put_in_hands(contract) /obj/effect/proc_holder/spell/fireball/hellish name = "Hellfire" desc = "This spell launches hellfire at the target." + school = "evocation" base_cooldown = 8 SECONDS + clothes_req = FALSE human_req = FALSE + invocation = "Your very soul will catch fire!" invocation_type = "shout" + fireball_type = /obj/item/projectile/magic/fireball/infernal action_background_icon_state = "bg_demon" @@ -99,88 +119,111 @@ /obj/effect/proc_holder/spell/infernal_jaunt name = "Infernal Jaunt" desc = "Use hellfire to phase out of existence." + base_cooldown = 20 SECONDS cooldown_min = 0 + overlay = null + action_icon_state = "jaunt" action_background_icon_state = "bg_demon" + phase_allowed = TRUE + clothes_req = FALSE human_req = FALSE /obj/effect/proc_holder/spell/infernal_jaunt/create_new_targeting() return new /datum/spell_targeting/self +/obj/effect/proc_holder/spell/infernal_jaunt/can_cast(mob/living/user, charge_check, show_message) + . = ..() + if(!.) + return FALSE + + if(!istype(user)) + return FALSE /obj/effect/proc_holder/spell/infernal_jaunt/cast(list/targets, mob/living/user = usr) - if(istype(user)) - if(istype(user.loc, /obj/effect/dummy/slaughter)) - var/continuing = 0 - if(istype(get_area(user), /area/shuttle)) // Can always phase in in a shuttle. - continuing = TRUE - else - for(var/mob/living/C in orange(2, get_turf(user.loc))) //Can also phase in when nearby a potential buyer. - if (C.mind && C.mind.soulOwner == C.mind) - continuing = TRUE - break - if(continuing) - to_chat(user,"You are now phasing in.") - if(do_after(user, 15 SECONDS, user, NONE)) - user.infernalphasein(src) - else - to_chat(user,"You can only re-appear near a potential signer or on a shuttle.") - revert_cast() - return ..() + if(istype(user.loc, /obj/effect/dummy/slaughter)) + var/continuing = 0 + if(istype(get_area(user), /area/shuttle)) // Can always phase in in a shuttle. + continuing = TRUE else - user.fakefire() - to_chat(user,"You begin to phase back into sinful flames.") + for(var/mob/living/C in orange(2, get_turf(user.loc))) //Can also phase in when nearby a potential buyer. + if (C.mind && C.mind.soulOwner == C.mind) + continuing = TRUE + break + if(continuing) + to_chat(user, span_warning("You are now phasing in.")) if(do_after(user, 15 SECONDS, user, NONE)) - ADD_TRAIT(user, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) - user.infernalphaseout(src) - else - to_chat(user,"You must remain still while exiting.") - user.ExtinguishMob() - cooldown_handler.start_recharge() - return - revert_cast() + user.infernalphasein(src) + else + to_chat(user, span_warning("You can only re-appear near a potential signer or on a shuttle.")) + revert_cast() + return ..() + else + user.fakefire() + to_chat(user, span_warning("You begin to phase back into sinful flames.")) + if(do_after(user, 15 SECONDS, user, NONE)) + ADD_TRAIT(user, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src)) + user.infernalphaseout(src) + else + to_chat(user, span_warning("You must remain still while exiting.")) + user.ExtinguishMob() + + cooldown_handler.start_recharge() + return /mob/living/proc/infernalphaseout(obj/effect/proc_holder/spell/infernal_jaunt/spell) dust_animation() - visible_message("[src] disappears in a flashfire!") + + visible_message(span_warning("[src] disappears in a flashfire!")) playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1) + var/obj/effect/dummy/slaughter/s_holder = new(loc) + ExtinguishMob() forceMove(s_holder) + holder = s_holder + REMOVE_TRAIT(src, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(spell)) fakefireextinguish() /mob/living/proc/infernalphasein(obj/effect/proc_holder/spell/infernal_jaunt/spell) if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM)) - to_chat(src,"You're too busy to jaunt in.") + to_chat(src, span_warning("You're too busy to jaunt in.")) return FALSE + fakefire() forceMove(get_turf(src)) - visible_message("[src] appears in a firey blaze!") + + visible_message(span_warning("[src] appears in a firey blaze!")) playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1) - spawn(1.5 SECONDS) - fakefireextinguish(TRUE) + + addtimer(CALLBACK(src, PROC_REF(fakefireextinguish), TRUE), 1.5 SECONDS) /obj/effect/proc_holder/spell/sintouch name = "Sin Touch" desc = "Subtly encourage someone to sin." + base_cooldown = 180 SECONDS cooldown_min = 0 + clothes_req = FALSE human_req = FALSE overlay = null + action_icon_state = "sintouch" action_background_icon_state = "bg_demon" + invocation = "TASTE SIN AND INDULGE!!" invocation_type = "shout" + var/max_targets = 3 @@ -191,25 +234,29 @@ /obj/effect/proc_holder/spell/sintouch/create_new_targeting() - var/datum/spell_targeting/targeted/T = new() - T.selection_type = SPELL_SELECTION_RANGE - T.random_target = TRUE - T.target_priority = SPELL_TARGET_RANDOM - T.use_turf_of_user = TRUE - T.range = 2 - T.max_targets = 3 - return T + var/datum/spell_targeting/targeted/targeting = new() + + targeting.selection_type = SPELL_SELECTION_RANGE + targeting.random_target = TRUE + targeting.target_priority = SPELL_TARGET_RANDOM + targeting.use_turf_of_user = TRUE + + targeting.range = 2 + targeting.max_targets = 3 + + return targeting /obj/effect/proc_holder/spell/sintouch/sintouch/cast(list/targets, mob/living/user = usr) - for(var/mob/living/carbon/human/H in targets) - if(!H.mind) + for(var/mob/living/carbon/human/human in targets) + if(!human.mind) continue - for(var/datum/objective/sintouched/A in H.mind.objectives) + + if(human.mind.has_antag_datum(/datum/antagonist/sintouched)) continue - H.influenceSin() - H.Weaken(4 SECONDS) + human.mind.add_antag_datum(/datum/antagonist/sintouched) + human.Weaken(4 SECONDS) /obj/effect/proc_holder/spell/summon_dancefloor name = "Summon Dancefloor" @@ -218,15 +265,13 @@ human_req = FALSE school = "conjuration" base_cooldown = 1 SECONDS - cooldown_min = 5 SECONDS //5 seconds, so the smoke can't be spammed + cooldown_min = 5 SECONDS // 5 seconds, so the smoke can't be spammed action_icon_state = "funk" action_background_icon_state = "bg_demon" var/list/dancefloor_turfs var/list/dancefloor_turfs_types var/dancefloor_exists = FALSE -// var/datum/effect_system/smoke_spread/transparent/dancefloor_devil/smoke - /obj/effect/proc_holder/spell/summon_dancefloor/create_new_targeting() return new /datum/spell_targeting/self @@ -236,13 +281,6 @@ LAZYINITLIST(dancefloor_turfs) LAZYINITLIST(dancefloor_turfs_types) -/* - if(!smoke) - smoke = new() - smoke.set_up(0, get_turf(user)) - smoke.start() -*/ - if(dancefloor_exists) dancefloor_exists = FALSE for(var/i in 1 to dancefloor_turfs.len) @@ -252,12 +290,15 @@ var/list/funky_turfs = RANGE_TURFS(1, user) for(var/turf/T in funky_turfs) if(T.density) - to_chat(user, "You're too close to a wall.") + to_chat(user, span_warning("You're too close to a wall.")) return + dancefloor_exists = TRUE var/i = 1 + dancefloor_turfs.len = funky_turfs.len dancefloor_turfs_types.len = funky_turfs.len + for(var/t in funky_turfs) var/turf/T = t dancefloor_turfs[i] = T @@ -265,12 +306,148 @@ T.ChangeTurf((i % 2 == 0) ? /turf/simulated/floor/light/colour_cycle/dancefloor_a : /turf/simulated/floor/light/colour_cycle/dancefloor_b) i++ +/obj/effect/proc_holder/spell/aoe/devil_fire + name = "Devil fire" + desc = "Призывает огненные волны в радиусе заклинания." + action_icon_state = "explosion_old" + + base_cooldown = 60 SECONDS + aoe_range = 10 + + clothes_req = FALSE + human_req = FALSE + + var/fire_prob = 50 + var/slow_time = 5 SECONDS + +/obj/effect/proc_holder/spell/aoe/devil_fire/create_new_targeting() + var/datum/spell_targeting/aoe/targeting = new() + + targeting.range = aoe_range + targeting.allowed_type = /atom + + return targeting + +/obj/effect/proc_holder/spell/aoe/devil_fire/cast(list/targets, mob/user = usr) + for(var/mob/living/living in targets) + living.Slowed(slow_time) + + for(var/turf/turf in targets) + if(turf == get_turf(user)) + continue + + if(!prob(fire_prob)) + continue + + new /obj/effect/hotspot(turf) + turf.hotspot_expose(2000, 50, 1) + + playsound(get_turf(user), 'sound/magic/blind.ogg', 50, TRUE) + +/obj/effect/proc_holder/spell/dark_conversion + name = "Dark conversion" + desc = "Превращает гуманоида в тенечеловека и искажает его восприятие реальности." + + action_icon = 'icons/mob/actions/actions_cult.dmi' + action_icon_state = "horror" + + base_cooldown = 300 SECONDS + var/cast_time = 5 SECONDS + + clothes_req = FALSE + human_req = FALSE + +/obj/effect/proc_holder/spell/dark_conversion/create_new_targeting() + var/datum/spell_targeting/aoe/targeting = new() + + targeting.range = 5 + targeting.allowed_type = /mob/living/carbon/human + + return targeting + +/obj/effect/proc_holder/spell/dark_conversion/create_new_handler() + var/datum/spell_handler/devil/devil = new + return devil + +/obj/effect/proc_holder/spell/dark_conversion/valid_target(mob/living/carbon/human/target, mob/user) + return target.mind && !isshadowperson(target) + +/obj/effect/proc_holder/spell/dark_conversion/cast(list/targets, mob/user = usr) + var/mob/living/carbon/human/human = targets[1] + var/mob/living/carbon/carbon = user + var/datum/antagonist/devil/devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + + carbon.say("INF' [devil.info.truename] NO") + playsound(get_turf(carbon), 'sound/magic/narsie_attack.ogg', 100, TRUE) + + human.Knockdown(0.1 SECONDS) + + if(!do_after(user, cast_time, user, NONE)) + revert_cast(user) + return + + make_shadow(human) + +/obj/effect/proc_holder/spell/dark_conversion/proc/make_shadow(mob/living/carbon/human/human) + human.set_species(/datum/species/shadow) + human.store_memory("Вы - создание тьмы. Старайтесь сохранить свою истинную форму и выполнить свои задания.", TRUE) + + var/datum/objective/assassinate/kill = new + kill.owner = human.mind + kill.find_target() + + LAZYADD(human.mind.objectives, kill) + LAZYADD(human.faction, "hell") + + human.mind.prepare_announce_objectives() + playsound(human, 'sound/magic/mutate.ogg', 100, TRUE) + +/obj/effect/proc_holder/spell/sacrifice_circle + name = "Create sacrifice circle" + desc = "Создает руну для жертвоприношений." + + action_icon = 'icons/mob/actions/actions_cult.dmi' + action_icon_state = "sintouch" + + base_cooldown = 900 SECONDS + var/cast_time = 5 SECONDS + + clothes_req = FALSE + human_req = FALSE + +/obj/effect/proc_holder/spell/sacrifice_circle/create_new_targeting() + return new /datum/spell_targeting/self + +/obj/effect/proc_holder/spell/sacrifice_circle/create_new_handler() + var/datum/spell_handler/devil/devil = new + return devil + +/obj/effect/proc_holder/spell/sacrifice_circle/cast(list/targets, mob/user = usr) + playsound(get_turf(user), 'sound/magic/cult_spell.ogg', 100, TRUE) + + if(!do_after(user, cast_time, user, NONE)) + revert_cast(user) + return + + create_rune(user) + +/obj/effect/proc_holder/spell/sacrifice_circle/proc/create_rune(mob/user) + var/mob/living/carbon/carbon = user + var/datum/antagonist/devil/devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + + if(!devil) + return + + var/obj/effect/decal/cleanable/devil/devil_rune = new(get_turf(carbon)) + playsound(get_turf(carbon), 'sound/magic/invoke_general.ogg', 100, TRUE) -/* -/datum/effect_system/smoke_spread/transparent/dancefloor_devil - effect_type = /obj/effect/particle_effect/smoke/transparent/dancefloor_devil + devil_rune.AddComponent( \ + /datum/component/ritual_object, \ + allowed_categories = /datum/ritual/devil, \ + allowed_special_role = list(ROLE_DEVIL), \ + ) + devil_rune.devil = devil + devil_rune.update_appearance(UPDATE_DESC) -/obj/effect/particle_effect/smoke/transparent/dancefloor_devil - lifetime = 2 -*/ + return diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index eeb076ff564..f3da52eac80 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -374,22 +374,30 @@ active_instances += instance_duration /datum/status_effect/fleshmend/tick(seconds_between_ticks) - if(length(active_instances) >= 1) + if(LAZYLEN(active_instances) >= 1) var/heal_amount = (length(active_instances) / tolerance) * (freezing ? 2 : 10) var/blood_restore = 30 * length(active_instances) var/update = NONE + update |= owner.heal_overall_damage(heal_amount, heal_amount, updating_health = FALSE) update |= owner.heal_damage_type(heal_amount, OXY, FALSE) + if(update) owner.updatehealth("fleshmend") + if(!HAS_TRAIT(owner, TRAIT_NO_BLOOD_RESTORE)) - owner.blood_volume = min(owner.blood_volume + blood_restore, BLOOD_VOLUME_NORMAL) + owner.setBlood(min(owner.blood_volume + blood_restore, BLOOD_VOLUME_NORMAL)) + var/list/expired_instances = list() + for(var/i in 1 to length(active_instances)) active_instances[i]-- + if(active_instances[i] <= 0) expired_instances += active_instances[i] + active_instances -= expired_instances + tolerance = max(tolerance - 0.05, 1) if(tolerance <= 1 && length(active_instances) == 0) qdel(src) @@ -774,3 +782,48 @@ /datum/status_effect/drill_payback/on_remove() ..() owner.clear_fullscreen("payback") + +/datum/status_effect/drask_coma + id = "drask_coma" + tick_interval = 2 SECONDS + + var/temp_step + var/cached_sleep_time + +/datum/status_effect/drask_coma/on_creation( + mob/living/new_owner, + duration = 300 SECONDS, + temp_step = 10, + ) + src.duration = duration + src.temp_step = temp_step + + return ..() + +/datum/status_effect/drask_coma/on_apply() + to_chat(owner, span_notice("Ваш метаболизм полностью остановлен.")) + + cached_sleep_time = world.time + owner.AdjustSleeping(duration) + RegisterSignal(owner, COMSIG_MOB_STATCHANGE, PROC_REF(stat_change)) + + return TRUE + +/datum/status_effect/drask_coma/proc/stat_change(datum/source, new_stat, old_stat) + SIGNAL_HANDLER + + if(new_stat == CONSCIOUS || new_stat == DEAD) + qdel(src) + +/datum/status_effect/drask_coma/tick(seconds_between_ticks) + owner.adjust_bodytemperature(-temp_step) + +/datum/status_effect/drask_coma/on_remove() + to_chat(owner, span_notice("Вы чувствуете прилив сил и наконец просыпаетесь.")) + + var/elapsed_time = world.time - cached_sleep_time + + if(elapsed_time < duration) + owner.AdjustSleeping(-(duration - elapsed_time)) + + UnregisterSignal(owner, COMSIG_MOB_STATCHANGE) diff --git a/code/datums/status_effects/screwy_hud.dm b/code/datums/status_effects/screwy_hud.dm new file mode 100644 index 00000000000..09b736d7c22 --- /dev/null +++ b/code/datums/status_effects/screwy_hud.dm @@ -0,0 +1,101 @@ +/** + * Screwy hud status. + * + * Applied to carbons, it will make their health bar look like it's incorrect - + * in crit (SCREWYHUD_CRIT), dead (SCREWYHUD_DEAD), or fully healthy (SCREWYHUD_HEALTHY) + * + * Grouped status effect, so multiple sources can add a screwyhud without + * accidentally removing another source's hud. + */ +/datum/status_effect/grouped/screwy_hud + alert_type = null + /// The priority of this screwyhud over other screwyhuds. + var/priority = -1 + /// The icon we override our owner's healths.icon_state with + var/override_icon + /// The icon prefix we override our owner's healthdoll + var/override_prefix + /// Сhange only species overlays + var/only_species = FALSE + +/datum/status_effect/grouped/screwy_hud/on_apply() + if(!iscarbon(owner)) + return FALSE + + RegisterSignal(owner, COMSIG_CARBON_UPDATING_HEALTH_HUD, PROC_REF(on_health_hud_updated)) + if(ishuman(owner)) + RegisterSignal(owner, COMSIG_HUMAN_UPDATING_HEALTH_HUD, PROC_REF(on_human_health_hud_updated)) + owner.update_health_hud() + return TRUE + +/datum/status_effect/grouped/screwy_hud/on_remove() + UnregisterSignal(owner, list(COMSIG_CARBON_UPDATING_HEALTH_HUD, COMSIG_HUMAN_UPDATING_HEALTH_HUD)) + owner.update_health_hud() + +/datum/status_effect/grouped/screwy_hud/proc/on_health_hud_updated(mob/living/carbon/source, shown_health_amount) + SIGNAL_HANDLER + + // Shouldn't even be running if we're dead, but just in case... + if(source.stat == DEAD) + return + + // It's entirely possible we have multiple screwy huds on one mob. + // Defer to priority to determine which to show. If our's is lower, don't show it. + for(var/datum/status_effect/grouped/screwy_hud/other_screwy_hud in source.status_effects) + if(other_screwy_hud.priority > priority) + return + + source.healths.icon_state = override_icon + return COMPONENT_OVERRIDE_HEALTH_HUD + +/datum/status_effect/grouped/screwy_hud/proc/on_human_health_hud_updated(mob/living/carbon/human/source, shown_health_amount) + SIGNAL_HANDLER + + // Shouldn't even be running if we're dead, but just in case... + if(source.stat == DEAD) + return + + // It's entirely possible we have multiple screwy huds on one mob. + // Defer to priority to determine which to show. If our's is lower, don't show it. + for(var/datum/status_effect/grouped/screwy_hud/other_screwy_hud in source.status_effects) + if(other_screwy_hud.priority > priority) + return + + source.healths.icon_state = override_icon + if(source.healthdoll) + var/list/new_overlays = list() + + var/list/cached_overlays = source.healthdoll.cached_healthdoll_overlays + // Use the dead health doll as the base, since we have proper "healthy" overlays now + for(var/obj/item/organ/external/bodypart as anything in source.bodyparts) + var/icon_num = override_prefix + if(istype(bodypart, /obj/item/organ/external/tail) && bodypart.dna?.species.tail) + new_overlays |= "[bodypart.dna.species.tail][icon_num]" + if(istype(bodypart, /obj/item/organ/external/wing) && bodypart.dna?.species.tail) + new_overlays |= "[bodypart.dna.species.wing][icon_num]" + else if(!only_species) + new_overlays |= "[bodypart.limb_zone][icon_num]" + + source.healthdoll.add_overlay(new_overlays - cached_overlays) + source.healthdoll.cut_overlay(cached_overlays - new_overlays) + source.healthdoll.cached_healthdoll_overlays = new_overlays + return COMPONENT_OVERRIDE_HEALTH_HUD + +/datum/status_effect/grouped/screwy_hud/fake_dead + id = "fake_hud_dead" + priority = 100 // death is absolute + override_icon = "health7" + override_prefix = "_DEAD" + only_species = TRUE + +/datum/status_effect/grouped/screwy_hud/fake_crit + id = "fake_hud_crit" + priority = 90 // crit is almost death, and death is absolute + override_icon = "health6" + override_prefix = 5 + +/datum/status_effect/grouped/screwy_hud/fake_healthy + id = "fake_hud_healthy" + priority = 10 // fully healthy is the opposite of death, which is absolute + override_icon = "health0" + override_prefix = 0 diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index eac5289d91b..238adcde39a 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -248,6 +248,7 @@ // Create the status effect with our mob + our arguments var/datum/status_effect/new_instance = new new_effect(arguments) if(!QDELETED(new_instance)) + SEND_SIGNAL(src, COMSIG_LIVING_GAINED_STATUS_EFFECT, new_instance) return new_instance @@ -265,6 +266,7 @@ . = FALSE for(var/datum/status_effect/existing_effect as anything in status_effects) if(existing_effect.id == initial(removed_effect.id) && existing_effect.before_remove(arglist(arguments))) + SEND_SIGNAL(src, COMSIG_LIVING_EARLY_LOST_STATUS_EFFECT, existing_effect) qdel(existing_effect) . = TRUE diff --git a/code/datums/status_effects/wet_stacks.dm b/code/datums/status_effects/wet_stacks.dm new file mode 100644 index 00000000000..a1eec928d79 --- /dev/null +++ b/code/datums/status_effects/wet_stacks.dm @@ -0,0 +1,63 @@ +/datum/status_effect/stacking/wet + id = "wet_stacks" + on_remove_on_mob_delete = TRUE + tick_interval = 2 SECONDS + stack_decay = 0.1 + /// Holder of wet effect particles + var/obj/effect/abstract/particle_holder/wet_effect + +/datum/status_effect/stacking/wet/Destroy() + if(wet_effect) + QDEL_NULL(wet_effect) + . = ..() + +/datum/status_effect/stacking/wet/proc/update_wet() + if(stacks > 0) + if(wet_effect) + return + wet_effect = new(owner, /particles/droplets) + else + qdel(wet_effect) + wet_effect = null + +/datum/status_effect/stacking/wet/proc/combine_wet_and_fire() + var/buf_stacks = stacks + stacks = clamp(buf_stacks - owner.fire_stacks, 0, 20) + owner.fire_stacks = clamp(owner.fire_stacks - buf_stacks, 0, 20) + +/datum/status_effect/stacking/wet/proc/WetMob() + if(!HAS_TRAIT(owner, TRAIT_WET_IMMUNITY) && stacks > 0) + owner.AddComponent(/datum/component/slippery, 5 SECONDS) + update_wet() + SEND_SIGNAL(owner, COMSIG_LIVING_WET) + return TRUE + return FALSE + + +/datum/status_effect/stacking/wet/add_stacks(stacks_added) //Adjusting the amount of fire_stacks we have on person + if(HAS_TRAIT(owner, TRAIT_WET_IMMUNITY)) + return + SEND_SIGNAL(owner, COMSIG_MOB_ADJUST_WET) + stacks = clamp(stacks + stacks_added, -20, 20) + if(owner.fire_stacks) + combine_wet_and_fire() + if(stacks <= 0) + DryMob() + else + WetMob() + + +/datum/status_effect/stacking/wet/proc/DryMob() + if(stacks > 0) + qdel(owner.GetComponent(/datum/component/slippery)) + stacks = 0 + update_wet() + +/datum/status_effect/stacking/wet/stack_decay_effect() + . = ..() + if(stacks <= 0) + DryMob() + qdel(src) + return FALSE + SEND_SIGNAL(owner, COMSIG_LIVING_WET_TICK) + return TRUE diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 676c181ae90..d7c778a7974 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -497,26 +497,20 @@ GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY /datum/supply_packs/security/armory/riothelmets name = "Riot Bundle Crate" - contains = list(/obj/item/clothing/head/helmet/riot, - /obj/item/clothing/head/helmet/riot, - /obj/item/clothing/head/helmet/riot, + contains = list(/obj/item/storage/backpack/duffel/security/riot_armory, + /obj/item/storage/backpack/duffel/security/riot_armory, + /obj/item/storage/backpack/duffel/security/riot_armory, /obj/item/shield/riot, /obj/item/shield/riot, - /obj/item/shield/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/clothing/suit/armor/riot, - /obj/item/clothing/suit/armor/riot) + /obj/item/shield/riot) cost = 80 containername = "riot bundle crate" /datum/supply_packs/security/armory/bulletarmor name = "Bulletproof Armor Crate" - contains = list(/obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/suit/armor/bulletproof, - /obj/item/clothing/head/helmet/alt, - /obj/item/clothing/head/helmet/alt, - /obj/item/clothing/head/helmet/alt) + contains = list(/obj/item/storage/backpack/duffel/security/bulletproof_armory, + /obj/item/storage/backpack/duffel/security/bulletproof_armory, + /obj/item/storage/backpack/duffel/security/bulletproof_armory) cost = 40 containername = "tactical armor crate" diff --git a/code/datums/syndiesupplypacks.dm b/code/datums/syndiesupplypacks.dm index d5bbbba1acc..4ca6971d525 100644 --- a/code/datums/syndiesupplypacks.dm +++ b/code/datums/syndiesupplypacks.dm @@ -2238,11 +2238,11 @@ GLOBAL_LIST_INIT(all_syndie_supply_groups, list(SYNDIE_SUPPLY_EMERGENCY,SYNDIE_S /obj/item/camera_bug, /obj/item/door_remote/omni/access_tuner, /obj/item/implanter/freedom/prototype, - /obj/item/aiModule/syndicate, + /obj/item/ai_module/syndicate, /obj/item/card/emag, /obj/item/encryptionkey/syndicate, /obj/item/encryptionkey/binary, - /obj/item/aiModule/toyAI, + /obj/item/ai_module/toy_ai, /obj/item/storage/belt/military/traitor/hacker, /obj/item/clothing/gloves/combat, /obj/item/flashlight/emp diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index 4eeac88c353..63b5de6148b 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -250,6 +250,13 @@ cost = 5 job = list(JOB_TITLE_CLOWN) +/datum/uplink_item/jobspecific/bipki + name = "Bipki case" + desc = "Suck me and I'll tell you what's inside." + item = /obj/item/case_with_bipki + cost = 30 + job = list(JOB_TITLE_CLOWN) + //Mime /datum/uplink_item/jobspecific/caneshotgun name = "Cane Shotgun and Assassination Shells" @@ -469,14 +476,6 @@ cost = 15 job = list(JOB_TITLE_BARTENDER) -//Barber -/datum/uplink_item/jobspecific/safety_scissors //Hue - name = "Safety Scissors" - desc = "A pair of scissors that are anything but what their name implies; can easily cut right into someone's throat." - item = /obj/item/scissors/safety - cost = 6 - job = list(JOB_TITLE_BARBER) - //Botanist /datum/uplink_item/jobspecific/bee_briefcase name = "Briefcase Full of Bees" @@ -1822,7 +1821,7 @@ /datum/uplink_item/device_tools/hacked_module name = "Hacked AI Upload Module" desc = "When used with an upload console, this module allows you to upload priority laws to an artificial intelligence. Be careful with their wording, as artificial intelligences may look for loopholes to exploit." - item = /obj/item/aiModule/syndicate + item = /obj/item/ai_module/syndicate cost = 38 /datum/uplink_item/device_tools/magboots @@ -2331,9 +2330,9 @@ /datum/uplink_item/contractor/zippo name = "Contractor Zippo Lighter" - desc = "A kit with your personal assistant. It comes with an increased amount of memory and special programs." - item = /obj/item/storage/box/contractor/spai_kit - cost = 120 + desc = "An unique black and gold zippo lighter with no purpose other than showing off." + item = /obj/item/lighter/zippo/contractor + cost = 1 /datum/uplink_item/contractor/loadout_box name = "Contractor standard loadout box" diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index cddd6ae0bd8..475ce42a3e8 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -102,16 +102,32 @@ return . if(ishuman(mob_to_check)) var/mob/living/carbon/human/human_mob = mob_to_check - if(human_mob.get_thermal_protection() >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) + if(human_mob.get_main_thermal_protection() >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) return FALSE else if(istype(mob_to_check, /mob/living/simple_animal/borer)) var/mob/living/simple_animal/borer/borer = mob_to_check - if(borer.host?.get_thermal_protection() >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) + if(borer.host?.get_main_thermal_protection() >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) return FALSE /datum/weather/ash_storm/weather_act(mob/living/target) - target.adjustFireLoss(4) + if(!ishuman(target)) + target.adjustFireLoss(4) + return + + target.apply_damage((1 - target.getarmor(BODY_ZONE_HEAD, FIRE) / 100) * THERMAL_PROTECTION_HEAD * 4, BURN, BODY_ZONE_HEAD) + target.apply_damage((1 - target.getarmor(BODY_ZONE_CHEST, FIRE) / 100) * THERMAL_PROTECTION_UPPER_TORSO * 4, BURN, BODY_ZONE_CHEST) + target.apply_damage((1 - target.getarmor(BODY_ZONE_PRECISE_GROIN, FIRE) / 100) * THERMAL_PROTECTION_LOWER_TORSO * 4, BURN, BODY_ZONE_PRECISE_GROIN) + + target.apply_damage((1 - target.getarmor(BODY_ZONE_L_ARM, FIRE) / 100) * THERMAL_PROTECTION_ARM_LEFT * 4, BURN, BODY_ZONE_L_ARM) + target.apply_damage((1 - target.getarmor(BODY_ZONE_PRECISE_L_HAND, FIRE) / 100) * THERMAL_PROTECTION_HAND_LEFT * 4, BURN, BODY_ZONE_PRECISE_L_HAND) + target.apply_damage((1 - target.getarmor(BODY_ZONE_R_ARM, FIRE) / 100) * THERMAL_PROTECTION_ARM_RIGHT * 4, BURN, BODY_ZONE_R_ARM) + target.apply_damage((1 - target.getarmor(BODY_ZONE_PRECISE_R_HAND, FIRE) / 100) * THERMAL_PROTECTION_HAND_RIGHT * 4, BURN, BODY_ZONE_PRECISE_R_HAND) + + target.apply_damage((1 - target.getarmor(BODY_ZONE_L_LEG, FIRE) / 100) * THERMAL_PROTECTION_LEG_LEFT * 4, BURN, BODY_ZONE_L_LEG) + target.apply_damage((1 - target.getarmor(BODY_ZONE_PRECISE_L_FOOT, FIRE) / 100) * THERMAL_PROTECTION_FOOT_LEFT * 4, BURN, BODY_ZONE_PRECISE_L_FOOT) + target.apply_damage((1 - target.getarmor(BODY_ZONE_R_LEG, FIRE) / 100) * THERMAL_PROTECTION_LEG_RIGHT * 4, BURN, BODY_ZONE_R_LEG) + target.apply_damage((1 - target.getarmor(BODY_ZONE_PRECISE_R_FOOT, FIRE) / 100) * THERMAL_PROTECTION_FOOT_RIGHT * 4, BURN, BODY_ZONE_PRECISE_R_FOOT) //Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm. diff --git a/code/datums/weather/weather_types/blob_storm.dm b/code/datums/weather/weather_types/blob_storm.dm index 31321e210f3..a4d8d06f640 100644 --- a/code/datums/weather/weather_types/blob_storm.dm +++ b/code/datums/weather/weather_types/blob_storm.dm @@ -7,7 +7,7 @@ telegraph_message = "Вы замечаете мелкие частицы в воздухе" weather_message = "Вы ощущаете поток неизвестных мелких частиц, которые проникают сквозь любую одежду. Спасти вас может только чудо." - weather_overlay = "ash_storm" + weather_overlay = "blob_storm" weather_duration_lower = 30 SECONDS weather_duration_upper = 1 MINUTES weather_color = COLOR_PALE_GREEN_GRAY @@ -26,6 +26,20 @@ /datum/weather/blob_storm/telegraph() + var/list/blobs = SSticker?.mode?.blobs["infected"] + SSticker?.mode?.blobs["offsprings"] + var/color + var/mass = 0 + for(var/datum/mind/blob in blobs) + var/mob/camera/blob/overmind = blob.current + if(QDELETED(overmind) || !istype(overmind) || overmind.stat == DEAD) + continue + if(overmind.blobs_legit.len > mass) + mass = overmind.blobs_legit.len + color = overmind.blobstrain.color + + if(color) + weather_color = color + ..() status_alarm(TRUE) GLOB.event_announcement.Announce("Биологической угроза пятого уровня достигла критической массы на борту [station_name()]. Выброс спор и массовое заражение неизбежно.", diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 9469b60ba46..306481baf3d 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -95,7 +95,7 @@ ///This datum, if set, allows terrain generation behavior to be ran on Initialize() // This is unfinished, used in Lavaland var/datum/map_generator/cave_generator/map_generator - var/area_flags = NONE + var/area_flags = BLOBS_ALLOWED /area/New(loc, ...) // This interacts with the map loader, so it needs to be set immediately diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm index 5d22363b9cb..40ee49953ac 100644 --- a/code/game/area/areas/depot-areas.dm +++ b/code/game/area/areas/depot-areas.dm @@ -3,6 +3,7 @@ name = "Suspicious Supply Depot" icon_state = "dark" tele_proof = 1 + area_flags = NONE /area/syndicate_depot/core icon_state = "red" diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm index c62e8d3d749..77a6ff31241 100644 --- a/code/game/area/areas/mining.dm +++ b/code/game/area/areas/mining.dm @@ -4,6 +4,7 @@ icon_state = "mining" has_gravity = STANDARD_GRAVITY sound_environment = SOUND_AREA_STANDARD_STATION + area_flags = NONE /area/mine/explored name = "Mine" @@ -162,7 +163,7 @@ /area/lavaland/surface/outdoors name = "Lavaland Wastes" outdoors = TRUE - area_flags = FLORA_ALLOWED + area_flags = FLORA_ALLOWED | BLOBS_ALLOWED /area/lavaland/surface/outdoors/unexplored // ruins spawn here icon_state = "unexplored" diff --git a/code/game/area/ss13_areas.dm b/code/game/area/ss13_areas.dm index 010db83df7d..5cef7ab1305 100644 --- a/code/game/area/ss13_areas.dm +++ b/code/game/area/ss13_areas.dm @@ -31,6 +31,7 @@ This applies to all STANDARD station areas base_lighting_alpha = 255 hide_attacklogs = TRUE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/adminconstruction @@ -42,6 +43,7 @@ This applies to all STANDARD station areas base_lighting_alpha = 255 hide_attacklogs = TRUE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/space icon_state = "space" @@ -102,6 +104,7 @@ This applies to all STANDARD station areas /area/shuttle/auxillary_base icon_state = "shuttle" + area_flags = NONE /area/shuttle/escape name = "Emergency Shuttle" @@ -195,11 +198,13 @@ This applies to all STANDARD station areas icon_state = "shuttle" name = "Alien Shuttle Base" requires_power = 1 + area_flags = NONE /area/shuttle/alien/mine icon_state = "shuttle" name = "Alien Shuttle Mine" requires_power = 1 + area_flags = NONE /area/shuttle/gamma icon_state = "shuttle" @@ -221,6 +226,8 @@ This applies to all STANDARD station areas /area/shuttle/specops name = "Special Ops Shuttle" icon_state = "shuttlered" + parallax_movedir = EAST + area_flags = NONE /area/shuttle/specops/centcom name = "Special Ops Shuttle" @@ -234,6 +241,8 @@ This applies to all STANDARD station areas name = "Syndicate Elite Shuttle" icon_state = "shuttlered" nad_allowed = TRUE + parallax_movedir = SOUTH + area_flags = NONE /area/shuttle/syndicate_elite/mothership name = "Syndicate Elite Shuttle" @@ -247,6 +256,8 @@ This applies to all STANDARD station areas name = "Syndicate SIT Shuttle" icon_state = "shuttlered" nad_allowed = TRUE + parallax_movedir = SOUTH + area_flags = NONE /area/shuttle/assault_pod name = "Steel Rain" @@ -259,6 +270,8 @@ This applies to all STANDARD station areas /area/shuttle/administration name = "Nanotrasen Vessel" icon_state = "shuttlered" + parallax_movedir = WEST + area_flags = NONE /area/shuttle/administration/centcom name = "Nanotrasen Vessel Centcom" @@ -270,6 +283,7 @@ This applies to all STANDARD station areas /area/shuttle/thunderdome name = "honk" + area_flags = NONE /area/shuttle/thunderdome/grnshuttle name = "Thunderdome GRN Shuttle" @@ -309,6 +323,7 @@ This applies to all STANDARD station areas /area/shuttle/vox name = "Vox Skipjack" icon_state = "shuttle" + area_flags = NONE /area/shuttle/vox/station name = "Vox Skipjack" @@ -317,6 +332,7 @@ This applies to all STANDARD station areas /area/shuttle/salvage name = "Salvage Ship" icon_state = "yellow" + area_flags = NONE /area/shuttle/salvage/start name = "Middle of Nowhere" @@ -373,27 +389,33 @@ This applies to all STANDARD station areas /area/shuttle/supply name = "Supply Shuttle" icon_state = "shuttle3" + area_flags = NONE /area/shuttle/ussp name = "USSP Shuttle" icon_state = "shuttle3" + area_flags = NONE /area/shuttle/spacebar name = "Space Bar Shuttle" icon_state = "shuttle3" + area_flags = NONE /area/shuttle/abandoned name = "Abandoned Ship" icon_state = "shuttle" + area_flags = NONE /area/shuttle/syndicate name = "Syndicate Nuclear Team Shuttle" icon_state = "shuttle" nad_allowed = TRUE + area_flags = NONE /area/shuttle/trade name = "Trade Shuttle" icon_state = "shuttle" + area_flags = NONE /area/shuttle/trade/sol name = "Sol Freighter" @@ -406,6 +428,7 @@ This applies to all STANDARD station areas /area/shuttle/pirate_corvette name = "Pirate Corvette" icon_state = "shuttle" + area_flags = NONE /area/shuttle/transit name = "Hyperspace" @@ -440,6 +463,7 @@ This applies to all STANDARD station areas base_lighting_alpha = 255 nad_allowed = TRUE has_gravity = STANDARD_GRAVITY + area_flags = NONE // New CC /area/centcom/bridge @@ -500,6 +524,52 @@ This applies to all STANDARD station areas /area/centcom/shuttle name = "Centcom Administration Shuttle" +/area/centcom/supplypod/supplypod_temp_holding + name = "Supplypod Shipping Lane" + icon_state = "supplypod_flight" + area_flags = UNIQUE_AREA + +/area/centcom/supplypod + name = "Supplypod Facility" + icon_state = "supplypod" + +/area/centcom/supplypod/pod_storage + name = "Supplypod Storage" + icon_state = "supplypod_holding" + +/area/centcom/supplypod/loading + name = "Supplypod Loading Facility" + icon_state = "supplypod_loading" + var/loading_id = "" + +/area/centcom/supplypod/loading/Initialize(mapload) + . = ..() + if(!loading_id) + CRASH("[type] created without a loading_id") + if(GLOB.supplypod_loading_bays[loading_id]) + CRASH("Duplicate loading bay area: [type] ([loading_id])") + GLOB.supplypod_loading_bays[loading_id] = src + +/area/centcom/supplypod/loading/one + name = "Bay #1" + loading_id = "1" + +/area/centcom/supplypod/loading/two + name = "Bay #2" + loading_id = "2" + +/area/centcom/supplypod/loading/three + name = "Bay #3" + loading_id = "3" + +/area/centcom/supplypod/loading/four + name = "Bay #4" + loading_id = "4" + +/area/centcom/supplypod/loading/ert + name = "ERT Bay" + loading_id = "5" + //SYNDICATES /area/syndicate_mothership @@ -512,6 +582,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE nad_allowed = TRUE ambientsounds = HIGHSEC_SOUNDS + area_flags = NONE /area/syndicate_mothership/outside name = "Syndicate Controlled Territory" @@ -551,6 +622,7 @@ This applies to all STANDARD station areas base_lighting_alpha = 255 base_lighting_color = COLOR_WHITE ambientsounds = HIGHSEC_SOUNDS + area_flags = NONE // Chrono @@ -563,6 +635,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE base_lighting_alpha = 255 nad_allowed = TRUE + area_flags = NONE //EXTRA @@ -575,6 +648,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE base_lighting_alpha = 255 nad_allowed = TRUE + area_flags = NONE /area/asteroid // -- TLE name = "Asteroid" @@ -604,6 +678,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE base_lighting_alpha = 255 hide_attacklogs = TRUE + area_flags = NONE /area/tdome/arena_source @@ -643,6 +718,7 @@ This applies to all STANDARD station areas icon_state = "green" area_flags = UNIQUE_AREA has_gravity = STANDARD_GRAVITY + area_flags = NONE //Abductors /area/abductor_ship @@ -650,6 +726,7 @@ This applies to all STANDARD station areas icon_state = "yellow" requires_power = FALSE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/wizard_station name = "Wizard's Den" @@ -671,6 +748,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE sound_environment = SOUND_AREA_MEDIUM_SOFTFLOOR nad_allowed = TRUE + area_flags = NONE /area/ninja/outpost name = "SpiderClan Dojo" @@ -698,6 +776,7 @@ This applies to all STANDARD station areas base_lighting_color = COLOR_WHITE base_lighting_alpha = 255 no_teleportlocs = TRUE + area_flags = NONE /area/trader_station name = "Trade Base" @@ -707,6 +786,7 @@ This applies to all STANDARD station areas static_lighting = FALSE base_lighting_alpha = 255 base_lighting_color = COLOR_WHITE + area_flags = NONE /area/trader_station/sol name = "Jupiter Station 6" @@ -719,6 +799,7 @@ This applies to all STANDARD station areas static_lighting = FALSE base_lighting_alpha = 255 base_lighting_color = COLOR_WHITE + area_flags = NONE /area/ussp_centcom/secretariat name = "Soviet secretariat" @@ -2659,6 +2740,7 @@ This applies to all STANDARD station areas icon_state = "syndie_hall" report_alerts = FALSE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/traitor/rnd name = "Syndicate Research and Development" @@ -2832,7 +2914,7 @@ This applies to all STANDARD station areas has_gravity = STANDARD_GRAVITY ambientsounds = AWAY_MISSION_SOUNDS sound_environment = SOUND_ENVIRONMENT_ROOM - + area_flags = NONE /area/awaymission/example name = "Strange Station" icon_state = "away" @@ -2860,6 +2942,7 @@ This applies to all STANDARD station areas name = "moonoutpost" has_gravity = STANDARD_GRAVITY report_alerts = FALSE + area_flags = NONE /area/moonoutpost19/mo19arrivals name = "MO19 Arrivals" @@ -2879,6 +2962,7 @@ This applies to all STANDARD station areas power_light = FALSE poweralm = FALSE outdoors = TRUE + area_flags = NONE /area/moonoutpost19/syndicateoutpost name = "Syndicate Outpost" @@ -2944,6 +3028,7 @@ This applies to all STANDARD station areas name = "space" report_alerts = FALSE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/awaycontent/a1 icon_state = "awaycontent1" @@ -3063,6 +3148,7 @@ GLOBAL_LIST_INIT(centcom_areas, list( static_lighting = TRUE report_alerts = FALSE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/special_event/alpha name = "Special event area Alpha" diff --git a/code/game/area/vision_reset_areas.dm b/code/game/area/vision_reset_areas.dm index cd8b5cbf552..c3ac0a13211 100644 --- a/code/game/area/vision_reset_areas.dm +++ b/code/game/area/vision_reset_areas.dm @@ -5,6 +5,7 @@ */ /area/vision_change_area + area_flags = NONE /area/vision_change_area/Entered(atom/movable/arrived, area/old_area) . = ..() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index dd61d7c4241..71eff1cfe86 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,11 +1,3 @@ -// Падежи русского языка -#define NOMINATIVE 1 // Именительный: кто это? Клоун и ассистуха -#define GENITIVE 2 // Родительный: откусить кусок от кого? От клоуна и ассистухи -#define DATIVE 3 // Дательный: дать полный доступ кому? Клоуну и ассистухе -#define ACCUSATIVE 4 // Винительный: обвинить кого? Клоуна и ассистуху -#define INSTRUMENTAL 5 // Творительный: возить по полу кем? Клоуном и ассистухой -#define PREPOSITIONAL 6 // Предложный: прохладная история о ком? О клоуне и об ассистухе - /atom layer = TURF_LAYER plane = GAME_PLANE @@ -13,6 +5,7 @@ var/level = 2 var/flags = NONE var/flags_2 = NONE + var/flags_ricochet = NONE var/list/fingerprints var/list/fingerprints_time var/list/fingerprintshidden @@ -108,6 +101,7 @@ var/base_pixel_y = 0 var/tts_seed = "Arthas" + var/tts_atom_say_effect = SOUND_EFFECT_RADIO /atom/New(loc, ...) SHOULD_CALL_PARENT(TRUE) @@ -338,6 +332,12 @@ /atom/proc/is_open_container() return is_refillable() && is_drainable() +/atom/proc/setOpened() + return + +/atom/proc/setClosed() + return + /// Is this atom injectable into other atoms /atom/proc/is_injectable(mob/user, allowmobs = TRUE) return reagents && (container_type & (INJECTABLE|REFILLABLE)) @@ -637,8 +637,19 @@ /atom/proc/ex_act() return -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) +/** + * React to a hit by a blob objecd + * + * default behaviour is to send the [COMSIG_ATOM_BLOB_ACT] signal + */ +/atom/proc/blob_act(obj/structure/blob/attacking_blob) + var/blob_act_result = SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, attacking_blob) + if(blob_act_result & COMPONENT_CANCEL_BLOB_ACT) + return FALSE + return TRUE + +/atom/proc/blob_vore_act(obj/structure/blob/special/core/voring_core) + return TRUE /atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) @@ -1199,8 +1210,6 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/proc/ratvar_act() return -/atom/proc/handle_ricochet(obj/item/projectile/P) - return //This proc is called on the location of an atom when the atom is Destroy()'d /atom/proc/handle_atom_del(atom/A) @@ -1224,9 +1233,8 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) if(M.client.prefs.toggles2 & PREFTOGGLE_2_RUNECHAT) M.create_chat_message(src, message, list("italics")) - var/effect = SOUND_EFFECT_RADIO var/traits = TTS_TRAIT_RATE_MEDIUM - INVOKE_ASYNC(GLOBAL_PROC, /proc/tts_cast, src, M, message_tts, tts_seed, TRUE, effect, traits) + INVOKE_ASYNC(GLOBAL_PROC, /proc/tts_cast, src, M, message_tts, tts_seed, TRUE, tts_atom_say_effect, traits) if(length(speech_bubble_hearers)) var/image/I = image('icons/mob/talk.dmi', src, "[bubble_icon][say_test(message)]", FLY_LAYER) @@ -1343,6 +1351,7 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) if(curturf) .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" + .["Edit reagents"] = "?_src_=vars;editreagents=[UID()]" .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" @@ -1550,6 +1559,18 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons) /atom/proc/get_visible_gender() // Used only in /mob/living/carbon/human and /mob/living/simple_animal/hostile/morph return gender +/atom/proc/handle_ricochet(obj/item/projectile/ricocheting_projectile) + var/turf/p_turf = get_turf(ricocheting_projectile) + var/face_direction = get_dir(src, p_turf) || get_dir(src, ricocheting_projectile) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (ricocheting_projectile.Angle + 180)) + var/a_incidence_s = abs(incidence_s) + if(a_incidence_s > 90 && a_incidence_s < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + ricocheting_projectile.set_angle(new_angle_s) + visible_message(span_warning("[ricocheting_projectile] reflects off [src]!")) + return TRUE /// Whether the mover object can avoid being blocked by this atom, while arriving from (or leaving through) the border_dir. /atom/proc/CanPass(atom/movable/mover, border_dir) diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index 3e17d60509f..9ebc5442cca 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -386,3 +386,54 @@ /datum/dna/gene/disability/paraplegia/New() ..() block = GLOB.paraplegiablock + +/datum/dna/gene/disability/aphasia + name = "Aphasia" + desc = "Субъект теряет возможность говорить на своём основном языке." + activation_message = list("Вам становится труднее выражать свои мысли. Meh nahbleh blahmeh?") + deactivation_message = list("Ваша речь возвращается в норму.") + instability = -GENE_INSTABILITY_MINOR + /// You will be able to hear these languages, but not to speak. + var/list/blacklisted_languages_types = list( + /datum/language/common + ) + +/datum/dna/gene/disability/aphasia/New() + . = ..() + block = GLOB.aphasiablock + +/datum/dna/gene/disability/aphasia/can_activate(mob/living/carbon/human/H, flags) + if(isplasmaman(H) || iswryn(H)) + to_chat(H, span_warning("Вы чувствуете, что что-то не так, но не можете понять, что именно.")) + return FALSE + + return ..() + +/datum/dna/gene/disability/aphasia/activate(mob/living/carbon/human/human, flags) + . = ..() + RegisterSignal(human, COMSIG_LIVING_EARLY_SAY, PROC_REF(check_speaking)) + +/datum/dna/gene/disability/aphasia/deactivate(mob/living/carbon/human/human, flags) + . = ..() + UnregisterSignal(human, COMSIG_LIVING_EARLY_SAY) + +/datum/dna/gene/disability/aphasia/proc/check_speaking( + mob/living/carbon/human/source, + message, + verb, + ignore_speech_problems, + ignore_atmospherics, + ignore_languages, + datum/multilingual_say_piece/lang_piece, +) + SIGNAL_HANDLER + + if(!lang_piece?.speaking) + return + + if(!is_type_in_list(lang_piece.speaking, blacklisted_languages_types)) + return + + to_chat(source, span_notice("Вы пытаетесь что-то сказать, но не можете произнести ни слова на этом языке.")) + + return COMPONENT_PREVENT_SPEAKING diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index a38be8e0513..3f59e965099 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -174,9 +174,3 @@ /datum/dna/gene/basic/fake/fake3/New() ..() block = GLOB.fakeblock3 - - -/datum/dna/gene/basic/fake/fake4/New() - ..() - block = GLOB.fakeblock4 - diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm index 313db2a819a..cacedda4e04 100644 --- a/code/game/dna/genes/goon_powers.dm +++ b/code/game/dna/genes/goon_powers.dm @@ -31,59 +31,59 @@ // Stealth Enhancers ///////////////////////// -/datum/dna/gene/basic/stealth - instability = GENE_INSTABILITY_MODERATE - - -/datum/dna/gene/basic/stealth/deactivate(mob/living/mutant, flags) - . = ..() - mutant.alpha = initial(mutant.alpha) - - // WAS: /datum/bioEffect/darkcloak -/datum/dna/gene/basic/stealth/darkcloak +/datum/dna/gene/basic/darkcloak name = "Cloak of Darkness" desc = "Позволяет субъекту излучать вокруг себя слабое свечение, создавая эффект маскировки." activation_messages = list("Вы начинаете исчезать в тени.") deactivation_messages = list("Вы становитесь полностью видимым.") activation_prob = 25 + instability = GENE_INSTABILITY_MODERATE -/datum/dna/gene/basic/stealth/darkcloak/New() +/datum/dna/gene/basic/darkcloak/New() ..() block = GLOB.shadowblock -/datum/dna/gene/basic/stealth/darkcloak/OnMobLife(mob/living/mutant) +/datum/dna/gene/basic/darkcloak/OnMobLife(mob/living/mutant) var/turf/simulated/T = get_turf(mutant) if(!istype(T)) return var/light_available = T.get_lumcount() * 10 if(light_available <= 2) - mutant.alpha = round(mutant.alpha * 0.8) + mutant.alpha_multiply(0.8, ALPHA_SOURCE_SHADOW_CLOAK) else - mutant.alpha = initial(mutant.alpha) + mutant.alpha_set(1, ALPHA_SOURCE_SHADOW_CLOAK) +/datum/dna/gene/basic/darkcloak/deactivate(mob/living/mutant, flags) + . = ..() + mutant.alpha_set(1, ALPHA_SOURCE_SHADOW_CLOAK) //WAS: /datum/bioEffect/chameleon -/datum/dna/gene/basic/stealth/chameleon +/datum/dna/gene/basic/chameleon name = "Chameleon" desc = "Субъект обретает способность тонко изменять структуру света, чтобы оставаться невидимым до тех пор, пока он остается неподвижным." activation_messages = list("Вы чувствуете себя единым целым с окружающим миром.") deactivation_messages = list("Вы чувствуете себя необычайно заметным.") activation_prob = 25 + instability = GENE_INSTABILITY_MODERATE -/datum/dna/gene/basic/stealth/chameleon/New() +/datum/dna/gene/basic/chameleon/New() ..() block = GLOB.chameleonblock -/datum/dna/gene/basic/stealth/chameleon/OnMobLife(mob/living/mutant) +/datum/dna/gene/basic/chameleon/OnMobLife(mob/living/mutant) if((world.time - mutant.last_movement) >= 30 && (mutant.mobility_flags & MOBILITY_MOVE) && !HAS_TRAIT(mutant, TRAIT_RESTRAINED)) - mutant.alpha -= 25 + mutant.alpha_add(standartize_alpha(-25), ALPHA_SOURCE_CHAMELEON) else - mutant.alpha = round(255 * 0.80) + mutant.alpha_set(0.80, ALPHA_SOURCE_CHAMELEON) + +/datum/dna/gene/basic/chameleon/deactivate(mob/living/mutant, flags) + . = ..() + mutant.alpha_set(1, ALPHA_SOURCE_CHAMELEON) ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/code/game/gamemodes/antag_paradise/antag_paradise.dm b/code/game/gamemodes/antag_paradise/antag_paradise.dm index e0187d71d54..c9b8d7f2736 100644 --- a/code/game/gamemodes/antag_paradise/antag_paradise.dm +++ b/code/game/gamemodes/antag_paradise/antag_paradise.dm @@ -12,7 +12,7 @@ required_players = 10 required_enemies = 1 forbidden_antag_jobs = list(ROLE_VAMPIRE = list(JOB_TITLE_CHAPLAIN)) - var/list/protected_jobs_AI = list(JOB_TITLE_CIVILIAN, JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_VIROLOGIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_HOP, JOB_TITLE_CHAPLAIN, JOB_TITLE_BARTENDER, JOB_TITLE_CHEF, JOB_TITLE_BOTANIST, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER, JOB_TITLE_CLOWN, JOB_TITLE_MIME, JOB_TITLE_JANITOR, JOB_TITLE_LIBRARIAN, JOB_TITLE_BARBER, JOB_TITLE_EXPLORER) // Basically all jobs, except AI. + var/list/protected_jobs_AI = list(JOB_TITLE_CIVILIAN, JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_VIROLOGIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_HOP, JOB_TITLE_CHAPLAIN, JOB_TITLE_BARTENDER, JOB_TITLE_CHEF, JOB_TITLE_BOTANIST, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER, JOB_TITLE_CLOWN, JOB_TITLE_MIME, JOB_TITLE_JANITOR, JOB_TITLE_LIBRARIAN, JOB_TITLE_EXPLORER) // Basically all jobs, except AI. var/secondary_protected_species = list(SPECIES_MACNINEPERSON) var/vampire_restricted_jobs = list(JOB_TITLE_CHAPLAIN) /// Chosen antags if any. Key - mind, value - antag type @@ -206,7 +206,7 @@ break -/datum/game_mode/antag_paradise/pre_setup() +/datum/game_mode/antag_paradise/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index 685abb965e8..4a3797f0883 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -1,6 +1,6 @@ /datum/game_mode /// List of of blobs, their offsprings and blobburnouts spawned by them - var/list/blobs = list("infected"=list(), "offsprings"=list(), "blobernauts"=list()) + var/list/blobs = list("infected"=list(), "offsprings"=list(), "minions"=list()) /// Count of blob tiles to blob win var/blob_win_count = BLOB_BASE_TARGET_POINT /// Number of resource produced by the core @@ -15,6 +15,10 @@ var/off_auto_gamma = FALSE /// Disables automatic nuke codes var/off_auto_nuke_codes = FALSE + /// Is all blobs have infinity points + var/is_blob_infinity_points = FALSE + /// Is all blobs have infinity points + var/list/legit_blobs = list() /// Total blobs objective var/datum/objective/blob_critical_mass/blob_objective @@ -35,7 +39,7 @@ var/players_per_core = BLOB_PLAYERS_PER_CORE -/datum/game_mode/blob/pre_setup() +/datum/game_mode/blob/mid_setup() var/list/possible_blobs = get_players_for_role(ROLE_BLOB) @@ -110,7 +114,7 @@ /datum/game_mode/proc/update_blob_objective() if(blob_objective && !blob_objective.completed) - blob_objective.critical_mass = GLOB.blobs.len + blob_objective.critical_mass = legit_blobs.len blob_objective.needed_critical_mass = blob_win_count blob_objective.set_target() @@ -126,7 +130,7 @@ blob_list.Add(value) for(var/value in blobs["offsprings"]) blob_list.Add(value) - for(var/value in blobs["blobernauts"]) + for(var/value in blobs["minions"]) blob_list.Add(value) return blob_list @@ -196,35 +200,37 @@ return if(blob_stage == BLOB_STAGE_NONE) blob_stage = BLOB_STAGE_ZERO - if(blob_stage == BLOB_STAGE_ZERO && GLOB.blobs.len >= min(FIRST_STAGE_COEF * blob_win_count, FIRST_STAGE_THRESHOLD)) + if(blob_stage == BLOB_STAGE_ZERO && legit_blobs.len >= min(FIRST_STAGE_COEF * blob_win_count, FIRST_STAGE_THRESHOLD)) blob_stage = BLOB_STAGE_FIRST send_intercept(BLOB_FIRST_REPORT) SSshuttle?.emergency?.cancel() SSshuttle?.lockdown_escape() - if(blob_stage == BLOB_STAGE_FIRST && GLOB.blobs.len >= min(SECOND_STAGE_COEF * blob_win_count, SECOND_STAGE_THRESHOLD)) + if(blob_stage == BLOB_STAGE_FIRST && legit_blobs.len >= min(SECOND_STAGE_COEF * blob_win_count, SECOND_STAGE_THRESHOLD)) blob_stage = BLOB_STAGE_SECOND GLOB.event_announcement.Announce("Подтверждена вспышка биологической угрозы пятого уровня на борту [station_name()]. Весь персонал обязан локализовать угрозу.", - "ВНИМАНИЕ: БИОЛОГИЧЕСКАЯ УГРОЗА.", 'sound/AI/outbreak5.ogg') + "ВНИМАНИЕ: БИОЛОГИЧЕСКАЯ УГРОЗА.", 'sound/AI/outbreak5.ogg') if(!off_auto_gamma) addtimer(CALLBACK(GLOBAL_PROC, /proc/set_security_level, SEC_LEVEL_GAMMA), TIME_TO_SWITCH_CODE) - if(blob_stage == BLOB_STAGE_SECOND && GLOB.blobs.len >= THIRD_STAGE_COEF * blob_win_count) + if(blob_stage == BLOB_STAGE_SECOND && legit_blobs.len >= THIRD_STAGE_COEF * blob_win_count && (blob_win_count - legit_blobs.len) <= THIRD_STAGE_DELTA_THRESHOLD) blob_stage = BLOB_STAGE_THIRD send_intercept(BLOB_SECOND_REPORT) - if(GLOB.blobs.len >= blob_win_count && blob_stage < BLOB_STAGE_STORM) + if(legit_blobs.len >= blob_win_count && blob_stage < BLOB_STAGE_STORM) if(SSweather) blob_stage = BLOB_STAGE_STORM SSweather.run_weather(/datum/weather/blob_storm) + show_warning("Вы набрали критическую массу и ощущаете практически бесконечный приток ресурсов.") + is_blob_infinity_points = TRUE addtimer(CALLBACK(src, PROC_REF(process_blob_stages)), STAGES_CALLBACK_TIME) /datum/game_mode/proc/show_warning(message) - for(var/datum/mind/blob in blobs["infected"]) + for(var/datum/mind/blob in (blobs["infected"] + blobs["offsprings"])) if(blob.current.stat != DEAD) - to_chat(blob.current, "[message]") + to_chat(blob.current, span_warning("[message]")) /datum/game_mode/proc/burst_blobs() diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index db27c5d382c..83b8f3e819d 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -8,7 +8,7 @@ return update_blob_objective() GLOB.event_announcement.Announce("Объект потерян. Причина: распостранение 5-ой биоугрозы. Взведение устройства самоуничтожения персоналом или внешними силами в данный момент не представляется возможным из-за высокого уровня заражения. Решение: оставить станцию в изоляции до принятия окончательных мер противодействия.", - "Отчет об объекте [station_name()]") + "Отчет об объекте [station_name()]") blob_stage = (delay_blob_end)? BLOB_STAGE_POST_END : BLOB_STAGE_END if(blob_stage == BLOB_STAGE_END) end_game() @@ -62,7 +62,7 @@ /datum/game_mode/proc/auto_declare_completion_blob() var/list/blob_infected = blobs["infected"] var/list/blob_offsprings = blobs["offsprings"] - var/list/blobernauts = blobs["blobernauts"] + var/list/minions = blobs["minions"] if(blob_infected?.len) declare_blob_completion() var/text = "
Блоб[(blob_infected.len > 1 ? "ами были" : "ом был")]:" @@ -75,9 +75,9 @@ for(var/datum/mind/blob in blob_offsprings) text += "
[blob.key] был [blob.name]" - if(blobernauts?.len) - text += "

Блобернаут[(blobernauts.len > 1 ? "ами были" : "ом был")]:" - for(var/datum/mind/blob in blobernauts) + if(minions?.len) + text += "

Миньoн[(minions.len > 1 ? "ами были" : "ом был")]:" + for(var/datum/mind/blob in minions) text += "
[blob.key] был [blob.name]" to_chat(world, text) diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 79f5bc1764d..fadbd95cda1 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -11,11 +11,11 @@ intercepttext += "Предварительный анализ организма классифицирует его как биологическую угрозу 5-го уровня. Его происхождение неизвестно.
" intercepttext += "Nanotrasen выпустила директиву 7-10 для [station_name()]. Станцию следует считать закрытой на карантин.
" intercepttext += "Приказы для всего персонала [station_name()] следующие:
" - intercepttext += " 1. Не покидайте карантинную зону
" - intercepttext += " 2. Обнаружить любые очаги угрозы на станции.
" - intercepttext += " 3. При обнаружении используйте любые необходимые средства для сдерживания организма.
" - intercepttext += " 4. Избегайте повреждения критической инфраструктуры станции.
" - intercepttext += "
Примечание. в случае нарушения карантина или неконтролируемого распространения биологической опасности директива 7-10 может быть дополнена директивой 7-12.
" + intercepttext += " 1. Не покидать карантинную зону.
" + intercepttext += " 2. Обнаружить все очаги угрозы на станции.
" + intercepttext += " 3. При обнаружении использовать любые необходимые средства для сдерживания организмов.
" + intercepttext += " 4. Избегать повреждения критической инфраструктуры станции.
" + intercepttext += "
Примечание. в случае нарушения карантина или неконтролируемого распространения биологической угрозы директива 7-10 может быть дополнена директивой 7-12.
" intercepttext += "Конец сообщения." if(BLOB_SECOND_REPORT) var/nukecode = rand(10000, 99999) @@ -29,8 +29,8 @@ intercepttext += "Для [station_name()] была издана директива 7-12.
" intercepttext += "Биологическая угроза вышла из-под контроля и скоро достигнет критической массы.
" intercepttext += "Вам приказано следующее:
" - intercepttext += " 1. Защитите диск ядерной аутентификации.
" - intercepttext += " 2. Взорвите ядерную боеголовку, находящуюся в хранилище станции.
" + intercepttext += " 1. Защищать диск ядерной аутентификации.
" + intercepttext += " 2. Взорвать ядерную боеголовку, находящуюся в хранилище станции.
" if(off_auto_nuke_codes) intercepttext += "Код ядерной аутентификации будет выслан в скором времени отдельным сообщением. Ожидайте.
" else @@ -54,7 +54,7 @@ intercepttext += "Дирректива 7-12 была отменена для [station_name()].
" intercepttext += "Биоугроза уничтожена, либо ее остаточные следы не представляют опасности.
" intercepttext += "Вам приказано следующее:
" - intercepttext += " 1. Уничтожьте все полученные засекреченные сообщения.
" + intercepttext += " 1. Уничтожить все полученные засекреченные сообщения.
" intercepttext += " 2. В случае невозможности продолжать смену ввиду потерь среди экипажа или критического состояния станции, провести эвакуацию экипажа.
" if(blob_stage == BLOB_STAGE_THIRD && !off_auto_nuke_codes) intercepttext += " 3. Код от боеголовки, как и ее назначение необходимо держать в строжайшей секретности.
" diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm deleted file mode 100644 index 24c3898a7cb..00000000000 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ /dev/null @@ -1,262 +0,0 @@ - -//////////////// -// BASE TYPE // -//////////////// - -//Do not spawn -/mob/living/simple_animal/hostile/blob - icon = 'icons/mob/blob.dmi' - pass_flags = PASSBLOB - status_flags = NONE //No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you. - faction = list(ROLE_BLOB) - bubble_icon = "blob" - 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) - universal_speak = 1 //So mobs can understand them when a blob uses Blob Broadcast - sentience_type = SENTIENCE_OTHER - gold_core_spawnable = NO_SPAWN - can_be_on_fire = TRUE - fire_damage = 3 - var/mob/camera/blob/overmind = null - tts_seed = "Earth" - -/mob/living/simple_animal/hostile/blob/ComponentInitialize() - AddComponent( \ - /datum/component/animal_temperature, \ - maxbodytemp = 360, \ - minbodytemp = 0, \ - ) - -/mob/living/simple_animal/hostile/blob/proc/adjustcolors(var/a_color) - if(a_color) - color = a_color - -/mob/living/simple_animal/hostile/blob/blob_act() - if(stat != DEAD && health < maxHealth) - for(var/i in 1 to 2) - var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed - if(overmind) - H.color = overmind.blob_reagent_datum.complementary_color - else - H.color = "#000000" - adjustHealth(-maxHealth * 0.0125) - - -//////////////// -// BLOB SPORE // -//////////////// - -/mob/living/simple_animal/hostile/blob/blobspore - name = "blob" - desc = "Some blob thing." - icon_state = "blobpod" - icon_living = "blobpod" - health = 40 - maxHealth = 40 - melee_damage_lower = 2 - melee_damage_upper = 4 - obj_damage = 20 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES - attacktext = "ударяет" - attack_sound = 'sound/weapons/genhit1.ogg' - speak_emote = list("pulses") - var/obj/structure/blob/factory/factory = null - var/list/human_overlays - var/mob/living/carbon/human/oldguy - var/is_zombie = FALSE - - -/mob/living/simple_animal/hostile/blob/blobspore/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - if(istype(mover, /obj/structure/blob)) - return TRUE - - -/mob/living/simple_animal/hostile/blob/blobspore/New(loc, var/obj/structure/blob/factory/linked_node) - if(istype(linked_node)) - factory = linked_node - factory.spores += src - ..() - - -/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) - AddElement(/datum/element/simple_flying) - - -/mob/living/simple_animal/hostile/blob/blobspore/Life(seconds, times_fired) - - if(!is_zombie && isturf(src.loc)) - for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile - if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD)) - Zombify(H) - break - ..() - -/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H) - if(!H.check_death_method()) - H.death() - var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) - is_zombie = TRUE - if(H.wear_suit) - var/obj/item/clothing/suit/armor/A = H.wear_suit - if(A.armor && A.armor.getRating("melee")) - maxHealth += A.armor.getRating("melee") //That zombie's got armor, I want armor! - maxHealth += 40 - health = maxHealth - name = "blob zombie" - desc = "A shambling corpse animated by the blob." - melee_damage_lower = 10 - melee_damage_upper = 15 - icon = H.icon - speak_emote = list("groans") - icon_state = "zombie2_s" - if(head_organ) - head_organ.h_style = null - H.update_hair() - human_overlays = H.overlays - update_icons() - H.forceMove(src) - oldguy = H - visible_message("The corpse of [H.name] suddenly rises!") - -/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed) - // Only execute the below if we successfuly died - . = ..() - if(!.) - return FALSE - // On death, create a small smoke of harmful gas (s-Acid) - var/datum/effect_system/smoke_spread/chem/S = new - var/turf/location = get_turf(src) - - // Create the reagents to put into the air - create_reagents(350) - - if(overmind && overmind.blob_reagent_datum) - reagents.add_reagent(overmind.blob_reagent_datum.id, 350) - else - reagents.add_reagent("spore", 350) - - // Setup up the smoke spreader and start it. - S.set_up(reagents, location, TRUE) - S.start() - qdel(src) - -/mob/living/simple_animal/hostile/blob/blobspore/Destroy() - if(factory) - factory.spores -= src - factory = null - if(oldguy) - oldguy.forceMove(get_turf(src)) - oldguy = null - return ..() - - -/mob/living/simple_animal/hostile/blob/blobspore/update_icons() - ..() - - adjustcolors(overmind?.blob_reagent_datum?.complementary_color) - -/mob/living/simple_animal/hostile/blob/blobspore/adjustcolors(var/a_color) - color = a_color - - if(is_zombie) - cut_overlays() - add_overlay(human_overlays) - var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head") - I.color = color - add_overlay(I) - - if(blocks_emissive) - add_overlay(get_emissive_block()) - - -///////////////// -// BLOBBERNAUT // -///////////////// - -/mob/living/simple_animal/hostile/blob/blobbernaut - name = "blobbernaut" - desc = "Some HUGE blob thing." - icon_state = "blobbernaut" - icon_living = "blobbernaut" - icon_dead = "blobbernaut_dead" - health = 200 - maxHealth = 200 - melee_damage_lower = 10 - melee_damage_upper = 15 - obj_damage = 60 - attacktext = "ударяет" - attack_sound = 'sound/effects/blobattack.ogg' - speak_emote = list("gurgles") - force_threshold = 10 - mob_size = MOB_SIZE_LARGE - environment_smash = ENVIRONMENT_SMASH_STRUCTURES - pressure_resistance = 50 - sight = SEE_TURFS|SEE_MOBS|SEE_OBJS - nightvision = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - move_resist = MOVE_FORCE_OVERPOWERING - -/mob/living/simple_animal/hostile/ancient_robot_leg/ComponentInitialize() - AddComponent( \ - /datum/component/animal_temperature, \ - minbodytemp = 0, \ - maxbodytemp = 360, \ - ) - -/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize(mapload) - . = ..() - ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, INNATE_TRAIT) - - -/mob/living/simple_animal/hostile/blob/blobbernaut/experience_pressure_difference(pressure_difference, direction) - if(!HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) - return ..() - -/mob/living/simple_animal/hostile/blob/blobbernaut/proc/add_to_gamemode() - var/list/blobernauts = SSticker?.mode?.blobs["blobernauts"] - blobernauts |= mind - -/mob/living/simple_animal/hostile/blob/blobbernaut/Life(seconds, times_fired) - if(stat != DEAD && (getBruteLoss() || getFireLoss())) // Heal on blob structures - if(locate(/obj/structure/blob) in get_turf(src)) - heal_overall_damage(0.25, 0.25) - if(on_fire) - adjust_fire_stacks(-1) // Slowly extinguish the flames - else - take_overall_damage(0.2, 0.2) // If you are at full health, you won't lose health. You'll need it. However the moment anybody sneezes on you, the decaying will begin. - ..() - -/mob/living/simple_animal/hostile/blob/blobbernaut/New() - ..() - if(name == "blobbernaut") - name = text("blobbernaut ([rand(1, 1000)])") - -/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed) - mind.name = name - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - flick("blobbernaut_death", src) - -/mob/living/simple_animal/hostile/blob/blobbernaut/verb/communicate_overmind() - set category = "Blobbernaut" - set name = "Blob Telepathy" - set desc = "Send a message to the Overmind" - - if(stat != DEAD) - blob_talk() - -/mob/living/simple_animal/hostile/blob/blobbernaut/proc/blob_talk() - var/message = tgui_input_text(usr, "Announce to the overmind", "Blob Telepathy") - var/rendered = "Blob Telepathy, [name]([overmind]) states, \"[message]\"" - if(message) - for(var/mob/M in GLOB.mob_list) - if(isovermind(M) || isblobbernaut(M) || isblobinfected(M.mind)) - M.show_message(rendered, 2) - else if(isobserver(M) && !isnewplayer(M)) - var/rendered_ghost = "Blob Telepathy, [name]([overmind]) \ - (F) states, \"[message]\"" - M.show_message(rendered_ghost, 2) diff --git a/code/game/gamemodes/blob/blobs/captured_nuke.dm b/code/game/gamemodes/blob/blobs/captured_nuke.dm deleted file mode 100644 index 7256332d366..00000000000 --- a/code/game/gamemodes/blob/blobs/captured_nuke.dm +++ /dev/null @@ -1,27 +0,0 @@ -/obj/structure/blob/captured_nuke //alternative to blob just straight up destroying nukes - name = "blob captured nuke" - icon_state = "blob" - desc = "A Nuclear Warhead tangled in blob tendrils pulsating with a horrific green glow." - max_integrity = 100 - point_return = 0 - -/obj/structure/blob/captured_nuke/Initialize(mapload, obj/machinery/nuclearbomb/N) - . = ..() - START_PROCESSING(SSobj, src) - N?.forceMove(src) - update_icon(UPDATE_OVERLAYS) - - -/obj/structure/blob/captured_nuke/update_overlays() - . = ..() - . += mutable_appearance('icons/mob/blob.dmi', "blob_nuke_overlay", appearance_flags = RESET_COLOR) - - -/obj/structure/blob/captured_nuke/Destroy() - for(var/obj/machinery/nuclearbomb/O in contents) - O.forceMove(loc) - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/blob/captured_nuke/Life(seconds, times_fired) - obj_integrity = min(max_integrity, obj_integrity + 1) diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm deleted file mode 100644 index 6b94aa23d1a..00000000000 --- a/code/game/gamemodes/blob/blobs/core.dm +++ /dev/null @@ -1,154 +0,0 @@ -/obj/structure/blob/core - name = "blob core" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 400 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) - fire_resist = 2 - point_return = -1 - var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes - var/resource_delay = 0 - var/point_rate = 2 - var/is_offspring = null - var/selecting = 0 - -/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) - GLOB.blob_cores += src - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(offspring) - is_offspring = TRUE - point_rate = new_rate - ..(loc, h) - - -/obj/structure/blob/core/adjustcolors(a_color) - cut_overlays() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - add_overlay(I) - var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") - add_overlay(C) - - if(blocks_emissive) - add_overlay(get_emissive_block()) - - -/obj/structure/blob/core/Destroy() - GLOB.blob_cores -= src - if(overmind) - overmind.blob_core = null - overmind = null - SSticker?.mode?.blob_died() - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - return ..() - -/obj/structure/blob/core/ex_act(severity) - var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity - take_damage(damage, BRUTE, "bomb", 0) - -/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) - . = ..() - if(obj_integrity > 0) - if(overmind) //we should have an overmind, but... - overmind.update_health_hud() - -/obj/structure/blob/core/RegenHealth() - return // Don't regen, we handle it in Life() - -/obj/structure/blob/core/Life(seconds, times_fired) - if(!overmind) - create_overmind() - else - if(resource_delay <= world.time) - resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - obj_integrity = min(max_integrity, obj_integrity + 1) - if(overmind) - overmind.update_health_hud() - if(overmind?.blob_reagent_datum?.color) - for(var/i = 1; i < 8; i += i) - Pulse(0, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(0, i, color) - for(var/b_dir in GLOB.alldirs) - if(!prob(5)) - continue - var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) - if(B) - B.change_to(/obj/structure/blob/shield/core) - if(B && overmind?.blob_reagent_datum?.color) - B.color = overmind.blob_reagent_datum.color - else - B.color = color - color = null - ..() - - -/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay) - if(overmind_get_delay > world.time && !override_delay) - return - - overmind_get_delay = world.time + 5 MINUTES - - if(overmind) - qdel(overmind) - - INVOKE_ASYNC(src, PROC_REF(get_new_overmind), new_overmind) - -/obj/structure/blob/core/proc/get_new_overmind(client/new_overmind) - var/mob/C = null - var/list/candidates = list() - if(!new_overmind) - // sendit - if(is_offspring) - candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, source = src) - else - candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, source = src) - - if(length(candidates)) - C = pick(candidates) - else - C = new_overmind - - if(C && !QDELETED(src)) - var/mob/camera/blob/B = new(loc) - B.key = C.key - B.blob_core = src - overmind = B - B.is_offspring = is_offspring - addtimer(CALLBACK(src, PROC_REF(add_datum_if_not_exist)), TIME_TO_ADD_OM_DATUM) - log_game("[B.key] has become Blob [is_offspring ? "offspring" : ""]") - -/obj/structure/blob/core/proc/lateblobtimer() - addtimer(CALLBACK(src, PROC_REF(lateblobcheck)), 50) - -/obj/structure/blob/core/proc/lateblobcheck() - if(overmind) - overmind.add_points(60) - if(!overmind.mind) - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") - -/obj/structure/blob/core/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer) - overmind?.forceMove(get_turf(src)) - return ..() - -/obj/structure/blob/core/proc/add_datum_if_not_exist() - overmind.select_reagent() - if(!overmind.mind.has_antag_datum(/datum/antagonist/blob_overmind)) - var/datum/antagonist/blob_overmind/overmind_datum = new - overmind_datum.add_to_mode = TRUE - overmind_datum.is_offspring = is_offspring - if(overmind.blob_reagent_datum) - overmind_datum.reagent = overmind.blob_reagent_datum - overmind.mind.add_antag_datum(overmind_datum) - color = overmind.blob_reagent_datum?.color - diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm deleted file mode 100644 index 517d6ece7fd..00000000000 --- a/code/game/gamemodes/blob/blobs/factory.dm +++ /dev/null @@ -1,30 +0,0 @@ -/obj/structure/blob/factory - name = "factory blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_factory" - max_integrity = 200 - point_return = 18 - var/list/spores = list() - var/max_spores = 3 - var/spore_delay = 0 - var/is_waiting_spawn = FALSE - -/obj/structure/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) - if(spore.factory == src) - spore.factory = null - spores = null - return ..() - -/obj/structure/blob/factory/run_action() - if(spores.len >= max_spores) - return - if(spore_delay > world.time) - return - flick("blob_factory_glow", src) - spore_delay = world.time + 100 // 10 seconds - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) - BS.color = overmind?.blob_reagent_datum?.complementary_color - BS.overmind = overmind - overmind.blob_mobs.Add(BS) diff --git a/code/game/gamemodes/blob/blobs/node.dm b/code/game/gamemodes/blob/blobs/node.dm deleted file mode 100644 index ca570996aec..00000000000 --- a/code/game/gamemodes/blob/blobs/node.dm +++ /dev/null @@ -1,39 +0,0 @@ -/obj/structure/blob/node - name = "blob node" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) - point_return = 18 - -/obj/structure/blob/node/New(loc, h = 100) - GLOB.blob_nodes += src - START_PROCESSING(SSobj, src) - ..(loc, h) - -/obj/structure/blob/node/adjustcolors(a_color) - cut_overlays() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - add_overlay(I) - var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") - add_overlay(C) - - if(blocks_emissive) - add_overlay(get_emissive_block()) - -/obj/structure/blob/node/Destroy() - GLOB.blob_nodes -= src - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/blob/node/Life(seconds, times_fired) - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(5, i, overmind.blob_reagent_datum?.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(5, i, color) - obj_integrity = min(max_integrity, obj_integrity + 1) - color = null diff --git a/code/game/gamemodes/blob/blobs/resource.dm b/code/game/gamemodes/blob/blobs/resource.dm deleted file mode 100644 index 4ef14ed4963..00000000000 --- a/code/game/gamemodes/blob/blobs/resource.dm +++ /dev/null @@ -1,15 +0,0 @@ -/obj/structure/blob/resource - name = "resource blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_resource" - max_integrity = 60 - point_return = 12 - var/resource_delay = 0 - -/obj/structure/blob/resource/run_action() - if(resource_delay > world.time) - return - flick("blob_resource_glow", src) - resource_delay = world.time + 40 // 4 seconds - if(overmind) - overmind.add_points(1) diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm deleted file mode 100644 index f95fc50b910..00000000000 --- a/code/game/gamemodes/blob/blobs/shield.dm +++ /dev/null @@ -1,80 +0,0 @@ -/obj/structure/blob/shield - name = "strong blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_shield" - desc = "Some blob creature thingy" - max_integrity = 150 - brute_resist = 0.25 - explosion_block = 3 - explosion_vertical_block = 2 - atmosblock = TRUE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - -/obj/structure/blob/shield/core - point_return = 0 - - -/obj/structure/blob/shield/check_integrity() - var/old_compromised_integrity = compromised_integrity - if(obj_integrity < max_integrity * 0.5) - compromised_integrity = TRUE - else - compromised_integrity = FALSE - if(old_compromised_integrity != compromised_integrity) - update_state() - update_appearance(UPDATE_NAME|UPDATE_DESC|UPDATE_ICON_STATE) - - -/obj/structure/blob/shield/update_state() - if(compromised_integrity) - atmosblock = FALSE - else - atmosblock = TRUE - air_update_turf(1) - - -/obj/structure/blob/shield/update_name(updates = ALL) - . = ..() - if(compromised_integrity) - name = "weakened [initial(name)]" - else - name = initial(name) - - -/obj/structure/blob/shield/update_desc(updates = ALL) - . = ..() - if(compromised_integrity) - desc = "A wall of twitching tendrils." - else - desc = initial(desc) - - -/obj/structure/blob/shield/update_icon_state() - if(compromised_integrity) - icon_state = "[initial(icon_state)]_damaged" - else - icon_state = initial(icon_state) - - -/obj/structure/blob/shield/reflective - name = "reflective blob" - desc = "A solid wall of slightly twitching tendrils with a reflective glow." - icon_state = "blob_glow" - max_integrity = 100 - brute_resist = 0.5 - explosion_block = 2 - explosion_vertical_block = 1 - point_return = 9 - flags = CHECK_RICOCHET - -/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.set_angle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE diff --git a/code/game/gamemodes/blob/blobs/storage.dm b/code/game/gamemodes/blob/blobs/storage.dm deleted file mode 100644 index b9052b3601a..00000000000 --- a/code/game/gamemodes/blob/blobs/storage.dm +++ /dev/null @@ -1,16 +0,0 @@ -/obj/structure/blob/storage - name = "storage blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_resource" - max_integrity = 30 - fire_resist = 2 - point_return = 12 - -/obj/structure/blob/storage/obj_destruction(damage_flag) - if(overmind) - overmind.max_blob_points -= 50 - ..() - -/obj/structure/blob/storage/proc/update_max_blob_points(var/new_point_increase) - if(overmind) - overmind.max_blob_points += new_point_increase diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm deleted file mode 100644 index d060d2c7890..00000000000 --- a/code/game/gamemodes/blob/overmind.dm +++ /dev/null @@ -1,134 +0,0 @@ -/mob/camera/blob - name = "Blob Overmind" - real_name = "Blob Overmind" - icon = 'icons/mob/blob.dmi' - icon_state = "marker" - nightvision = 8 - sight = SEE_TURFS|SEE_MOBS|SEE_OBJS - invisibility = INVISIBILITY_OBSERVER - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - mouse_opacity = MOUSE_OPACITY_OPAQUE - see_invisible = SEE_INVISIBLE_LIVING - pass_flags = PASSBLOB - faction = list(ROLE_BLOB) - - var/obj/structure/blob/core/blob_core = null // The blob overmind's core - var/blob_points = 0 - var/max_blob_points = 100 - var/last_attack = 0 - var/nodes_required = TRUE //if the blob needs nodes to place resource and factory blobs - var/split_used = FALSE - var/is_offspring = FALSE - var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob() - var/list/blob_mobs = list() - -/mob/camera/blob/New() - var/new_name = "[initial(name)] ([rand(1, 999)])" - name = new_name - real_name = new_name - last_attack = world.time - ..() - START_PROCESSING(SSobj, src) - -/mob/camera/blob/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/mob/camera/blob/process() - if(!blob_core) - qdel(src) - -/mob/camera/blob/Login() - ..() - sync_mind() - update_health_hud() - sync_lighting_plane_alpha() - -/mob/camera/blob/update_health_hud() - if(blob_core && hud_used) - hud_used.blobhealthdisplay.maptext = "
[round(blob_core.obj_integrity)]
" - -/mob/camera/blob/proc/add_points(var/points) - if(points != 0) - blob_points = clamp(blob_points + points, 0, max_blob_points) - if(hud_used) - hud_used.blobpwrdisplay.maptext = "
[round(src.blob_points)]
" - - -/mob/camera/blob/memory() - SSticker.mode.update_blob_objective() - ..() - -/mob/camera/blob/say(message) - if(!message) - return - - if(client) - if(check_mute(client.ckey, MUTE_IC)) - to_chat(src, "You cannot send IC messages (muted).") - return - if(client.handle_spam_prevention(message, MUTE_IC)) - return - - if(stat) - return - - blob_talk(message) - - -/mob/camera/blob/proc/blob_talk(message) - add_say_logs(src, message, language = "BLOB") - - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) - - if(!message) - return - - var/rendered = "Blob Telepathy, [name]([blob_reagent_datum.name]) states, \"[message]\"" - for(var/mob/M in GLOB.mob_list) - if(isovermind(M) || isblobbernaut(M) || isblobinfected(M.mind)) - M.show_message(rendered, 2) - else if(isobserver(M) && !isnewplayer(M)) - var/rendered_ghost = "Blob Telepathy, \ - [name]([blob_reagent_datum.name]) \ - (F) states, \"[message]\"" - M.show_message(rendered_ghost, 2) - - -/mob/camera/blob/blob_act(obj/structure/blob/B) - return - -/mob/camera/blob/get_status_tab_items() - var/list/status_tab_data = ..() - . = status_tab_data - if(blob_core) - status_tab_data[++status_tab_data.len] = list("Core Health:", "[blob_core.obj_integrity]") - status_tab_data[++status_tab_data.len] = list("Power Stored:", "[blob_points]/[max_blob_points]") - -/mob/camera/blob/Move(atom/newloc, direct = NONE, glide_size_override = 0, update_dir = TRUE) - if(world.time < last_movement) - return - last_movement = world.time + 0.5 // cap to 20fps - - var/obj/structure/blob/B = locate() in range("3x3", newloc) - if(B) - loc = newloc - else - return 0 - -/mob/camera/blob/proc/can_attack() - return (world.time > (last_attack + CLICK_CD_RANGE)) - -/mob/camera/blob/proc/select_reagent() - var/list/possible_reagents = list() - var/datum/antagonist/blob_overmind/overmind_datum = mind?.has_antag_datum(/datum/antagonist/blob_overmind) - if(!overmind_datum) - for(var/type in subtypesof(/datum/reagent/blob)) - possible_reagents.Add(new type) - blob_reagent_datum = pick(possible_reagents) - else - blob_reagent_datum = overmind_datum.reagent - if(blob_core) - blob_core.adjustcolors(blob_reagent_datum.color) - - color = blob_reagent_datum.complementary_color diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm deleted file mode 100644 index f2ec8f7c9ac..00000000000 --- a/code/game/gamemodes/blob/powers.dm +++ /dev/null @@ -1,497 +0,0 @@ -// Point controlling procs - -/mob/camera/blob/proc/can_buy(var/cost = 15) - if(blob_points < cost) - to_chat(src, "Вы не можете себе это позволить!") - return 0 - add_points(-cost) - return 1 - -// Power verbs - -/mob/camera/blob/verb/transport_core() - set category = "Blob" - set name = "Jump to Core" - set desc = "Возвращает вас к вашему ядру." - - if(blob_core) - src.loc = blob_core.loc - -/mob/camera/blob/verb/jump_to_node() - set category = "Blob" - set name = "Jump to Node" - set desc = "Перемещает вас к выбранному узлу." - - if(GLOB.blob_nodes.len) - var/list/nodes = list() - for(var/i = 1; i <= GLOB.blob_nodes.len; i++) - var/obj/structure/blob/node/B = GLOB.blob_nodes[i] - nodes["Blob Node #[i] ([get_location_name(B)])"] = B - var/node_name = input(src, "Выберете.", "Перемещение к узлу") in nodes - var/obj/structure/blob/node/chosen_node = nodes[node_name] - if(chosen_node) - src.loc = chosen_node.loc - -/mob/camera/blob/verb/toggle_node_req() - set category = "Blob" - set name = "Toggle Node Requirement" - set desc = "Переключить требование узла для размещения ресурсной плитки и фабрики." - nodes_required = !nodes_required - if(nodes_required) - to_chat(src, "Теперь вам необходимо иметь узел или ядро рядом ​​для размещения фабрики и ресурсной плитки.") - else - to_chat(src, "Теперь вам не нужно иметь узел или ядро рядом ​​для размещения фабрики и ресурсной плитки.") - -/mob/camera/blob/verb/create_shield_power() - set category = "Blob" - set name = "Create/Upgrade Shield Blob (15)" - set desc = "Создайте/улучшите крепкую плитку. Использование на существующей крепкой плитке превращает её в отражающую плитку, способную отражать большинство энергетических снарядов, но делая её намного слабее для остальных атак." - - var/turf/T = get_turf(src) - create_shield(T) - -/mob/camera/blob/proc/create_shield(var/turf/T) - - var/obj/structure/blob/B = locate(/obj/structure/blob) in T - var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T - - if(!S) - if(!B)//We are on a blob - to_chat(src, "Тут нет плитки!") - return - - else if(!istype(B, /obj/structure/blob/normal)) - to_chat(src, "Невозможно использовать на этой плитке. Найдите обычную плитку.") - return - - else if(!can_buy(15)) - return - - B.color = blob_reagent_datum.color - B.change_to(/obj/structure/blob/shield) - else - - if(istype(S, /obj/structure/blob/shield/reflective)) - to_chat(src, "Здесь уже отражающая плитка!") - return - - - else if(S.obj_integrity < S.max_integrity * 0.5) - to_chat(src, "Эта крепкая плитка слишком повреждена, чтобы ее можно было модифицировать!") - return - - else if (!can_buy(15)) - return - - to_chat(src, "Вы выделяете отражающую слизь на крепкую плитку, позволяя ей отражать энергетические снаряды ценой снижения прочности.") - - S.change_to(/obj/structure/blob/shield/reflective) - S.color = blob_reagent_datum.color - return - -/mob/camera/blob/verb/create_resource() - set category = "Blob" - set name = "Create Resource Blob (40)" - set desc = "Создайте ресурсную плитку, которая будет приносить вам ресурсы." - - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - - if(!B)//We are on a blob - to_chat(src, "Тут нет плитки!") - return - - if(!istype(B, /obj/structure/blob/normal)) - to_chat(src, "Невозможно использовать на этой плитке. Найдите обычную плитку.") - return - for(var/obj/structure/blob/resource/blob in orange(4, T)) - to_chat(src, "Поблизости находится ресурсная плитка, отойдите на расстояние более 4 плиток от неё!") - return - - if(nodes_required) - if(!(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T))) - to_chat(src, "Вам нужно разместить этот объект ближе к узлу или ядру!") - return //handholdotron 2000 - - if(!can_buy(40)) - return - - B.color = blob_reagent_datum.color - B.change_to(/obj/structure/blob/resource) - var/obj/structure/blob/resource/R = locate() in T - if(R) - R.overmind = src - - return - -/mob/camera/blob/verb/create_node() - set category = "Blob" - set name = "Create Node Blob (60)" - set desc = "Создает узел." - - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - - if(!B)//We are on a blob - to_chat(src, "Тут нет плитки блоба!") - return - - if(!istype(B, /obj/structure/blob/normal)) - to_chat(src, "Невозможно использовать на этой плитке. Найдите обычную плитку.") - return - - for(var/obj/structure/blob/node/blob in orange(5, T)) - to_chat(src, "Поблизости находится узел, отойдите на расстояние более 5 плиток от него!") - return - - if(!can_buy(60)) - return - - B.change_to(/obj/structure/blob/node) - var/obj/structure/blob/node/R = locate() in T - if(R) - R.adjustcolors(blob_reagent_datum.color) - R.overmind = src - return - - -/mob/camera/blob/verb/create_factory() - set category = "Blob" - set name = "Create Factory Blob (60)" - set desc = "Создает плитку, производящую споры." - - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/B = locate(/obj/structure/blob) in T - if(!B) - to_chat(src, "Тут нет плитки!") - return - - if(!istype(B, /obj/structure/blob/normal)) - to_chat(src, "Невозможно использовать на этой плитке. Найдите обычную плитку.") - return - - for(var/obj/structure/blob/factory/blob in orange(7, T)) - to_chat(src, "Поблизости находится фабрика, отойдите на расстояние более 7 плиток от неё!") - return - - if(nodes_required) - if(!(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T))) - to_chat(src, "Вам нужно разместить этот объект ближе к узлу или ядру!") - return //handholdotron 2000 - - if(!can_buy(60)) - return - - B.change_to(/obj/structure/blob/factory) - B.color = blob_reagent_datum.color - var/obj/structure/blob/factory/R = locate() in T - if(R) - R.overmind = src - return - - -/mob/camera/blob/verb/create_blobbernaut() - set category = "Blob" - set name = "Create Blobbernaut (60)" - set desc = "Создает сильное порождение блоба. Блобернаута!" - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/B = locate(/obj/structure/blob) in T - if(!B) - to_chat(src, "Вы должны быть на плитке блоба!") - return FALSE - - if(!istype(B, /obj/structure/blob/factory)) - to_chat(src, "Невозможно использовать эту плитку, найдите фабрику.") - return FALSE - var/obj/structure/blob/factory/b_fac = B - - if(b_fac.is_waiting_spawn) - return FALSE - - if(!can_buy(60)) - return FALSE - - spawn() - var/mob/C - b_fac.is_waiting_spawn = TRUE - - var/list/candidates = SSghost_spawns.poll_candidates("Вы хотите сыграть за блобернаута?", ROLE_BLOB, TRUE, 10 SECONDS, source = /mob/living/simple_animal/hostile/blob/blobbernaut) - if(length(candidates)) - C = pick(candidates) - - if(!C) - add_points(60) - b_fac.is_waiting_spawn = FALSE - - if(b_fac && b_fac.is_waiting_spawn) //Если фабрика цела и её не разрушили во время голосования - var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new (get_turf(b_fac)) - qdel(b_fac) - blobber.key = C.key - log_game("[blobber.key] has spawned as Blobbernaut") - to_chat(blobber, "Вы блобернаут! Вы должны помочь всем формам блоба в их миссии по уничтожению всего!") - to_chat(blobber, "Вы исцеляетесь, стоя на плитках блоба, однако вы будете медленно разлагаться, если получите урон за пределами блоба.") - - blobber.color = blob_reagent_datum.complementary_color - blobber.overmind = src - blob_mobs.Add(blobber) - blobber.AIStatus = AI_OFF - blobber.LoseTarget() - addtimer(CALLBACK(blobber, TYPE_PROC_REF(/mob/living/simple_animal/hostile/blob/blobbernaut/, add_to_gamemode)), TIME_TO_ADD_OM_DATUM) - return TRUE - - -/mob/camera/blob/verb/relocate_core() - set category = "Blob" - set name = "Relocate Core (80)" - set desc = "Перемещает ваше ядро ​​на узел, на котором вы находитесь, ваше старое ядро ​​будет превращено в узел." - - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/node/B = locate(/obj/structure/blob/node) in T - if(!B) - to_chat(src, "Вы должны быть на узле!") - return - - if(!can_buy(80)) - return - - // The old switcharoo. - var/turf/old_turf = blob_core.loc - blob_core.loc = T - B.loc = old_turf - return - - -/mob/camera/blob/verb/revert() - set category = "Blob" - set name = "Remove Blob" - set desc = "Удаляет плитку. Вы получите 30 % возмещение стоимости специальных структур блоба." - - var/turf/T = get_turf(src) - remove_blob(T) - -/mob/camera/blob/proc/remove_blob(var/turf/T) - - var/obj/structure/blob/B = locate(/obj/structure/blob) in T - if(!T) - return - if(!B) - to_chat(src, "Тут нет плитки блоба!") - return - if(B.point_return < 0) - to_chat(src, "Невозможно удалить эту плитку!") - return - if(max_blob_points < B.point_return + blob_points) - to_chat(src, "У вас слишком много ресурсов для удаления этой плитки!") - return - if(B.point_return) - add_points(B.point_return) - to_chat(src, "Получено [B.point_return] ресурса после удаления \the [B].") - qdel(B) - return - - -/mob/camera/blob/verb/expand_blob_power() - set category = "Blob" - set name = "Expand/Attack Blob (5)" - set desc = "Пытается создать новую плитку блоба в этом тайле. Если тайл не чист, мы наносим урон объекту, находящемуся в нем, что может его очистить." - - var/turf/T = get_turf(src) - expand_blob(T) - -/mob/camera/blob/proc/expand_blob(var/turf/T) - if(!T) - return - - if(!can_attack()) - return - - if(!is_location_within_transition_boundaries(T)) - to_chat(src, "Вы не можете расширяться сюда...") - return - - var/obj/structure/blob/B = locate() in T - if(B) - to_chat(src, "Здесь уже есть плитка!") - return - - var/obj/structure/blob/OB = locate() in circlerange(T, 1) - if(!OB) - to_chat(src, "Рядом с вами нет ни одной плитки.") - return - - if(!((locate(/mob/living) in T) || can_buy(5))) - return - last_attack = world.time - OB.expand(T, 0, blob_reagent_datum.color) - for(var/mob/living/L in T) - if(ROLE_BLOB in L.faction) //no friendly/dead fire - continue - var/mob_protection = L.get_permeability_protection() - blob_reagent_datum.reaction_mob(L, REAGENT_TOUCH, 25, 1, mob_protection) - blob_reagent_datum.send_message(L) - OB.color = blob_reagent_datum.color - return - - -/mob/camera/blob/verb/rally_spores_power() - set category = "Blob" - set name = "Rally Spores" - set desc = "Направьте споры, чтоб они переместились в выбранное место." - - var/turf/T = get_turf(src) - rally_spores(T) - -/mob/camera/blob/proc/rally_spores(var/turf/T) - to_chat(src, "Вы направляете свои споры.") - - var/list/surrounding_turfs = block(T.x - 1, T.y - 1, T.z, T.x + 1, T.y + 1, T.z) - if(!surrounding_turfs.len) - return - - for(var/mob/living/simple_animal/hostile/blob/blobspore/BS in GLOB.alive_mob_list) - if(isturf(BS.loc) && get_dist(BS, T) <= 35) - BS.LoseTarget() - BS.Goto(pick(surrounding_turfs), BS.move_to_delay) - return - -/mob/camera/blob/verb/split_consciousness() - set category = "Blob" - set name = "Split consciousness (100) (One use)" - set desc = "Тратьте ресурсы, чтобы попытаться создать еще одного блоба." - - var/turf/T = get_turf(src) - if(!T) - return - if(split_used) - to_chat(src, "Вы уже произвели потомка.") - return - if(is_offspring) - to_chat(src, "Потомки блоба не могут производить потомков.") - return - - var/obj/structure/blob/N = (locate(/obj/structure/blob) in T) - if(!N) - to_chat(src, "Для создания вашего потомка необходим узел.") - return - if(!istype(N, /obj/structure/blob/node)) - to_chat(src, "Для создания вашего потомка необходим узел.") - return - if(!can_buy(100)) - return - - split_used = TRUE - - new /obj/structure/blob/core/ (get_turf(N), 200, null, blob_core.point_rate, offspring = TRUE) - qdel(N) - - -/mob/camera/blob/verb/blob_broadcast() - set category = "Blob" - set name = "Blob Broadcast" - set desc = "Говорите, используя споры и блобернаутов в качестве рупоров. Это действие бесплатно." - - var/speak_text = clean_input("Что вы хотите сказать от лица ваших созданий?", "Blob Broadcast", null) - - if(!speak_text) - return - else - to_chat(usr, "Вы говорите от лица ваших созданий, [speak_text]") - for(var/mob/living/simple_animal/hostile/blob_minion in blob_mobs) - if(blob_minion.stat == CONSCIOUS) - blob_minion.say(speak_text) - return - -/mob/camera/blob/verb/create_storage() - set category = "Blob" - set name = "Create Storage Blob (40)" - set desc = "Создаёт хранилище, которая будет накапливать дополнительные ресурсы для вас. Это увеличивает ваш максимальный предел ресурсов на 50." - - - var/turf/T = get_turf(src) - - if(!T) - return - - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - - if(!B)//We are on a blob - to_chat(src, "Тут нет плитки блоба!") - return - - if(!istype(B, /obj/structure/blob/normal)) - to_chat(src, "Невозможно использовать эту плитку, найдите обычную.") - return - - for(var/obj/structure/blob/storage/blob in orange(3, T)) - to_chat(src, "Поблизости находится хранилище, отойдите на расстояние более 4 плиток от него!") - return - - if(!can_buy(40)) - return - - B.color = blob_reagent_datum.color - B.change_to(/obj/structure/blob/storage) - var/obj/structure/blob/storage/R = locate() in T - if(R) - R.overmind = src - R.update_max_blob_points(50) - - return - - -/mob/camera/blob/verb/chemical_reroll() - set category = "Blob" - set name = "Reactive Chemical Adaptation (50)" - set desc = "Заменяет ваш химикат на другой случайным образом." - - if(!can_buy(50)) - return - - var/datum/reagent/blob/B = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type)) - blob_reagent_datum = new B - var/datum/antagonist/blob_overmind/overmind_datum = mind.has_antag_datum(/datum/antagonist/blob_overmind) - if(overmind_datum) - overmind_datum.reagent = blob_reagent_datum - color = blob_reagent_datum.complementary_color - - for(var/obj/structure/blob/BL in GLOB.blobs) - BL.adjustcolors(blob_reagent_datum.color) - - for(var/mob/living/simple_animal/hostile/blob/BLO) - BLO.adjustcolors(blob_reagent_datum.complementary_color) - - to_chat(src, "Ваш новый реагент: [blob_reagent_datum.name] - [blob_reagent_datum.description]") - -/mob/camera/blob/verb/blob_help() - set category = "Blob" - set name = "*Blob Help*" - set desc = "Help on how to blob." - for (var/message in get_blob_help_messages(blob_reagent_datum)) - to_chat(src, message) - - diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm deleted file mode 100644 index 349298f8b1f..00000000000 --- a/code/game/gamemodes/blob/theblob.dm +++ /dev/null @@ -1,300 +0,0 @@ -//I will need to recode parts of this but I am way too tired atm -/obj/structure/blob - name = "blob" - icon = 'icons/mob/blob.dmi' - light_range = 3 - desc = "Some blob creature thingy" - density = FALSE - opacity = TRUE - anchored = TRUE - pass_flags_self = PASSBLOB - can_astar_pass = CANASTARPASS_ALWAYS_PROC - max_integrity = 30 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. - var/health_timestamp = 0 - var/brute_resist = 0.5 //multiplies brute damage by this - var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = FALSE //if the blob blocks atmos and heat spread - /// If a threshold is reached, resulting in shifting variables - var/compromised_integrity = FALSE - var/mob/camera/blob/overmind - creates_cover = TRUE - obj_flags = BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP // stops blob mobs from falling on multiz. - - -/obj/structure/blob/Initialize(mapload) - . = ..() - GLOB.blobs += src - setDir(pick(GLOB.cardinal)) - check_integrity() - if(atmosblock) - air_update_turf(1) - ConsumeTile() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) - - -/obj/structure/blob/Destroy() - if(atmosblock) - atmosblock = FALSE - air_update_turf(1) - GLOB.blobs -= src - if(isturf(loc)) //Necessary because Expand() is screwed up and spawns a blob and then deletes it - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - return ..() - -/obj/structure/blob/has_prints() - return FALSE - -/obj/structure/blob/BlockSuperconductivity() - return atmosblock - -/obj/structure/blob/proc/check_integrity() - return - -/obj/structure/blob/proc/update_state() - return - -/obj/structure/blob/CanAllowThrough(atom/movable/mover, border_dir) - . = ..() - return checkpass(mover, PASSBLOB) - -/obj/structure/blob/CanAtmosPass(turf/T, vertical) - return !atmosblock - - -/obj/structure/blob/CanAStarPass(to_dir, datum/can_pass_info/pass_info) - return pass_info.pass_flags == PASSEVERYTHING || (pass_info.pass_flags & PASSBLOB) - - -/obj/structure/blob/process() - Life() - return - -/obj/structure/blob/blob_act(obj/structure/blob/B) - return - -/obj/structure/blob/proc/Life() - return - -/obj/structure/blob/proc/RegenHealth() - // All blobs heal over time when pulsed, but it has a cool down - if(health_timestamp > world.time) - return 0 - if(obj_integrity < max_integrity) - obj_integrity = min(max_integrity, obj_integrity + 1) - check_integrity() - health_timestamp = world.time + 10 // 1 seconds - - -/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand - RegenHealth() - - if(run_action())//If we can do something here then we dont need to pulse more - return - - if(pulse > 30) - return//Inf loop check - - //Looking for another blob to pulse - var/list/dirs = list(1,2,4,8) - dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us - for(var/i = 1 to 4) - if(!dirs.len) break - var/dirn = pick(dirs) - dirs.Remove(dirn) - var/turf/T = get_step(src, dirn) - if(!is_location_within_transition_boundaries(T)) - continue - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - if(!B) - expand(T,1,a_color)//No blob here so try and expand - return - B.adjustcolors(a_color) - - B.Pulse((pulse+1),get_dir(src.loc,T), a_color) - return - return - - -/obj/structure/blob/proc/run_action() - return 0 - -/obj/structure/blob/proc/ConsumeTile() - for(var/atom/A in loc) - A.blob_act(src) - if(iswallturf(loc)) - loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it - -/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) - if(prob && !prob(obj_integrity)) - return - if(isspaceturf(T) && prob(75)) return - if(!T) - var/list/dirs = list(1,2,4,8) - for(var/i = 1 to 4) - var/dirn = pick(dirs) - dirs.Remove(dirn) - T = get_step(src, dirn) - if(!(locate(/obj/structure/blob) in T)) break - else T = null - - if(!T) return 0 - if(!is_location_within_transition_boundaries(T)) - return - var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) - B.color = a_color - B.set_density(TRUE) - if(T.Enter(B,src))//Attempt to move into the tile - B.set_density(initial(B.density)) - B.loc = T - else - T.blob_act()//If we cant move in hit the turf - B.loc = null //So we don't play the splat sound, see Destroy() - qdel(B) - - for(var/atom/A in T)//Hit everything in the turf - A.blob_act(src) - return 1 - - -/obj/structure/blob/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) - SIGNAL_HANDLER - - arrived.blob_act(src) - - -/obj/structure/blob/tesla_act(power) - ..() - take_damage(power / 400, BURN, "energy") - - -/obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut - return - ..() - -/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - switch(damage_type) - if(BRUTE) - damage_amount *= brute_resist - if(BURN) - damage_amount *= fire_resist - if(CLONE) - else - return 0 - var/armor_protection = 0 - if(damage_flag) - armor_protection = armor.getRating(damage_flag) - damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) - if(overmind?.blob_reagent_datum && damage_flag) - damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) - return damage_amount - -/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - if(QDELETED(src)) - return - . = ..() - if(. && obj_integrity > 0) - check_integrity() - -/obj/structure/blob/proc/change_to(var/type) - if(!ispath(type)) - error("[type] is an invalid type for the blob.") - var/obj/structure/blob/B = new type(src.loc) - if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) - B.color = color - else - B.adjustcolors(color) - qdel(src) - -/obj/structure/blob/proc/adjustcolors(var/a_color) - if(a_color) - color = a_color - - -/obj/structure/blob/examine(mob/user) - . = ..() - . += "It looks like it's made of [get_chem_name()]." - . += "It looks like this chemical does: [get_chem_desc()]." - -/obj/structure/blob/proc/get_chem_name() - for(var/mob/camera/blob/B in GLOB.mob_list) - if(!QDELETED(B) && lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these - return B.blob_reagent_datum.name - return "unknown" - -/obj/structure/blob/proc/get_chem_desc() - for(var/mob/camera/blob/B in GLOB.mob_list) - if(!QDELETED(B) && lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these - return B.blob_reagent_datum.description - return "something unknown" - - -/obj/structure/blob/hit_by_thrown_carbon(mob/living/carbon/human/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt) - damage *= 0.25 // Lets not have sorium be too much of a blender / rapidly kill itself - return ..() - - -/obj/structure/blob/normal - icon_state = "blob" - light_range = 0 - obj_integrity = 21 //doesn't start at full health - max_integrity = 25 - brute_resist = 0.25 - - -/obj/structure/blob/normal/check_integrity() - var/old_compromised_integrity = compromised_integrity - if(obj_integrity <= 15) - compromised_integrity = TRUE - else - compromised_integrity = FALSE - if(old_compromised_integrity != compromised_integrity) - update_state() - update_appearance(UPDATE_NAME|UPDATE_DESC|UPDATE_ICON_STATE) - - -/obj/structure/blob/normal/update_state() - if(compromised_integrity) - brute_resist = 0.5 - else - brute_resist = 0.25 - - -/obj/structure/blob/normal/update_name(updates = ALL) - . = ..() - if(compromised_integrity) - name = "fragile blob" - else - name = "[overmind ? "blob" : "dead blob"]" - - -/obj/structure/blob/normal/update_desc(updates = ALL) - . = ..() - if(compromised_integrity) - desc = "A thin lattice of slightly twitching tendrils." - else - desc = "A thick wall of [overmind ? "writhing" : "lifeless"] tendrils." - - -/obj/structure/blob/normal/update_icon_state() - if(compromised_integrity) - icon_state = "blob_damaged" - else - icon_state = "blob" - - diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index afd5f851ad2..0eb36bde1d9 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -26,7 +26,7 @@ to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") -/datum/game_mode/changeling/pre_setup() +/datum/game_mode/changeling/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/changeling/thief_chan.dm b/code/game/gamemodes/changeling/thief_chan.dm index f6e1ffef9d4..fc94e3562ae 100644 --- a/code/game/gamemodes/changeling/thief_chan.dm +++ b/code/game/gamemodes/changeling/thief_chan.dm @@ -13,7 +13,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров и генокрадов. Не дайте генокрадам достичь успеха и скрыться, и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/thief/changeling/pre_setup() +/datum/game_mode/thief/changeling/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index dd660e42eaa..3a67065cf61 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -14,7 +14,7 @@ to_chat(world, "There is an alien creature on the station along with some syndicate operatives out for their own gain! Do not let the changeling and the traitors succeed!") -/datum/game_mode/traitor/changeling/pre_setup() +/datum/game_mode/traitor/changeling/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/clockwork/clockwork.dm b/code/game/gamemodes/clockwork/clockwork.dm index b3e52e47ca1..338d4fd0c3f 100644 --- a/code/game/gamemodes/clockwork/clockwork.dm +++ b/code/game/gamemodes/clockwork/clockwork.dm @@ -64,7 +64,7 @@ GLOBAL_LIST_EMPTY(all_clockers) to_chat(world, "The current game mode is - Clockwork Cult!") to_chat(world, "Some crewmembers are attempting to start a clockwork cult!
\nClockers - complete your objectives. Convert crewmembers to your cause by using the credence structure. Remember - there is no you, there is only the cult.
\nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with holy water reverts them to whatever CentComm-allowed faith they had.
") -/datum/game_mode/clockwork/pre_setup() +/datum/game_mode/clockwork/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/clockwork/clockwork_items.dm b/code/game/gamemodes/clockwork/clockwork_items.dm index d4f8b5db9f3..219a7a04666 100644 --- a/code/game/gamemodes/clockwork/clockwork_items.dm +++ b/code/game/gamemodes/clockwork/clockwork_items.dm @@ -557,7 +557,7 @@ if(bodypart.internal_bleeding()) to_chat(user, span_warning("You tear through [human]'s skin releasing the blood from [human.p_their()] [bodypart.name]!")) playsound(get_turf(human), 'sound/effects/pierce.ogg', 30, TRUE) - human.blood_volume = max(human.blood_volume - 100, 0) + human.setBlood(max(human.blood_volume - 100, 0)) var/splatter_dir = get_dir(user, human) blood_color = human.dna.species.blood_color new /obj/effect/temp_visual/dir_setting/bloodsplatter(human.drop_location(), splatter_dir, blood_color) @@ -716,7 +716,7 @@ animate(carbon, alpha = 20, time = 1 SECONDS) ADD_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(INVIS_SPELL)) sleep(10) - carbon.alpha = 20 + carbon.alpha_set(standartize_alpha(20), ALPHA_SOURCE_CLOCKROBE) add_attack_logs(user, user, "cloaked [src]", ATKLOG_ALL) addtimer(CALLBACK(src, PROC_REF(uncloak), carbon), 10 SECONDS) if(enchant_type == SPEED_SPELL) @@ -729,11 +729,11 @@ else ToggleHood() -/obj/item/clothing/suit/hooded/clockrobe/proc/uncloak(mob/user) +/obj/item/clothing/suit/hooded/clockrobe/proc/uncloak(mob/living/user) animate(user, alpha = 255, time = 1 SECONDS) REMOVE_TRAIT(src, TRAIT_NODROP, CURSED_ITEM_TRAIT(INVIS_SPELL)) sleep(10) - user.alpha = 255 + user.alpha_set(1, ALPHA_SOURCE_CLOCKROBE) deplete_spell() /obj/item/clothing/suit/hooded/clockrobe/proc/unspeed(mob/living/carbon/carbon) diff --git a/code/game/gamemodes/clockwork/clockwork_structures.dm b/code/game/gamemodes/clockwork/clockwork_structures.dm index e65baa26fee..feab56be944 100644 --- a/code/game/gamemodes/clockwork/clockwork_structures.dm +++ b/code/game/gamemodes/clockwork/clockwork_structures.dm @@ -177,7 +177,7 @@ M.adjustHealth(-8) if(ishuman(L) && !HAS_TRAIT(L, TRAIT_NO_BLOOD_RESTORE) && L.blood_volume < BLOOD_VOLUME_NORMAL) - L.blood_volume += 1 + L.AdjustBlood(1) /obj/structure/clockwork/functional/beacon/Destroy() diff --git a/code/game/gamemodes/clockwork/clockwork_workshop.dm b/code/game/gamemodes/clockwork/clockwork_workshop.dm index 4a7c05791bd..5c16a4bcac7 100644 --- a/code/game/gamemodes/clockwork/clockwork_workshop.dm +++ b/code/game/gamemodes/clockwork/clockwork_workshop.dm @@ -132,7 +132,8 @@ "brass" = design.brass_cost, "power" = design.power_cost, "requirements" = matreq, - "image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]" + "icon" = initial(I.icon), + "icon_state" = initial(I.icon_state), ) static_data["items"][cat] = cat_items diff --git a/code/game/gamemodes/cult/blood_magic.dm b/code/game/gamemodes/cult/blood_magic.dm index 51375cb0f5a..5d9a07f5ee3 100644 --- a/code/game/gamemodes/cult/blood_magic.dm +++ b/code/game/gamemodes/cult/blood_magic.dm @@ -99,10 +99,12 @@ /datum/action/innate/cult/blood_spell/Grant(mob/living/owner, datum/action/innate/cult/blood_magic/BM) if(health_cost) desc += "
Deals [health_cost] damage to your arm per use." + base_desc = desc desc += "
Has [charges] use\s remaining." all_magic = BM button.ordered = FALSE + ..() /datum/action/innate/cult/blood_spell/override_location() @@ -723,7 +725,7 @@ if(H.blood_volume < BLOOD_VOLUME_SAFE) var/restore_blood = BLOOD_VOLUME_SAFE - H.blood_volume if(uses * 2 < restore_blood) - H.blood_volume += uses * 2 + H.AdjustBlood(uses * 2) to_chat(user, "You use the last of your charges to restore what blood you could, and the spell dissipates!") uses = 0 return ..() diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index a6254bc7d85..5ebd607a53c 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(all_cults) to_chat(world, "The current game mode is - Cult!") to_chat(world, "Some crewmembers are attempting to start a cult!
\nCultists - complete your objectives. Convert crewmembers to your cause by using the offer rune. Remember - there is no you, there is only the cult.
\nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with holy water reverts them to whatever CentComm-allowed faith they had.
") -/datum/game_mode/cult/pre_setup() +/datum/game_mode/cult/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/devil/devil.dm b/code/game/gamemodes/devil/devil.dm deleted file mode 100644 index d6927753ed9..00000000000 --- a/code/game/gamemodes/devil/devil.dm +++ /dev/null @@ -1,48 +0,0 @@ -GLOBAL_LIST_INIT(whiteness, list( - /obj/item/clothing/under/color/white = 2, - /obj/item/clothing/under/rank/bartender = 1, - /obj/item/clothing/under/rank/chef = 1, - /obj/item/clothing/under/rank/chief_engineer = 1, - /obj/item/clothing/under/rank/scientist = 1, - /obj/item/clothing/under/rank/chemist = 1, - /obj/item/clothing/under/rank/chief_medical_officer = 1, - /obj/item/clothing/under/rank/geneticist = 1, - /obj/item/clothing/under/rank/virologist = 1, - /obj/item/clothing/under/rank/nursesuit = 1, - /obj/item/clothing/under/rank/medical = 1, - /obj/item/clothing/under/rank/psych = 1, - /obj/item/clothing/under/rank/orderly = 1, - /obj/item/clothing/under/rank/security/brigphys = 1, - /obj/item/clothing/under/rank/internalaffairs = 1, - /obj/item/clothing/under/rank/ntrep = 1, - /obj/item/clothing/under/det = 1, - /obj/item/clothing/under/wedding/bride_white = 1, - /obj/item/clothing/under/mafia/white = 1, - /obj/item/clothing/under/noble_clothes = 1, - /obj/item/clothing/under/sl_suit = 1, - /obj/item/clothing/under/burial = 1 -)) - - - -/mob/living/proc/check_devil_bane_multiplier(obj/item/weapon, mob/living/attacker) - switch(mind.devilinfo.bane) - if(BANE_WHITECLOTHES) - if(ishuman(attacker)) - var/mob/living/carbon/human/H = attacker - if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - if(GLOB.whiteness[U.type]) - src.visible_message("[src] seems to have been harmed by the purity of [attacker]'s clothes.", "Unsullied white clothing is disrupting your form.") - return GLOB.whiteness[U.type] + 1 - if(BANE_TOOLBOX) - if(istype(weapon,/obj/item/storage/toolbox)) - src.visible_message("The [weapon] seems unusually robust this time.", "The [weapon] is your unmaking!") - return 2.5 // Will take four hits with a normal toolbox. - if(BANE_HARVEST) - if(istype(weapon,/obj/item/reagent_containers/food/snacks/grown/) || istype(weapon,/obj/item/grown)) - src.visible_message("The spirits of the harvest aid in the exorcism.", "The harvest spirits are harming you.") - src.Weaken(4 SECONDS) - qdel(weapon) - return 2 - return 1 diff --git a/code/game/gamemodes/devil/devil_agent/devil_agent.dm b/code/game/gamemodes/devil/devil_agent/devil_agent.dm deleted file mode 100644 index e9a1d998a7a..00000000000 --- a/code/game/gamemodes/devil/devil_agent/devil_agent.dm +++ /dev/null @@ -1,38 +0,0 @@ -/datum/game_mode/devil/devil_agents - name = "Devil Agents" - config_tag = "devilagents" - required_players = 25 - required_enemies = 3 - recommended_enemies = 8 - - traitors_possible = 10 //hard limit on traitors if scaling is turned off - num_modifier = 4 - objective_count = 2 - - var/list/target_list = list() - var/list/late_joining_list = list() - minimum_devils = 3 - -/datum/game_mode/devil/devil_agents/post_setup() - var/i = 0 - for(var/datum/mind/devil in devils) - i++ - if(i + 1 > devils.len) - i = 0 - target_list[devil] = devils[i + 1] - ..() - -/datum/game_mode/devil/devil_agents/forge_devil_objectives(datum/mind/devil_mind, quantity) - ..(devil_mind, quantity - give_outsell_objective(devil_mind)) - -/datum/game_mode/devil/devil_agents/proc/give_outsell_objective(datum/mind/devil) - //If you override this method, have it return the number of objectives added. - if(target_list.len && target_list[devil]) // Is a double agent - var/datum/mind/target_mind = target_list[devil] - var/datum/objective/devil/outsell/outsellobjective = new - outsellobjective.owner = devil - outsellobjective.target = target_mind - outsellobjective.update_explanation_text() - devil.objectives += outsellobjective - return 1 - return 0 diff --git a/code/game/gamemodes/devil/devil_game_mode.dm b/code/game/gamemodes/devil/devil_game_mode.dm deleted file mode 100644 index 563bac3e3e0..00000000000 --- a/code/game/gamemodes/devil/devil_game_mode.dm +++ /dev/null @@ -1,66 +0,0 @@ -/datum/game_mode/devil - name = "devil" - config_tag = "devil" - protected_jobs = list(JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, - JOB_TITLE_LAWYER, JOB_TITLE_LIBRARIAN, JOB_TITLE_CHAPLAIN, JOB_TITLE_HOS, JOB_TITLE_CAPTAIN, JOB_TITLE_BRIGDOC, - JOB_TITLE_CCOFFICER, JOB_TITLE_CCSPECOPS, JOB_TITLE_AI, JOB_TITLE_CYBORG, JOB_TITLE_CCFIELD, JOB_TITLE_CCSUPREME) - required_players = 2 - required_enemies = 1 - recommended_enemies = 4 - - var/traitors_possible = 4 //hard limit on devils if scaling is turned off - var/num_modifier = 0 // Used for gamemodes, that are a child of traitor, that need more than the usual. - var/objective_count = 2 - var/minimum_devils = 1 -// var/devil_scale_coefficient = 10 - -/datum/game_mode/devil/announce() - to_chat(world, {"The current game mode is - Devil!
) - Devils: Purchase souls and tempt the crew to sin!
- Crew: Resist the lure of sin and remain pure!"}) - -/datum/game_mode/devil/pre_setup() - - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - restricted_jobs += protected_jobs - - var/list/possible_devils = get_players_for_role(ROLE_DEVIL) - var/num_devils = 0 - if(!possible_devils.len) - return 0 - if(CONFIG_GET(number/traitor_scaling)) - num_devils = max(1, round((num_players())/(CONFIG_GET(number/traitor_scaling)))+1) - else - num_devils = max(1, min(num_players(), traitors_possible)) - add_game_logs("Number of devils chosen: [num_devils]") - - for(var/j = 0, j < num_devils, j++) - if (!possible_devils.len) - break - var/datum/mind/devil = pick(possible_devils) - devils += devil - devil.special_role = ROLE_DEVIL - devil.restricted_roles = restricted_jobs - - add_game_logs("[devil.key] has been selected as a [config_tag]", devil) - possible_devils.Remove(devil) - - if(devils.len < required_enemies) - return 0 - return 1 - - -/datum/game_mode/devil/post_setup() - for(var/datum/mind/devil in devils) - spawn(rand(10, 100)) - finalize_devil(devil, TRUE) - spawn(100) - forge_devil_objectives(devil, objective_count) //This has to be in a separate loop, as we need devil names to be generated before we give objectives in devil agent. - devil.devilinfo.announce_laws(devil.current) - var/obj_count = 1 - to_chat(devil.current, " Your current objectives:") - for(var/datum/objective/objective in devil.objectives) - to_chat(devil.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - ..() - return 1 diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm deleted file mode 100644 index 5c00d4b57a9..00000000000 --- a/code/game/gamemodes/devil/devilinfo.dm +++ /dev/null @@ -1,560 +0,0 @@ -#define BLOOD_THRESHOLD 3 //How many souls are needed per stage. -#define TRUE_THRESHOLD 7 -#define ARCH_THRESHOLD 12 - -#define BASIC_DEVIL 0 -#define BLOOD_LIZARD 1 -#define TRUE_DEVIL 2 -#define ARCH_DEVIL 3 - -#define LOSS_PER_DEATH 2 - -#define SOULVALUE (soulsOwned.len-reviveNumber) - -#define DEVILRESURRECTTIME 600 - -GLOBAL_LIST_EMPTY(allDevils) -GLOBAL_LIST_INIT(lawlorify, list ( - LORE = list( - OBLIGATION_FOOD = "This devil seems to always offer it's victims food before slaughtering them.", - OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.", - OBLIGATION_DANCEOFF = "This devil will never turn down a dance off.", - OBLIGATION_GREET = "This devil seems to only be able to converse with people it knows the name of.", - OBLIGATION_PRESENCEKNOWN = "This devil seems to be unable to attack from stealth.", - OBLIGATION_SAYNAME = "He will always chant his name upon killing someone.", - OBLIGATION_ANNOUNCEKILL = "This devil always loudly announces his kills for the world to hear.", - OBLIGATION_ANSWERTONAME = "This devil always responds to his truename.", - BANE_SILVER = "Silver seems to gravely injure this devil.", - BANE_SALT = "Throwing salt at this devil will hinder his ability to use infernal powers temporarily.", - BANE_LIGHT = "Bright flashes will disorient the devil, likely causing him to flee.", - BANE_IRON = "Cold iron will slowly injure him, until he can purge it from his system.", - BANE_WHITECLOTHES = "Wearing clean white clothing will help ward off this devil.", - BANE_HARVEST = "Presenting the labors of a harvest will disrupt the devil.", - BANE_TOOLBOX = "That which holds the means of creation also holds the means of the devil's undoing.", - BAN_HURTWOMAN = "This devil seems to prefer hunting men.", - BAN_CHAPEL = "This devil avoids holy ground.", - BAN_HURTPRIEST = "The anointed clergy appear to be immune to his powers.", - BAN_AVOIDWATER = "The devil seems to have some sort of aversion to water, though it does not appear to harm him.", - BAN_STRIKEUNCONCIOUS = "This devil only shows interest in those who are awake.", - BAN_HURTLIZARD = "This devil will not strike an Unathi first.", - BAN_HURTANIMAL = "This devil avoids hurting animals.", - BANISH_WATER = "To banish the devil, you must infuse it's body with holy water.", - BANISH_COFFIN = "This devil will return to life if it's remains are not placed within a coffin.", - BANISH_FORMALDYHIDE = "To banish the devil, you must inject it's lifeless body with embalming fluid.", - BANISH_RUNES = "This devil will resurrect after death, unless it's remains are within a rune.", - BANISH_CANDLES = "A large number of nearby lit candles will prevent it from resurrecting.", - BANISH_DESTRUCTION = "It's corpse must be utterly destroyed to prevent resurrection.", - BANISH_FUNERAL_GARB = "If clad in funeral garments, this devil will be unable to resurrect. Should the clothes not fit, lay them gently on top of the devil's corpse." - ), - LAW = list( - OBLIGATION_FOOD = "When not acting in self defense, you must always offer your victim food before harming them.", - OBLIGATION_FIDDLE = "When not in immediate danger, if you are challenged to a musical duel, you must accept it. You are not obligated to duel the same person twice.", - OBLIGATION_DANCEOFF = "When not in immediate danger, if you are challenged to a dance off, you must accept it. You are not obligated to face off with the same person twice.", - OBLIGATION_GREET = "You must always greet other people by their last name before talking with them.", - OBLIGATION_PRESENCEKNOWN = "You must always make your presence known before attacking.", - OBLIGATION_SAYNAME = "You must always say your true name after you kill someone.", - OBLIGATION_ANNOUNCEKILL = "Upon killing someone, you must make your deed known to all within earshot, over comms if reasonably possible.", - OBLIGATION_ANSWERTONAME = "If you are not under attack, you must always respond to your true name.", - BAN_HURTWOMAN = "You must never harm a female outside of self defense.", - BAN_CHAPEL = "You must never attempt to enter the chapel.", - BAN_HURTPRIEST = "You must never attack a priest.", - BAN_AVOIDWATER = "You must never willingly touch a wet surface.", - BAN_STRIKEUNCONCIOUS = "You must never strike an unconscious person.", - BAN_HURTLIZARD = "You must never harm an Unathi outside of self defense.", - BAN_HURTANIMAL = "You must never harm a non-sentient creature or robot outside of self defense.", - BANE_SILVER = "Silver, in all of it's forms shall be your downfall.", - BANE_SALT = "Salt will disrupt your magical abilities.", - BANE_LIGHT = "Blinding lights will prevent you from using offensive powers for a time.", - BANE_IRON = "Cold wrought iron shall act as poison to you.", - BANE_WHITECLOTHES = "Those clad in pristine white garments will strike you true.", - BANE_HARVEST = "The fruits of the harvest shall be your downfall.", - BANE_TOOLBOX = "Toolboxes are bad news for you, for some reason.", - BANISH_WATER = "If your corpse is filled with holy water, you will be unable to resurrect.", - BANISH_COFFIN = "If your corpse is in a coffin, you will be unable to resurrect.", - BANISH_FORMALDYHIDE = "If your corpse is embalmed, you will be unable to resurrect.", - BANISH_RUNES = "If your corpse is placed within a rune, you will be unable to resurrect.", - BANISH_CANDLES = "If your corpse is near lit candles, you will be unable to resurrect.", - BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.", - BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect." - ) - )) - -/datum/devilinfo - var/datum/mind/owner = null - var/obligation - var/ban - var/bane - var/banish - var/truename - var/list/datum/mind/soulsOwned = new - var/datum/dna/humanform = null - var/reviveNumber = 0 - var/form = BASIC_DEVIL - var/exists = 0 - var/static/list/dont_remove_spells = list( - /obj/effect/proc_holder/spell/summon_contract, - /obj/effect/proc_holder/spell/conjure_item/violin, - /obj/effect/proc_holder/spell/summon_dancefloor) - var/ascendable = FALSE - -/datum/devilinfo/New() - ..() - dont_remove_spells = typecacheof(dont_remove_spells) - -/proc/randomDevilInfo(name = randomDevilName()) - var/datum/devilinfo/devil = new - devil.truename = name - devil.bane = randomdevilbane() - devil.obligation = randomdevilobligation() - devil.ban = randomdevilban() - devil.banish = randomdevilbanish() - return devil - -/proc/devilInfo(name, saveDetails = 0) - if(GLOB.allDevils[lowertext(name)]) - return GLOB.allDevils[lowertext(name)] - else - var/datum/devilinfo/devil = randomDevilInfo(name) - GLOB.allDevils[lowertext(name)] = devil - devil.exists = saveDetails - return devil - - - -/proc/randomDevilName() - var/preTitle = "" - var/title = "" - var/mainName = "" - var/suffix = "" - if(prob(65)) - if(prob(35)) - preTitle = pick("Dark ", "Hellish ", "Fiery ", "Sinful ", "Blood ") - title = pick("Lord ", "Fallen Prelate ", "Count ", "Viscount ", "Vizier ", "Elder ", "Adept ") - var/probability = 100 - mainName = pick("Hal", "Ve", "Odr", "Neit", "Ci", "Quon", "Mya", "Folth", "Wren", "Gyer", "Geyr", "Hil", "Niet", "Twou", "Hu", "Don") - while(prob(probability)) - mainName += pick("hal", "ve", "odr", "neit", "ca", "quon", "mya", "folth", "wren", "gyer", "geyr", "hil", "niet", "twoe", "phi", "coa") - probability -= 20 - if(prob(40)) - suffix = pick(" the Red", " the Soulless", " the Master", ", the Lord of all things", ", Jr.") - return preTitle + title + mainName + suffix - -/proc/randomdevilobligation() - return pick(OBLIGATION_FOOD, OBLIGATION_FIDDLE, OBLIGATION_DANCEOFF, OBLIGATION_GREET, OBLIGATION_PRESENCEKNOWN, OBLIGATION_SAYNAME, OBLIGATION_ANNOUNCEKILL, OBLIGATION_ANSWERTONAME) - -/proc/randomdevilban() - return pick(BAN_HURTWOMAN, BAN_CHAPEL, BAN_HURTPRIEST, BAN_AVOIDWATER, BAN_STRIKEUNCONCIOUS, BAN_HURTLIZARD, BAN_HURTANIMAL) - -/proc/randomdevilbane() - return pick(BANE_SALT, BANE_LIGHT, BANE_IRON, BANE_WHITECLOTHES, BANE_SILVER, BANE_HARVEST, BANE_TOOLBOX) - -/proc/randomdevilbanish() - return pick(BANISH_WATER, BANISH_COFFIN, BANISH_FORMALDYHIDE, BANISH_RUNES, BANISH_CANDLES, BANISH_DESTRUCTION, BANISH_FUNERAL_GARB) - -/datum/devilinfo/proc/link_with_mob(mob/living/L) - if(ishuman(L)) - var/mob/living/carbon/human/H = L - humanform = H.dna.Clone() - owner = L.mind - give_base_spells(1) - -/datum/devilinfo/proc/add_soul(datum/mind/soul) - if(soulsOwned.Find(soul)) - return - soulsOwned += soul - owner.current.set_nutrition(NUTRITION_LEVEL_FULL) - to_chat(owner.current, "You feel satiated as you received a new soul.") - update_hud() - switch(SOULVALUE) - if(0) - to_chat(owner.current, "Your hellish powers have been restored.") - give_base_spells() - if(BLOOD_THRESHOLD) - to_chat(owner.current, "You feel as though your humanoid form is about to shed. You will soon turn into a blood lizard.") - sleep(50) - increase_blood_lizard() - if(TRUE_THRESHOLD) - to_chat(owner.current, "You feel as though your current form is about to shed. You will soon turn into a true devil.") - sleep(50) - increase_true_devil() - if(ARCH_THRESHOLD) - arch_devil_prelude() - increase_arch_devil() - -/datum/devilinfo/proc/remove_soul(datum/mind/soul) - if(soulsOwned.Remove(soul)) - to_chat(owner.current, "You feel as though a soul has slipped from your grasp.") - check_regression() - update_hud() - -/datum/devilinfo/proc/check_regression() - if(form == ARCH_DEVIL) - return //arch devil can't regress - //Yes, fallthrough behavior is intended, so I can't use a switch statement. - if(form == TRUE_DEVIL && SOULVALUE < TRUE_THRESHOLD) - regress_blood_lizard() - if(form == BLOOD_LIZARD && SOULVALUE < BLOOD_THRESHOLD) - regress_humanoid() - if(SOULVALUE < 0) - remove_spells() - to_chat(owner.current, "As punishment for your failures, all of your powers except contract creation have been revoked.") - -/datum/devilinfo/proc/regress_humanoid() - to_chat(owner.current, "Your powers weaken, have more contracts be signed to regain power.") - if(ishuman(owner.current)) - var/mob/living/carbon/human/H = owner.current - if(humanform) - H.set_species(humanform.species) - H.dna = humanform.Clone() - H.sync_organ_dna(assimilate = 0) - else - H.set_species(/datum/species/human) - // TODO: Add some appearance randomization here or something - humanform = H.dna.Clone() - H.regenerate_icons() - else - owner.current.color = "" - give_base_spells() - if(istype(owner.current.loc, /obj/effect/dummy/slaughter)) - owner.current.forceMove(get_turf(owner.current))//Fixes dying while jaunted leaving you permajaunted. - form = BASIC_DEVIL - -/datum/devilinfo/proc/regress_blood_lizard() - var/mob/living/carbon/true_devil/devil = owner.current - to_chat(devil, span_warning("Your powers weaken, have more contracts be signed to regain power.")) - devil.oldform.loc = devil.loc - owner.transfer_to(devil.oldform) - REMOVE_TRAIT(devil.oldform, TRAIT_GODMODE, UNIQUE_TRAIT_SOURCE(src)) - give_lizard_spells() - qdel(devil) - form = BLOOD_LIZARD - update_hud() - - -/datum/devilinfo/proc/increase_blood_lizard() - if(ishuman(owner.current)) - var/mob/living/carbon/human/H = owner.current - var/list/language_temp = LAZYLEN(H.languages) ? H.languages.Copy() : null - H.set_species(/datum/species/unathi) - if(language_temp) - H.languages = language_temp - H.underwear = "Nude" - H.undershirt = "Nude" - H.socks = "Nude" - H.change_skin_color(80, 16, 16) //A deep red - H.regenerate_icons() - else //Did the devil get hit by a staff of transmutation? - owner.current.color = "#501010" - give_lizard_spells() - form = BLOOD_LIZARD - - - -/datum/devilinfo/proc/increase_true_devil() - var/mob/living/carbon/true_devil/ascended = new /mob/living/carbon/true_devil(owner.current.loc, owner.current) - ascended.faction |= "hell" - // Put the old body in stasis - ADD_TRAIT(owner.current, TRAIT_GODMODE, UNIQUE_TRAIT_SOURCE(src)) - owner.current.loc = ascended - ascended.oldform = owner.current - owner.transfer_to(ascended) - ascended.set_name() - give_true_spells() - form = TRUE_DEVIL - update_hud() - -/datum/devilinfo/proc/arch_devil_prelude() - if(!ascendable) - return - var/mob/living/carbon/true_devil/D = owner.current - to_chat(D, "You feel as though your form is about to ascend.") - sleep(50) - if(!D) - return - D.visible_message("[D]'s skin begins to erupt with spikes.", \ - "Your flesh begins creating a shield around yourself.") - sleep(100) - if(!D) - return - D.visible_message("The horns on [D]'s head slowly grow and elongate.", \ - "Your body continues to mutate. Your telepathic abilities grow.") - sleep(90) - if(!D) - return - D.visible_message("[D]'s body begins to violently stretch and contort.", \ - "You begin to rend apart the final barriers to ultimate power.") - sleep(40) - if(!D) - return - to_chat(D, "Yes!") - sleep(10) - if(!D) - return - to_chat(D, "YES!!") - sleep(10) - if(!D) - return - to_chat(D, "YE--") - sleep(1) - if(!D) - return - to_chat(world, "SLOTH, WRATH, GLUTTONY, ACEDIA, ENVY, GREED, PRIDE! FIRES OF HELL AWAKEN!!") - world << 'sound/hallucinations/veryfar_noise.ogg' - sleep(50) - if(!SSticker.mode.devil_ascended) - SSshuttle.emergency.request(null, 0.3) - SSticker.mode.devil_ascended++ - -/datum/devilinfo/proc/increase_arch_devil() - if(!ascendable) - return - var/mob/living/carbon/true_devil/D = owner.current - if(!istype(D)) - return - give_arch_spells() - D.convert_to_archdevil() - if(istype(D.loc, /obj/effect/dummy/slaughter)) - D.forceMove(get_turf(D)) - var/area/A = get_area(owner.current) - if(A) - notify_ghosts("An arch devil has ascended in [A.name]. Reach out to the devil to start climbing the infernal corporate ladder.", title = "Arch Devil Ascended", source = owner.current, action = NOTIFY_ATTACK) - form = ARCH_DEVIL - -/datum/devilinfo/proc/remove_spells() - for(var/obj/effect/proc_holder/spell/spell as anything in owner.spell_list) - if(!is_type_in_typecache(spell, dont_remove_spells)) - owner.RemoveSpell(spell) - -/datum/devilinfo/proc/give_summon_contract() - owner.AddSpell(new /obj/effect/proc_holder/spell/summon_contract(null)) - - -/datum/devilinfo/proc/give_base_spells(give_summon_contract = 0) - remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork(null)) - if(give_summon_contract) - give_summon_contract() - if(obligation == OBLIGATION_FIDDLE) - owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/violin(null)) - if(obligation == OBLIGATION_DANCEOFF) - owner.AddSpell(new /obj/effect/proc_holder/spell/summon_dancefloor(null)) - -/datum/devilinfo/proc/give_lizard_spells() - remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/infernal_jaunt(null)) - -/datum/devilinfo/proc/give_true_spells() - remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork/greater(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/fireball/hellish(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/infernal_jaunt(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/sintouch(null)) - -/datum/devilinfo/proc/give_arch_spells() - remove_spells() - owner.AddSpell(new /obj/effect/proc_holder/spell/conjure_item/pitchfork/ascended(null)) - owner.AddSpell(new /obj/effect/proc_holder/spell/sintouch/ascended(null)) - -/datum/devilinfo/proc/beginResurrectionCheck(mob/living/body) - if(owner.current != body) - body = owner.current - if(SOULVALUE > 0) - to_chat(owner.current, "Your body has been damaged to the point that you may no longer use it. At the cost of some of your power, you will return to life soon.") - addtimer(CALLBACK(src, "activateResurrection", body), DEVILRESURRECTTIME) - else - to_chat(owner.messageable_mob(), "Your hellish powers are too weak to resurrect yourself.") - -/datum/devilinfo/proc/activateResurrection(mob/living/body) - if(QDELETED(body) || body.stat == DEAD) - if(SOULVALUE > 0) - if(check_banishment(body)) - to_chat(owner.messageable_mob(), "Unfortunately, the mortals have finished a ritual that prevents your resurrection.") - return -1 - else - to_chat(owner.messageable_mob(), "WE LIVE AGAIN!") - return hellish_resurrection(body) - else - to_chat(owner.messageable_mob(), "Unfortunately, the power that stemmed from your contracts has been extinguished. You no longer have enough power to resurrect.") - return -1 - else - to_chat(owner.current, "You seem to have resurrected without your hellish powers.") - -/datum/devilinfo/proc/check_banishment(mob/living/body) - switch(banish) - if(BANISH_WATER) - if(!QDELETED(body) && iscarbon(body)) - var/mob/living/carbon/H = body - return H.reagents.has_reagent("holy water") - return 0 - if(BANISH_COFFIN) - return (!QDELETED(body) && istype(body.loc, /obj/structure/closet/coffin)) - if(BANISH_FORMALDYHIDE) - if(!QDELETED(body) && iscarbon(body)) - var/mob/living/carbon/H = body - return H.reagents.has_reagent("formaldehyde") - return 0 - if(BANISH_RUNES) - if(!QDELETED(body)) - for(var/obj/effect/decal/cleanable/crayon/R in range(0,body)) - if (R.name == "rune") - return 1 - return 0 - if(BANISH_CANDLES) - if(!QDELETED(body)) - var/count = 0 - for(var/obj/item/candle/C in range(1,body)) - count += C.lit - if(count>=4) - return 1 - return 0 - if(BANISH_DESTRUCTION) - if(!QDELETED(body)) - return 0 - return 1 - if(BANISH_FUNERAL_GARB) - if(!QDELETED(body) && iscarbon(body)) - var/mob/living/carbon/human/H = body - if(H.w_uniform && istype(H.w_uniform, /obj/item/clothing/under/burial)) - return 1 - return 0 - else - for(var/obj/item/clothing/under/burial/B in range(0,body)) - if(B.loc == get_turf(B)) //Make sure it's not in someone's inventory or something. - return 1 - return 0 - -/datum/devilinfo/proc/hellish_resurrection(mob/living/body) - message_admins("[owner.name] (true name is: [truename]) is resurrecting using hellish energy.") - if(SOULVALUE <= ARCH_THRESHOLD && ascendable) // once ascended, arch devils do not go down in power by any means. - reviveNumber += LOSS_PER_DEATH - update_hud() - if(!QDELETED(body)) - body.revive() - if(!body.client) - var/mob/dead/observer/O = owner.get_ghost() - O.reenter_corpse() - if(istype(body.loc, /obj/effect/dummy/slaughter)) - body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted. - if(istype(body, /mob/living/carbon/true_devil)) - var/mob/living/carbon/true_devil/D = body - if(D.oldform) - D.oldform.revive() // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body. - if(body.stat == DEAD) // Not sure why this would happen - create_new_body() - else if(GLOB.blobstart.len > 0) - // teleport the body so repeated beatdowns aren't an option) - body.forceMove(get_turf(pick(GLOB.blobstart))) - // give them the devil lawyer outfit in case they got stripped - if(ishuman(body)) - var/mob/living/carbon/human/H = body - H.equipOutfit(/datum/outfit/devil_lawyer) - else - create_new_body() - check_regression() - -/datum/devilinfo/proc/create_new_body() - if(GLOB.blobstart.len > 0) - var/turf/targetturf = get_turf(pick(GLOB.blobstart)) - var/mob/currentMob = owner.current - if(QDELETED(currentMob)) - currentMob = owner.get_ghost() - if(!currentMob) - message_admins("[owner.name]'s devil resurrection failed due to client logoff. Aborting.") - return -1 - if(currentMob.mind != owner) - message_admins("[owner.name]'s devil resurrection failed due to becoming a new mob. Aborting.") - return -1 - var/mob/living/carbon/human/H = new /mob/living/carbon/human(targetturf) - owner.transfer_to(H) - if(isobserver(currentMob)) - var/mob/dead/observer/O = currentMob - O.reenter_corpse() - if(humanform) - H.set_species(humanform.species) - H.dna = humanform.Clone() - - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it - H.UpdateAppearance() - else - // gibbed cyborg or similar - create a randomized "humanform" appearance - H.scramble_appearance() - humanform = H.dna.Clone() - - - H.equipOutfit(/datum/outfit/devil_lawyer) - give_base_spells(TRUE) - if(SOULVALUE >= BLOOD_THRESHOLD) - increase_blood_lizard() - if(SOULVALUE >= TRUE_THRESHOLD) //Yes, BOTH this and the above if statement are to run if soulpower is high enough. - increase_true_devil() - if(SOULVALUE >= ARCH_THRESHOLD && ascendable) - increase_arch_devil() - else - throw EXCEPTION("Unable to find a blobstart landmark for hellish resurrection") - -/datum/devilinfo/proc/update_hud() - if(iscarbon(owner.current)) - var/mob/living/C = owner.current - if(C.hud_used && C.hud_used.devilsouldisplay) - C.hud_used.devilsouldisplay.update_counter(SOULVALUE) - -// SECTION: Messages and explanations - -/datum/devilinfo/proc/announce_laws() - var/list/messages = list() - messages.Add("You remember your link to the infernal. You are [truename], an agent of hell, a devil. And you were sent to the plane of creation for a reason. A greater purpose. Convince the crew to sin, and embroiden Hell's grasp.") - messages.Add("However, your infernal form is not without weaknesses.") - messages.Add("You may not use violence to coerce someone into selling their soul.") - messages.Add("You may not directly and knowingly physically harm a devil, other than yourself.") - messages.Add(GLOB.lawlorify[LAW][bane]) - messages.Add(GLOB.lawlorify[LAW][ban]) - messages.Add(GLOB.lawlorify[LAW][obligation]) - messages.Add(GLOB.lawlorify[LAW][banish]) - messages.Add("

Remember, the crew can research your weaknesses if they find out your devil name.
") - return messages - -#undef BLOOD_THRESHOLD -#undef TRUE_THRESHOLD -#undef ARCH_THRESHOLD -#undef BASIC_DEVIL -#undef BLOOD_LIZARD -#undef TRUE_DEVIL -#undef ARCH_DEVIL -#undef LOSS_PER_DEATH -#undef SOULVALUE -#undef DEVILRESURRECTTIME - -/datum/outfit/devil_lawyer - name = "Devil Lawyer" - uniform = /obj/item/clothing/under/lawyer/black - shoes = /obj/item/clothing/shoes/laceup - back = /obj/item/storage/backpack - l_hand = /obj/item/storage/briefcase - l_pocket = /obj/item/pen - l_ear = /obj/item/radio/headset - - id = /obj/item/card/id - -/datum/outfit/devil_lawyer/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - var/obj/item/card/id/W = H.wear_id - if(!istype(W) || W.assignment) // either doesn't have a card, or the card is already written to - return - var/name_to_use = H.real_name - if(H.mind && H.mind.devilinfo) - // Having hell create an ID for you causes its risks - name_to_use = H.mind.devilinfo.truename - - W.name = "[name_to_use]'s ID Card (Lawyer)" - W.registered_name = name_to_use - W.assignment = "Lawyer" - W.rank = W.assignment - W.age = H.age - W.sex = capitalize(H.gender) - W.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) - W.photo = get_id_photo(H) diff --git a/code/game/gamemodes/devil/game_mode.dm b/code/game/gamemodes/devil/game_mode.dm index a6c785fbd56..fc38bd056c9 100644 --- a/code/game/gamemodes/devil/game_mode.dm +++ b/code/game/gamemodes/devil/game_mode.dm @@ -1,106 +1,3 @@ /datum/game_mode var/list/datum/mind/sintouched = list() var/list/datum/mind/devils = list() - var/devil_ascended = 0 // Number of arch devils on station - -/datum/game_mode/proc/auto_declare_completion_sintouched() - var/text = "" - if(!length(sintouched)) - return - - text += "
The sintouched were:" - var/list/sintouchedUnique = uniqueList(sintouched) - for(var/S in sintouchedUnique) - var/datum/mind/sintouched_mind = S - text += printplayer(sintouched_mind) - text += printobjectives(sintouched_mind) - text += "
" - - text += "
" - - to_chat(world,text) - -/datum/game_mode/proc/auto_declare_completion_devils() - var/text = "" - if(!length(devils)) - return - - text += "
The devils were:" - for(var/D in devils) - var/datum/mind/devil = D - text += printplayer(devil) - text += printdevilinfo(devil) - text += printobjectives(devil) - text += "
" - - text += "
" - - to_chat(world,text) - - -/datum/game_mode/proc/finalize_devil(datum/mind/devil_mind, ascendable = FALSE) - var/trueName= randomDevilName() - devil_mind.devilinfo = devilInfo(trueName, 1) - devil_mind.devilinfo.ascendable = ascendable - devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]
You may not use violence to coerce someone into selling their soul.
You may not directly and knowingly physically harm a devil, other than yourself.
[GLOB.lawlorify[LAW][devil_mind.devilinfo.bane]]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]
[GLOB.lawlorify[LAW][devil_mind.devilinfo.banish]]
") - devil_mind.devilinfo.link_with_mob(devil_mind.current) - if(devil_mind.assigned_role == JOB_TITLE_CLOWN) - to_chat(devil_mind.current, "Your infernal nature allows you to wield weapons without harming yourself.") - devil_mind.current.force_gene_block(GLOB.clumsyblock, FALSE) - // Don't give them another action if they already have one. - if(!(locate(/datum/action/innate/toggle_clumsy) in devil_mind.current.actions)) - var/datum/action/innate/toggle_clumsy/toggle_clumsy = new - toggle_clumsy.Grant(devil_mind.current) - - spawn(10) - devil_mind.devilinfo.update_hud() - if(issilicon(devil_mind.current)) - var/mob/living/silicon/S = devil_mind.current - S.laws.set_sixsixsix_law("You may not use violence to coerce someone into selling their soul.") - S.laws.set_sixsixsix_law("You may not directly and knowingly physically harm a devil, other than yourself.") - S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]") - S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]") - S.laws.set_sixsixsix_law("Accomplish your objectives at all costs.") - -// unsure about the second "quantity" arg and how it fits with the antag refactor -/datum/game_mode/proc/forge_devil_objectives(datum/mind/devil_mind, quantity) - var/list/validtypes = list(/datum/objective/devil/soulquantity, /datum/objective/devil/soulquality, /datum/objective/devil/sintouch, /datum/objective/devil/buy_target) - for(var/i = 1 to quantity) - var/type = pick(validtypes) - var/datum/objective/devil/objective = new type(null) - objective.owner = devil_mind - devil_mind.objectives += objective - if(!istype(objective, /datum/objective/devil/buy_target)) - validtypes -= type //prevent duplicate objectives, EXCEPT for buy_target. - else - objective.find_target() - -/datum/game_mode/proc/greet_devil(datum/mind/devil_mind) - if(!devil_mind.devilinfo) - return - var/list/messages = list() - messages.Add(devil_mind.devilinfo.announce_laws()) - messages.Add(devil_mind.prepare_announce_objectives()) - to_chat(devil_mind.current, chat_box_red(messages.Join("
"))) - - -/datum/game_mode/proc/printdevilinfo(datum/mind/ply) - if(!ply.devilinfo) - return "Target is not a devil." - var/text = "
The devil's true name is: [ply.devilinfo.truename]
" - text += "The devil's bans were:
" - text += " [GLOB.lawlorify[LORE][ply.devilinfo.ban]]
" - text += " [GLOB.lawlorify[LORE][ply.devilinfo.bane]]
" - text += " [GLOB.lawlorify[LORE][ply.devilinfo.obligation]]
" - text += " [GLOB.lawlorify[LORE][ply.devilinfo.banish]]

" - return text - -/datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] - hud.join_hud(devil_mind.current) - set_antag_hud(devil_mind.current, "huddevil") - -/datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] - hud.leave_hud(devil_mind.current) - set_antag_hud(devil_mind.current, null) diff --git a/code/game/gamemodes/devil/objectives.dm b/code/game/gamemodes/devil/objectives.dm index 835a16c4593..655920bb6de 100644 --- a/code/game/gamemodes/devil/objectives.dm +++ b/code/game/gamemodes/devil/objectives.dm @@ -1,112 +1,87 @@ /datum/objective/devil -/datum/objective/devil/soulquantity +/datum/objective/devil/sacrifice + var/list/target_minds = list() needs_target = FALSE - explanation_text = "You shouldn't see this text. Error:DEVIL1" - target_amount = 4 + check_cryo = FALSE + target_amount = 12 + explanation_text = "" -/datum/objective/devil/soulquantity/New() - target_amount = pick(6, 7, 8) - update_explanation_text() +/datum/objective/devil/sacrifice/proc/forge() + if(!get_targets()) + return FALSE -/datum/objective/devil/proc/update_explanation_text() - //Intentionally empty + for(var/datum/mind/mind in target_minds) + explanation_text += "Принесите в жертву [mind.name], [mind.assigned_role].\n
" -/datum/objective/devil/soulquantity/update_explanation_text() - explanation_text = "Purchase, and retain control over at least [target_amount] souls." + return TRUE -/datum/objective/devil/soulquantity/check_completion() - var/count = 0 - for(var/S in owner.devilinfo.soulsOwned) - var/datum/mind/L = S - if(L.soulOwner == owner) - count++ - return count >= target_amount +/datum/objective/devil/sacrifice/proc/get_targets() + var/list/command_minds = list() + var/list/security_minds = list() + var/list/other_minds = list() + for(var/datum/mind/mind in SSticker.minds) + if(mind == owner) + continue + if(!ishuman(mind.current) \ + || mind.current.stat == DEAD \ + || mind.offstation_role) + continue -/datum/objective/devil/soulquality - needs_target = FALSE - explanation_text = "You shouldn't see this text. Error:DEVIL2" - var/contractType - var/contractName - -/datum/objective/devil/soulquality/New() - contractType = pick(CONTRACT_POWER, CONTRACT_WEALTH, CONTRACT_PRESTIGE, CONTRACT_MAGIC, CONTRACT_REVIVE, CONTRACT_KNOWLEDGE) - target_amount = pick(1, 2) - switch(contractType) - if(CONTRACT_POWER) - contractName = "for power" - if(CONTRACT_WEALTH) - contractName = "for wealth" - if(CONTRACT_PRESTIGE) - contractName = "for prestige" - if(CONTRACT_MAGIC) - contractName = "for magic" - if(CONTRACT_REVIVE) - contractName = "of revival" - if(CONTRACT_KNOWLEDGE) - contractName = "for knowledge" - update_explanation_text() - -/datum/objective/devil/soulquality/update_explanation_text() - explanation_text = "Have mortals sign at least [target_amount] contracts [contractName]." - -/datum/objective/devil/soulquality/check_completion() - var/count = 0 - for(var/S in owner.devilinfo.soulsOwned) - var/datum/mind/L = S - if(L.soulOwner != L && L.damnation_type == contractType) - count++ - return count >= target_amount + if(LAZYIN(GLOB.command_positions, mind.assigned_role)) + LAZYADD(command_minds, mind) + else if(LAZYIN(GLOB.security_positions, mind.assigned_role)) + LAZYADD(security_minds, mind) + else + LAZYADD(other_minds, mind) -/datum/objective/devil/sintouch - needs_target = FALSE - explanation_text = "You shouldn't see this text. Error:DEVIL3" + var/command_target_count = ceil(target_amount / 12) + var/security_target_count = floor(target_amount / 4) + var/other_target_count = target_amount - command_target_count - security_target_count -/datum/objective/devil/sintouch/New() - target_amount = pick(4, 5) - explanation_text = "Ensure at least [target_amount] mortals are sintouched." + if(LAZYLEN(command_minds) < command_target_count || LAZYLEN(security_minds) < security_target_count || LAZYLEN(other_minds) < other_target_count) + return FALSE -/datum/objective/devil/sintouch/check_completion() - return target_amount <= SSticker.mode.sintouched.len + for(var/i in 1 to command_target_count) + LAZYADD(target_minds, pick_n_take(command_minds)) + + for(var/i in 1 to security_target_count) + LAZYADD(target_minds, pick_n_take(security_minds)) + for(var/i in 1 to other_target_count) + LAZYADD(target_minds, pick_n_take(other_minds)) + return TRUE -/datum/objective/devil/buy_target - explanation_text = "You shouldn't see this text. Error:DEVIL4" +/datum/objective/devil/sacrifice/check_completion() + var/list/collected_minds = list() -/datum/objective/devil/buy_target/New() - find_target() - update_explanation_text() + for(var/datum/mind/mind as anything in target_minds) + if(mind.hasSoul) + continue -/datum/objective/devil/buy_target/update_explanation_text() - if(target) - explanation_text = "Purchase and retain the soul of [target.name], the [target.assigned_role]." - else - explanation_text = "Free objective." + LAZYADD(collected_minds, mind) -/datum/objective/devil/buy_target/check_completion() - return target.soulOwner == owner + return LAZYLEN(collected_minds) > target_amount +/datum/objective/devil/sintouch + needs_target = FALSE + explanation_text = "You shouldn't see this text. Error:DEVIL3" + +/datum/objective/devil/sintouch/New() + target_amount = pick(4, 5) + explanation_text = "Ensure at least [target_amount] mortals are sintouched." -/datum/objective/devil/outsell - explanation_text = "You shouldn't see this text. Error:DEVIL5" +/datum/objective/devil/sintouch/check_completion() + return target_amount <= SSticker.mode.sintouched.len -/datum/objective/devil/outsell/update_explanation_text() - explanation_text = "Purchase and retain control over more souls than [target.devilinfo.truename], known to mortals as [target.name], the [target.assigned_role]." +/datum/objective/devil/ascend + explanation_text = "Ascend to your true form." + needs_target = FALSE -/datum/objective/devil/outsell/check_completion() - var/selfcount = 0 - for(var/S in owner.devilinfo.soulsOwned) - var/datum/mind/L = S - if(L.soulOwner == owner) - selfcount++ - var/targetcount = 0 - for(var/S in target.devilinfo.soulsOwned) - var/datum/mind/L = S - if(L.soulOwner == target) - targetcount++ - return selfcount > targetcount +/datum/objective/devil/ascend/check_completion() + return isdevil(owner) diff --git a/code/game/gamemodes/devil/true_devil/inventory.dm b/code/game/gamemodes/devil/true_devil/inventory.dm deleted file mode 100644 index a1ab62fb623..00000000000 --- a/code/game/gamemodes/devil/true_devil/inventory.dm +++ /dev/null @@ -1,2 +0,0 @@ -// empty now - diff --git a/code/game/gamemodes/emergency_shuttle_lockdown.dm b/code/game/gamemodes/emergency_shuttle_lockdown.dm index f53b13b9a3b..d5d2e906fec 100644 --- a/code/game/gamemodes/emergency_shuttle_lockdown.dm +++ b/code/game/gamemodes/emergency_shuttle_lockdown.dm @@ -5,5 +5,5 @@ emergencyNoEscape = FALSE if(emergency.mode == SHUTTLE_STRANDED) emergency.mode = SHUTTLE_DOCKED - emergency.timer = world.time + emergency.timer = world.time + 3 MINUTES GLOB.priority_announcement.Announce("Угроза устранена. У вас есть 3 минуты, чтобы подняться на борт эвакуационного шаттла.", "Приоритетное оповещение.") diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index 2da4bd1f268..9549ce475c6 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -9,6 +9,6 @@ to_chat(world, "Just have fun and role-play!") -/datum/game_mode/extended/pre_setup() +/datum/game_mode/extended/mid_setup() return TRUE diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 8ef7feab05c..1e1859e4afc 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -77,11 +77,17 @@ /** - * Attempts to select players for special roles the mode might have. + * Selects players for special roles not affected by job assignment. Ex: Nuke, Wizard, Mouse blob */ /datum/game_mode/proc/pre_setup() return TRUE +/** + * After station jobs distributed, attempts to select players for special roles the mode might have. + * It also needs to return TRUE if game_mode is ready(antag roles assigned properly) + */ +/datum/game_mode/proc/mid_setup() + return TRUE /** * Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things. diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index f920ce70b34..fc248793984 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -48,7 +48,7 @@ GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective. return TRUE -/datum/game_mode/heist/pre_setup() +/datum/game_mode/heist/mid_setup() for(var/datum/mind/raider in raiders) raider.assigned_role = SPECIAL_ROLE_RAIDER raider.special_role = SPECIAL_ROLE_RAIDER diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index de5d8343e67..379dd5251aa 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -276,10 +276,10 @@ /obj/machinery/doomsday_device/Destroy() STOP_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 0 + SSshuttle.emergencyNoEscape = FALSE if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time + SSshuttle.emergency.timer = world.time + 3 MINUTES GLOB.priority_announcement.Announce("Вредоносное окружение устранено. У вас есть 3 минуты, чтобы подняться на борт эвакуационного шаттла.", "Приоритетное оповещение.", 'sound/AI/shuttledock.ogg') return ..() @@ -287,7 +287,7 @@ detonation_timer = world.time + default_timer timing = 1 START_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 1 + SSshuttle.emergencyNoEscape = TRUE /obj/machinery/doomsday_device/proc/seconds_remaining() . = max(0, (round(detonation_timer - world.time) / 10)) @@ -296,10 +296,10 @@ var/turf/T = get_turf(src) if(!T || !is_station_level(T.z)) GLOB.minor_announcement.Announce("УСТРОЙСТВО СУДНОГО ДНЯ ВНЕ ЗОНЫ ДЕЙСТВИЯ СТАНЦИИ, ОСТАНОВКА.", "ОШИБКА ОШИБКА $0ШБК$!А41.%%!!(%$^^__+ @#Ш0E4", 'sound/misc/notice1.ogg') - SSshuttle.emergencyNoEscape = 0 + SSshuttle.emergencyNoEscape = FALSE if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time + SSshuttle.emergency.timer = world.time + 3 MINUTES GLOB.priority_announcement.Announce("Вредоносное окружение устранено. У вас есть 3 минуты, чтобы подняться на борт эвакуационного шаттла.", "Приоритетное оповещение.", 'sound/AI/shuttledock.ogg') qdel(src) if(!timing) diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index 077a845b1d9..a8370d1c9da 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -23,7 +23,7 @@ to_chat(world, "Abductors - kidnap the crew and replace their organs with experimental ones.") to_chat(world, "Crew - don't get abducted and stop the abductors.") -/datum/game_mode/abduction/pre_setup() +/datum/game_mode/abduction/mid_setup() possible_abductors = get_players_for_role(ROLE_ABDUCTOR) if(!possible_abductors.len) diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index 445466bfc48..13501fdc868 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -316,13 +316,15 @@ uses = -1 /obj/item/organ/internal/heart/gland/bloody/activate() - owner.blood_volume = max(owner.blood_volume - 20, 0) - owner.visible_message("[owner]'s skin erupts with blood!",\ - "Blood pours from your skin!") + owner.AdjustBlood(-20) - for(var/turf/T in oview(3,owner)) //Make this respect walls and such + owner.visible_message(span_danger("Из кожи [owner] льётся кровь!"), \ + span_userdanger("Из вашей кожи хлещет кровь!")) + + for(var/turf/T in oview(3, owner)) // Make this respect walls and such owner.add_splatter_floor(T) - for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it. + + for(var/mob/living/carbon/human/H in oview(3, owner)) // Blood decals for simple animals would be neat. aka Carp with blood on it. H.add_mob_blood(owner) diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm index 20afc715ecf..54ebbbd355d 100644 --- a/code/game/gamemodes/miniantags/borer/borer.dm +++ b/code/game/gamemodes/miniantags/borer/borer.dm @@ -21,7 +21,7 @@ add_say_logs(src, message) if(stat == DEAD) return say_dead(message) - + var/mob/living/simple_animal/borer/B = loc to_chat(src, "Вы тихо шепчете, \"[message]\"") to_chat(B.host, span_alien("Пленённый разум [src] шепчет, \"[message]\"")) @@ -39,20 +39,16 @@ return B.host.say_understands(other, speaking) - /mob/living/captive_brain/resist() var/mob/living/simple_animal/borer/B = loc - if(host_resisting) - to_chat(src, span_notice("Вы уже пытаетесь вернуть своё тело!")) - return + to_chat(src, span_userdanger("Вы [host_resisting ? "перестаёте" : "начинаете"] сопротивляться контролю паразита.")) + to_chat(B.host, span_userdanger("Вы чувствуете, как пленённый разум [src] [host_resisting ? "перестаёт" : "начинает"] сопротивляться.")) host_resisting = TRUE - to_chat(src, span_userdanger("Вы начинаете упорно сопротивляться контролю паразита (это займёт примерно минуту).")) - to_chat(B.host, span_userdanger("Вы чувствуете, как пленённый разум [src] начинает сопротивляться.")) - var/delay = (rand(350,450) + B.host.getBrainLoss()) - if(!do_after(src, delay, B.host, ALL)) + if(!do_after(src, rand(350, 450) + B.host.getBrainLoss(), B.host, ALL, max_interact_count = 1)) + host_resisting = FALSE return return_control(B) @@ -116,7 +112,7 @@ var/truename // Name used for brainworm-speak. var/controlling // Used in human death check. - + var/mob/living/carbon/human/host // Human host for the brain worm. var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth. @@ -157,7 +153,7 @@ if(!.) return . - + detach() /mob/living/simple_animal/borer/ComponentInitialize() @@ -255,7 +251,7 @@ to_chat(src, span_notice("Теперь вы будете говорить в сознание носителя.")) talk_inside_host = FALSE return - + to_chat(src, span_notice("Теперь вы сможете говорить, находясь внутри носителя.")) talk_inside_host = TRUE return @@ -353,12 +349,12 @@ else return ..() -/mob/living/simple_animal/borer/UnarmedAttack(mob/living/carbon/human/M) - if(!can_unarmed_attack()) +/mob/living/simple_animal/borer/OnUnarmedAttack(mob/living/carbon/human/human) + if(!istype(human)) return - if(istype(M)) - to_chat(src, span_notice("Вы анализируете жизненные показатели [M].")) - healthscan(src, M, 1, TRUE) + + to_chat(src, span_notice("Вы анализируете жизненные показатели [human].")) + healthscan(src, human, 1, TRUE) /mob/living/simple_animal/borer/proc/secrete_chemicals() if(!host) @@ -380,7 +376,7 @@ for(var/datum/reagent/reagent as anything in subtypesof(/datum/reagent)) if(!LAZYIN(GLOB.borer_reagents, reagent.id) || !reagent.name) continue - + content += "[reagent.name] ([initial(reagent.chemuse)])

[reagent.chemdesc ? initial(reagent.chemdesc) : initial(reagent.description)]

" content += "" @@ -435,19 +431,19 @@ if(docile) to_chat(src, "Вы слишком обессилели для этого.") return - + var/list/content = list() - + for(var/datum/borer_focus/focus as anything in subtypesof(/datum/borer_focus)) if(locate(focus) in antag_datum.learned_focuses) continue LAZYADD(content, focus.bodypartname) - + if(!LAZYLEN(content)) to_chat(src, span_notice("Вы приобрели все доступные фокусы.")) return - + var/tgui_menu = tgui_input_list(src, "Choose focus", "Focus Menu", content) if(!tgui_menu) return @@ -471,7 +467,7 @@ to_chat(src, span_notice("Вы прячетесь.")) hiding = TRUE return - + layer = MOB_LAYER to_chat(src, span_notice("Вы перестали прятаться.")) hiding = FALSE @@ -639,23 +635,17 @@ controlling = TRUE + RemoveInfestActions() GrantControlActions() + talk_to_borer_action.Remove(host) host.med_hud_set_status() if(src && !src.key) src.key = "@[borer_key]" - return - -/mob/living/carbon/proc/punish_host() - var/mob/living/simple_animal/borer/borer = has_brain_worms() - if(borer?.host_brain) - to_chat(src, span_danger("Вы посылаете карающий всплеск психической агонии в мозг своего носителя.")) - to_chat(borer.host_brain, span_danger("Ужасная, жгучая агония пронзает вас насквозь, вырывая беззвучный крик из глубин вашего запертого разума!")) + return - return - //Brain slug proc for voluntary removal of control. /mob/living/carbon/proc/release_control() @@ -664,8 +654,8 @@ if(borer?.host_brain) to_chat(src, span_danger("Вы убираете свои хоботки, освобождая [borer.host_brain].")) borer.detach() - return - + return + log_runtime(EXCEPTION("Missing borer or missing host brain upon borer release."), src) return @@ -748,6 +738,8 @@ sneaking = FALSE RemoveControlActions() + GrantInfestActions() + talk_to_borer_action.Grant(host) host.med_hud_set_status() @@ -822,20 +814,22 @@ mind?.RemoveSpell(/obj/effect/proc_holder/spell/borer_dominate) /mob/living/simple_animal/borer/proc/GrantInfestActions() + mind?.AddSpell(new /obj/effect/proc_holder/spell/borer_force_say) talk_to_host_action.Grant(src) leave_body_action.Grant(src) take_control_action.Grant(src) make_chems_action.Grant(src) - mind?.AddSpell(new /obj/effect/proc_holder/spell/borer_force_say) focus_menu_action.Grant(src) + torment_action.Grant(src) /mob/living/simple_animal/borer/proc/RemoveInfestActions() + mind?.RemoveSpell(/obj/effect/proc_holder/spell/borer_force_say) talk_to_host_action.Remove(src) take_control_action.Remove(src) leave_body_action.Remove(src) make_chems_action.Remove(src) - mind?.RemoveSpell(/obj/effect/proc_holder/spell/borer_force_say) focus_menu_action.Remove(src) + torment_action.Remove(src) /mob/living/simple_animal/borer/proc/GrantControlActions() talk_to_brain_action.Grant(host) diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index ce9d67f7946..c91170c94e7 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -102,6 +102,7 @@ light_on = FALSE universal_speak = 0 universal_understand = 0 + hud_type = /datum/hud/swarmer var/resources = 0 //Resource points, generated by consuming metal/glass var/max_resources = 200 @@ -128,6 +129,7 @@ for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) updatename() + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) /mob/living/simple_animal/hostile/swarmer/proc/updatename() real_name = "Swarmer [rand(100,999)]-[pick("kappa","sigma","beta","omicron","iota","epsilon","omega","gamma","delta","tau","alpha")]" diff --git a/code/game/gamemodes/miniantags/demons/demon.dm b/code/game/gamemodes/miniantags/demons/demon.dm index 60f8b2bac62..71f195765ba 100644 --- a/code/game/gamemodes/miniantags/demons/demon.dm +++ b/code/game/gamemodes/miniantags/demons/demon.dm @@ -41,6 +41,7 @@ whisper_action = new() whisper_action.Grant(src) addtimer(CALLBACK(src, PROC_REF(attempt_objectives)), 5 SECONDS) + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) /mob/living/simple_animal/demon/ComponentInitialize() AddComponent( \ diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm index e7a464d7d32..97c09310ad8 100644 --- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm +++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm @@ -774,9 +774,7 @@ do_attack_animation(L) try_shock_mob(L) -/mob/living/simple_animal/demon/pulse_demon/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/demon/pulse_demon/OnUnarmedAttack(atom/A) if(isliving(A)) try_attack_mob(A) else if(isitem(A) && !is_under_tile()) diff --git a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm index 59dabb2265d..06f7d4860e4 100644 --- a/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm +++ b/code/game/gamemodes/miniantags/demons/shadow_demon/shadow_demon.dm @@ -59,11 +59,8 @@ set_varspeed(-0.3) return lum_count - -/mob/living/simple_animal/demon/shadow/UnarmedAttack(atom/target) - if(!can_unarmed_attack()) - return - + +/mob/living/simple_animal/demon/shadow/OnUnarmedAttack(atom/target) if(!ishuman(target)) if(isitem(target)) target.extinguish_light(TRUE) diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index 5fab14d1b2f..df58b9822b3 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -29,6 +29,7 @@ move_resist = MOVE_FORCE_STRONG AIStatus = AI_OFF butcher_results = list(/obj/item/reagent_containers/food/snacks/ectoplasm = 1) + hud_type = /datum/hud/guardian var/summoned = FALSE var/cooldown = 0 var/damage_transfer = 1 //how much damage from each attack we transfer to the owner diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index 89e50ebda09..a16bf67dd28 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -23,7 +23,7 @@ /mob/living/simple_animal/hostile/guardian/punch/AttackingTarget() . = ..() if(iscarbon(target) && target != summoner) - if(length(battlecry) > 8)//no more then 8 letters in a battle cry. + if(length_char(battlecry) > 8)//no more then 8 letters in a battle cry. visible_message("[src] punches [target]!") else say("[battlecry]", TRUE) diff --git a/code/game/gamemodes/miniantags/revenant/revenant.dm b/code/game/gamemodes/miniantags/revenant/revenant.dm index a9c47c8f336..02f7313be43 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant.dm @@ -58,6 +58,7 @@ /mob/living/simple_animal/revenant/Initialize(mapload) . = ..() ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) AddElement(/datum/element/simple_flying) /mob/living/simple_animal/revenant/ComponentInitialize() diff --git a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm index 94d9d3f5ca8..a4da0c365b7 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_abilities.dm @@ -439,7 +439,7 @@ var/list/potential_victims = list() for(var/mob/living/carbon/potential_victim in range(aoe_range, get_turf(possessed_object))) - if(!can_see(possessed_object, potential_victim, aoe_range)) // You can't see me + if(!possessed_object.can_see(potential_victim, aoe_range)) // You can't see me continue if(potential_victim.stat != CONSCIOUS) // Don't kill our precious essence-filled sleepy mobs diff --git a/code/game/gamemodes/miniantags/sintouched/objectives.dm b/code/game/gamemodes/miniantags/sintouched/objectives.dm index be5ebe4b8cd..5c4288bacd8 100644 --- a/code/game/gamemodes/miniantags/sintouched/objectives.dm +++ b/code/game/gamemodes/miniantags/sintouched/objectives.dm @@ -3,17 +3,6 @@ needs_target = FALSE var/mob/living/carbon/human/user -/* NO ERP OBJECTIVE FOR YOU. -/datum/objective/sintouched/lust - dangerrating = 3 // it's not AS dangerous. - -/datum/objective/sintouched/lust/New() - var/mob/dead/D = pick(dead_mob_list) - if(prob(50) && D) - explanation_text = "You know that [D] has perished.... and you think [D] is kinda cute. Make sure everyone knows how HOT [D]'s lifeless body is." - else - explanation_text = "Go get married, then immediately cheat on your new spouse." */ - /datum/objective/sintouched/proc/on_apply() return diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 9891af7473d..76a5460ad55 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -49,6 +49,8 @@ for(var/datum/mind/synd_mind in syndicates) synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. synd_mind.special_role = SPECIAL_ROLE_NUKEOPS + +/datum/game_mode/nuclear/mid_setup() return TRUE /datum/game_mode/proc/remove_operative(datum/mind/operative_mind) @@ -159,7 +161,7 @@ head_organ.sec_hair_colour = hair_c M.change_eye_color(eye_c) M.s_tone = skin_tone - head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) + head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species) head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) M.body_accessory = null M.regenerate_icons() diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index b55f4f98e5e..900b94fd7d1 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -513,9 +513,9 @@ GLOBAL_VAR(bomb_set) return if(locate(/obj/structure/blob) in T) return - var/obj/structure/blob/captured_nuke/N = new(T, src) + var/obj/structure/blob/special/captured_nuke/N = new(T, src) N.overmind = B.overmind - N.adjustcolors(B.color) + N.update_blob() /obj/machinery/nuclearbomb/tesla_act(power, explosive) ..() diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 94d79c24da9..70d4dee437a 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1751,3 +1751,15 @@ GLOBAL_LIST_EMPTY(admin_objective_list) /datum/objective/blob_find_place_to_burst needs_target = FALSE explanation_text = "Найдите укромное место на станции, в котором вас не смогут найти после вылупления до тех пор, пока вы не наберетесь сил." + +/datum/objective/blob_minion + name = "protect the blob core" + explanation_text = "Защищайте ядро блоба и исполняйте приказы надразумов. Любой ценой." + var/datum/weakref/overmind + + +/datum/objective/blob_minion/check_completion() + var/mob/camera/blob/resolved_overmind = overmind.resolve() + if(!resolved_overmind) + return FALSE + return resolved_overmind.stat != DEAD diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 5fbf5fbe614..86cbbf3a1c7 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -83,7 +83,7 @@ /////////////////////////////////////////////////////////////////////////////// //Gets the round setup, cancelling if there's not enough players at the start// /////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/pre_setup() +/datum/game_mode/revolution/mid_setup() var/list/possible_revolutionaries = get_players_for_role(ROLE_REV) if(CONFIG_GET(flag/protect_roles_from_antagonist)) diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index b5bcb8c187d..e7f3cae9311 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -46,7 +46,6 @@ GLOB.fakeblock1 = getAssignedBlock("", numsToAssign) GLOB.fakeblock2 = getAssignedBlock("", numsToAssign) GLOB.fakeblock3 = getAssignedBlock("", numsToAssign) - GLOB.fakeblock4 = getAssignedBlock("", numsToAssign) // Bay muts GLOB.breathlessblock = getAssignedBlock("BREATHLESS", numsToAssign, DNA_HARD_BOUNDS, good = TRUE) @@ -100,6 +99,7 @@ // Paradise1984 Disabilities GLOB.auld_imperial_block = getAssignedBlock("AULD_IMPERIAL", numsToAssign) GLOB.paraplegiablock = getAssignedBlock("PARAPLEGIA", numsToAssign) + GLOB.aphasiablock = getAssignedBlock("APHASIA", numsToAssign) // // Static Blocks diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm index 448fbc55d30..0de9f804fad 100644 --- a/code/game/gamemodes/shadowling/shadowling.dm +++ b/code/game/gamemodes/shadowling/shadowling.dm @@ -80,7 +80,7 @@ Made by Xhuis to_chat(world, "The current game mode is - Shadowling!") to_chat(world, "There are alien shadowlings on the station. Crew: Kill the shadowlings before they can eat or enthrall the crew. Shadowlings: Enthrall the crew while remaining in hiding.") -/datum/game_mode/shadowling/pre_setup() +/datum/game_mode/shadowling/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm index 6418d850862..ef9c898dbd5 100644 --- a/code/game/gamemodes/shadowling/shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm @@ -139,7 +139,7 @@ user.SetWeakened(0) user.SetKnockdown(0) user.incorporeal_move = INCORPOREAL_NORMAL - user.alpha = 0 + user.alpha_set(0, ALPHA_SOURCE_SHADOWLING) user.ExtinguishMob() user.forceMove(get_turf(user)) //to properly move the mob out of a potential container user.pulledby?.stop_pulling() @@ -151,7 +151,7 @@ user.visible_message("[user] suddenly manifests!", "The pressure becomes too much and you vacate the interdimensional darkness.") user.incorporeal_move = INCORPOREAL_NONE - user.alpha = 255 + user.alpha_set(1, ALPHA_SOURCE_SHADOWLING) user.forceMove(get_turf(user)) @@ -174,17 +174,17 @@ return new /datum/spell_targeting/self -/obj/effect/proc_holder/spell/shadowling_guise/cast(list/targets, mob/user = usr) +/obj/effect/proc_holder/spell/shadowling_guise/cast(list/targets, mob/living/user = usr) user.visible_message("[user] suddenly fades away!", "You veil yourself in darkness, making you harder to see.") - user.alpha = 10 + user.alpha_set(standartize_alpha(10), ALPHA_SOURCE_SHADOW_THRALL) addtimer(CALLBACK(src, PROC_REF(reveal), user), conseal_time) -/obj/effect/proc_holder/spell/shadowling_guise/proc/reveal(mob/user) +/obj/effect/proc_holder/spell/shadowling_guise/proc/reveal(mob/living/user) if(QDELETED(user)) return - user.alpha = initial(user.alpha) + user.alpha_set(1, ALPHA_SOURCE_SHADOW_THRALL) user.visible_message("[user] appears from nowhere!", "Your shadowy guise slips away.") @@ -956,12 +956,12 @@ user.visible_message("[user] suddenly vanishes!", \ "You begin phasing through planes of existence. Use the ability again to return.") user.incorporeal_move = INCORPOREAL_NORMAL - user.alpha = 0 + user.alpha_set(0, ALPHA_SOURCE_SHADOWLING) else user.visible_message("[user] suddenly appears from nowhere!", \ "You return from the space between worlds.") user.incorporeal_move = INCORPOREAL_NONE - user.alpha = 255 + user.alpha_set(1, ALPHA_SOURCE_SHADOWLING) /obj/effect/proc_holder/spell/aoe/ascendant_storm diff --git a/code/game/gamemodes/spaceninja/space_ninja.dm b/code/game/gamemodes/spaceninja/space_ninja.dm index c28323be312..2255f353a82 100644 --- a/code/game/gamemodes/spaceninja/space_ninja.dm +++ b/code/game/gamemodes/spaceninja/space_ninja.dm @@ -38,6 +38,9 @@ pre_ninja.set_original_mob(pre_ninja.current) pre_ninja?.current.loc = pick(GLOB.ninjastart) ..() + + +/datum/game_mode/space_ninja/mid_setup() return TRUE diff --git a/code/game/gamemodes/thief/changeling_thief.dm b/code/game/gamemodes/thief/changeling_thief.dm index e0b46a6fe66..cdf2f3620df 100644 --- a/code/game/gamemodes/thief/changeling_thief.dm +++ b/code/game/gamemodes/thief/changeling_thief.dm @@ -14,7 +14,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров и генокрадов. Не дайте генокрадам достичь успеха и скрыться, и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/changeling/thief/pre_setup() +/datum/game_mode/changeling/thief/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/thief/thief.dm b/code/game/gamemodes/thief/thief.dm index e8ec2f4fe1b..a14e5020c85 100644 --- a/code/game/gamemodes/thief/thief.dm +++ b/code/game/gamemodes/thief/thief.dm @@ -19,7 +19,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров. Не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/thief/pre_setup() +/datum/game_mode/thief/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/thief/traitor_thief.dm b/code/game/gamemodes/thief/traitor_thief.dm index 3a6d393a920..399dd8056ab 100644 --- a/code/game/gamemodes/thief/traitor_thief.dm +++ b/code/game/gamemodes/thief/traitor_thief.dm @@ -14,7 +14,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров и агентов Синдиката. Не дайте агентам Синдиката достичь успеха и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/traitor/thief/pre_setup() +/datum/game_mode/traitor/thief/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/thief/traitor_thief_chan.dm b/code/game/gamemodes/thief/traitor_thief_chan.dm index 6c36335f2a9..b4e5f7818db 100644 --- a/code/game/gamemodes/thief/traitor_thief_chan.dm +++ b/code/game/gamemodes/thief/traitor_thief_chan.dm @@ -15,7 +15,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров, генокрадов и агентов Синдиката. Не дайте агентам Синдиката и Генокрадам достичь успеха и скрыться, и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/traitor/thief/changeling/pre_setup() +/datum/game_mode/traitor/thief/changeling/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/thief/traitor_thief_vamp.dm b/code/game/gamemodes/thief/traitor_thief_vamp.dm index 9d1b3c52ac2..f0f03d0a126 100644 --- a/code/game/gamemodes/thief/traitor_thief_vamp.dm +++ b/code/game/gamemodes/thief/traitor_thief_vamp.dm @@ -15,7 +15,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров, вампиров и агентов Синдиката. Не дайте агентам Синдиката и Вампирам достичь успеха и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/traitor/thief/vampire/pre_setup() +/datum/game_mode/traitor/thief/vampire/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/thief/vampire_thief.dm b/code/game/gamemodes/thief/vampire_thief.dm index 7c446b68d56..32c4802914d 100644 --- a/code/game/gamemodes/thief/vampire_thief.dm +++ b/code/game/gamemodes/thief/vampire_thief.dm @@ -14,7 +14,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров и вампиров. Не дайте вампирам достичь успеха и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/vampire/thief/pre_setup() +/datum/game_mode/vampire/thief/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index 26a3307edad..ee94130c5e3 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -18,7 +18,7 @@ restricted_jobs = list(JOB_TITLE_CYBORG, JOB_TITLE_AI) protected_jobs = list(JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_HOS, JOB_TITLE_CAPTAIN, JOB_TITLE_BLUESHIELD, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, JOB_TITLE_LAWYER, JOB_TITLE_BRIGDOC, JOB_TITLE_CCOFFICER, JOB_TITLE_CCFIELD, JOB_TITLE_CCSPECOPS, JOB_TITLE_CCSUPREME, JOB_TITLE_SYNDICATE) /// Basically all jobs, except AI. - var/list/protected_jobs_AI = list(JOB_TITLE_CIVILIAN, JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_VIROLOGIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_HOP, JOB_TITLE_CHAPLAIN, JOB_TITLE_BARTENDER, JOB_TITLE_CHEF, JOB_TITLE_BOTANIST, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER, JOB_TITLE_CLOWN, JOB_TITLE_MIME, JOB_TITLE_JANITOR, JOB_TITLE_LIBRARIAN, JOB_TITLE_BARBER, JOB_TITLE_EXPLORER) + var/list/protected_jobs_AI = list(JOB_TITLE_CIVILIAN, JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_VIROLOGIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_HOP, JOB_TITLE_CHAPLAIN, JOB_TITLE_BARTENDER, JOB_TITLE_CHEF, JOB_TITLE_BOTANIST, JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER, JOB_TITLE_CLOWN, JOB_TITLE_MIME, JOB_TITLE_JANITOR, JOB_TITLE_LIBRARIAN, JOB_TITLE_EXPLORER) required_players = 0 required_enemies = 1 recommended_enemies = 4 @@ -35,7 +35,7 @@ to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") -/datum/game_mode/traitor/pre_setup() +/datum/game_mode/traitor/mid_setup() . = FALSE if(CONFIG_GET(flag/protect_roles_from_antagonist)) diff --git a/code/game/gamemodes/vampire/goon_vampire.dm b/code/game/gamemodes/vampire/goon_vampire.dm index ca50b58ccb3..7aad2d011f5 100644 --- a/code/game/gamemodes/vampire/goon_vampire.dm +++ b/code/game/gamemodes/vampire/goon_vampire.dm @@ -25,7 +25,7 @@ to_chat(world, "There are Bluespace Vampires infesting your fellow crewmates, keep your blood close and neck safe!") -/datum/game_mode/goon_vampire/pre_setup() +/datum/game_mode/goon_vampire/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/vampire/thief_vamp.dm b/code/game/gamemodes/vampire/thief_vamp.dm index b34b71c112a..fe86a45843a 100644 --- a/code/game/gamemodes/vampire/thief_vamp.dm +++ b/code/game/gamemodes/vampire/thief_vamp.dm @@ -14,7 +14,7 @@ to_chat(world, "На станции зафиксирована деятельность гильдии воров и вампиров. Не дайте вампирам достичь успеха и не допустите кражу дорогостоящего оборудования!") -/datum/game_mode/thief/vampire/pre_setup() +/datum/game_mode/thief/vampire/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm index 2dc3afd4555..8a50aae218d 100644 --- a/code/game/gamemodes/vampire/traitor_vamp.dm +++ b/code/game/gamemodes/vampire/traitor_vamp.dm @@ -16,7 +16,7 @@ to_chat(world, "There is a Vampire from Space Transylvania on the station along with some syndicate operatives out for their own gain! Do not let the vampire and the traitors succeed!") -/datum/game_mode/traitor/vampire/pre_setup() +/datum/game_mode/traitor/vampire/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index 0434e246cac..4f70138d549 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -21,7 +21,7 @@ to_chat(world, "There are Bluespace Vampires infesting your fellow crewmates, keep your blood close and neck safe!") -/datum/game_mode/vampire/pre_setup() +/datum/game_mode/vampire/mid_setup() if(CONFIG_GET(flag/protect_roles_from_antagonist)) restricted_jobs += protected_jobs diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 97b3b8b0aba..a58e3ab6cdb 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -596,7 +596,7 @@ GLOBAL_LIST_EMPTY(multiverse) H.equip_to_slot_or_del(new /obj/item/clothing/under/roman(H), ITEM_SLOT_CLOTH_INNER) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/roman(H), ITEM_SLOT_FEET) H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), ITEM_SLOT_HAND_LEFT) - H.equip_to_slot_or_del(new /obj/item/claymore(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/claymore(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), ITEM_SLOT_BACK) if("pirate") H.equip_to_slot_or_del(new /obj/item/clothing/under/pirate(H), ITEM_SLOT_CLOTH_INNER) @@ -604,7 +604,7 @@ GLOBAL_LIST_EMPTY(multiverse) H.equip_to_slot_or_del(new /obj/item/clothing/head/bandana(H), ITEM_SLOT_HEAD) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), ITEM_SLOT_FEET) H.equip_to_slot_or_del(new /obj/item/clothing/glasses/eyepatch(H), ITEM_SLOT_EYES) - H.equip_to_slot_or_del(new /obj/item/claymore(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/claymore(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), ITEM_SLOT_BACK) H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), ITEM_SLOT_HAND_LEFT) if("yand")//mine is an evil laugh @@ -612,7 +612,7 @@ GLOBAL_LIST_EMPTY(multiverse) H.equip_to_slot_or_del(new /obj/item/clothing/head/kitty(H), ITEM_SLOT_HEAD) H.equip_to_slot_or_del(new /obj/item/clothing/under/schoolgirl(H), ITEM_SLOT_CLOTH_INNER) H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(H), ITEM_SLOT_CLOTH_OUTER) - H.equip_to_slot_or_del(new /obj/item/katana(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/katana(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), ITEM_SLOT_HAND_LEFT) H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), ITEM_SLOT_BACK) if("clown") @@ -621,7 +621,7 @@ GLOBAL_LIST_EMPTY(multiverse) H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), ITEM_SLOT_MASK) H.equip_to_slot_or_del(new /obj/item/clothing/head/stalhelm(H), ITEM_SLOT_HEAD) H.equip_to_slot_or_del(new /obj/item/bikehorn(H), ITEM_SLOT_POCKET_LEFT) - H.equip_to_slot_or_del(new /obj/item/claymore(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/claymore(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), ITEM_SLOT_HAND_LEFT) H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), ITEM_SLOT_BACK) @@ -647,7 +647,7 @@ GLOBAL_LIST_EMPTY(multiverse) H.equip_to_slot_or_del(new /obj/item/clothing/head/kitty(H), ITEM_SLOT_HEAD) H.equip_to_slot_or_del(new /obj/item/clothing/under/schoolgirl(H), ITEM_SLOT_CLOTH_INNER) H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/vest(H), ITEM_SLOT_CLOTH_OUTER) - H.equip_to_slot_or_del(new /obj/item/katana(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/katana(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/shield/riot/roman(H), ITEM_SLOT_HAND_LEFT) H.equip_to_slot_or_del(new /obj/item/twohanded/spear(H), ITEM_SLOT_BACK) if(!H.real_name || H.real_name == "unknown") diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index 2901e08da0b..c36b3412c55 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -503,37 +503,48 @@ /obj/item/soulstone/proc/init_shade(mob/living/M, mob/user, forced = FALSE) var/type = get_shade_type() var/mob/living/simple_animal/shade/S = new type(src) + S.name = "Shade of [M.real_name]" S.real_name = "Shade of [M.real_name]" S.key = M.key S.cancel_camera() + update_appearance(UPDATE_ICON_STATE|UPDATE_NAME) log_game("[S.key] has become [S.name] with [purified ? "holy" : "corrupted"] essence.") if(user) S.faction |= "\ref[user]" //Add the master as a faction, allowing inter-mob cooperation - if(iswizard(user)) - SSticker.mode.update_wiz_icons_added(S.mind) - S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE - if(iscultist(user)) - SSticker.mode.add_cultist(S.mind) - S.mind.special_role = SPECIAL_ROLE_CULTIST - S.mind.store_memory("Serve the cult's will.") - to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") - else - S.mind.store_memory("Serve [user.real_name], your creator.") - to_chat(S, "Your soul has been captured! You are now bound to [user.real_name]'s will. Help them succeed in their goals at all costs.") + + if(S.mind) + if(iswizard(user)) + SSticker.mode.update_wiz_icons_added(S.mind) + S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE + + if(iscultist(user)) + SSticker.mode.add_cultist(S.mind) + S.mind.special_role = SPECIAL_ROLE_CULTIST + S.mind.store_memory("Serve the cult's will.") + to_chat(S, span_userdanger("Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.")) + + else + S.mind.store_memory("Serve [user.real_name], your creator.") + to_chat(S, span_userdanger("Your soul has been captured! You are now bound to [user.real_name]'s will. Help them succeed in their goals at all costs.")) + if(forced && user) - to_chat(user, "Capture successful!: [M.real_name]'s soul has been ripped from [user.p_their()] body and stored within the soul stone.") + to_chat(user, "[span_info("Capture successful!:")] [M.real_name]'s soul has been ripped from [user.p_their()] body and stored within the soul stone.") + if(isrobot(M))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn. M.dust() + else for(var/obj/item/I in M) M.drop_item_ground(I) + M.dust() /obj/item/soulstone/proc/get_shade_type() if(purified) return /mob/living/simple_animal/shade/holy + return /mob/living/simple_animal/shade/cult /obj/item/soulstone/proc/get_cult_ghost(mob/living/M, mob/user, get_new_player = FALSE) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 4e272eb52b8..557b0d4f5a8 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -55,6 +55,8 @@ apprentice.set_original_mob(apprentice.current) apprentice.current.loc = pick(GLOB.wizardstart) ..() + +/datum/game_mode/wizard/mid_setup() return TRUE /datum/game_mode/wizard/post_setup() diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index ed8fe897cae..b4eafb34e96 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -216,12 +216,13 @@ if(G.implantable) //only works for organ-implants var/obj/item/organ/internal/I = new G.path I.insert(H) - to_chat(H, span_notice("Implanting you with [G.display_name]!")) + to_chat(H, span_notice("Implanting you with [I.name]!")) continue if(G.slot) - if(H.equip_to_slot_or_del(G.spawn_item(H, H.client.prefs.loadout_gear[G.display_name]), G.slot)) - to_chat(H, "Equipping you with [G.display_name]!") + var/obj/item/placed_in = G.spawn_item(H, H.client.prefs.get_gear_metadata(G)) + if(H.equip_to_slot_or_del(placed_in, G.slot, TRUE)) + to_chat(H, span_notice("Equipping you with [placed_in.name]!")) else gear_leftovers += G else @@ -239,19 +240,19 @@ if(gear_leftovers.len) for(var/datum/gear/G in gear_leftovers) - var/obj/item/placed_in = G.spawn_item(get_turf(H), H.client.prefs.loadout_gear[G.display_name]) + var/obj/item/placed_in = G.spawn_item(null, H.client.prefs.get_gear_metadata(G)) if(placed_in.equip_to_best_slot(H)) - to_chat(H, "Placing [G.display_name] in your inventory!") + to_chat(H, span_notice("Placing [placed_in.name] in your inventory!")) continue if(H.put_in_hands(placed_in)) - to_chat(H, "Placing [G.display_name] in your hands!") + to_chat(H, span_notice("Placing [placed_in.name] in your hands!")) continue - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") + to_chat(H, span_danger("Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.")) qdel(placed_in) qdel(gear_leftovers) - return 1 + return TRUE /datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) var/datum/job/J = SSjobs.GetJobType(jobtype) diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 6ad447c1326..9f6e5f2a2f4 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -19,7 +19,7 @@ ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_PILOT, ACCESS_WEAPONS) minimal_player_age = 21 min_age_type = JOB_MIN_AGE_COMMAND - blocked_race_for_job = list(SPECIES_VOX) + blocked_race_for_job = list(SPECIES_VOX, SPECIES_NUCLEATION) exp_requirements = 3000 exp_type = EXP_TYPE_SECURITY disabilities_allowed = 0 @@ -75,7 +75,7 @@ minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_PILOT, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_GATEWAY, ACCESS_WEAPONS) alt_titles = list("Brig Sergeant") minimal_player_age = 21 - blocked_race_for_job = list(SPECIES_VOX) + blocked_race_for_job = list(SPECIES_VOX, SPECIES_NUCLEATION) exp_requirements = 2100 exp_type = EXP_TYPE_SECURITY outfit = /datum/outfit/job/warden @@ -150,7 +150,7 @@ glasses = /obj/item/clothing/glasses/hud/security/sunglasses/aviators id = /obj/item/card/id/security l_pocket = /obj/item/toy/crayon/white - r_pocket = /obj/item/lighter/zippo + r_pocket = /obj/item/lighter/zippo/detective pda = /obj/item/pda/detective l_hand = /obj/item/storage/briefcase/crimekit backpack_contents = list( @@ -192,7 +192,7 @@ minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) alt_titles = list("Security Trainer","Patrol Officer", "Security Cadet") minimal_player_age = 14 - blocked_race_for_job = list(SPECIES_VOX) + blocked_race_for_job = list(SPECIES_VOX, SPECIES_NUCLEATION) exp_requirements = 600 exp_type = EXP_TYPE_CREW outfit = /datum/outfit/job/officer @@ -303,7 +303,7 @@ access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) minimal_player_age = 7 - blocked_race_for_job = list(SPECIES_VOX) + blocked_race_for_job = list(SPECIES_VOX, SPECIES_NUCLEATION) exp_requirements = 1200 exp_type = EXP_TYPE_SECURITY outfit = /datum/outfit/job/pilot diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 950f74dd3f0..964541d55f6 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -184,7 +184,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca transfer_allowed = FALSE minimal_player_age = 21 min_age_type = JOB_MIN_AGE_HIGH_ED - blocked_race_for_job = list(SPECIES_VOX) + blocked_race_for_job = list(SPECIES_VOX, SPECIES_NUCLEATION) exp_requirements = 3000 exp_type = EXP_TYPE_SECURITY access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, @@ -210,7 +210,7 @@ GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newsca gloves = /obj/item/clothing/gloves/combat/swat shoes = /obj/item/clothing/shoes/jackboots l_ear = /obj/item/radio/headset/heads/blueshield/alt - glasses = /obj/item/clothing/glasses/hud/health/sunglasses + glasses = /obj/item/clothing/glasses/hud/blueshield id = /obj/item/card/id/nanotrasen pda = /obj/item/pda/heads/blueshield backpack_contents = list( diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 00bdf0a6cab..f8d0eeb1bc1 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -82,6 +82,7 @@ is_supply = 1 supervisors = "the quartermaster" department_head = list(JOB_TITLE_QUARTERMASTER) + blocked_race_for_job = list(SPECIES_NUCLEATION) selection_color = "#e2dbc8" access = list(ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) minimal_access = list(ACCESS_MINING, ACCESS_MINT, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) @@ -613,37 +614,6 @@ backpack_contents = list( /obj/item/videocam = 1) -/datum/job/barber - title = JOB_TITLE_BARBER - flag = JOB_FLAG_BARBER - department_flag = JOBCAT_KARMA - total_positions = 1 - spawn_positions = 1 - is_service = 1 - supervisors = "the head of personnel" - department_head = list(JOB_TITLE_HOP) - selection_color = "#d1e8d3" - alt_titles = list("Hair Stylist","Beautician") - access = list(ACCESS_MAINT_TUNNELS) - minimal_access = list(ACCESS_MAINT_TUNNELS) - outfit = /datum/outfit/job/barber - - salary = 100 - min_start_money = 100 - max_start_money = 300 - -/datum/outfit/job/barber - name = "Barber" - jobtype = /datum/job/barber - - uniform = /obj/item/clothing/under/barber - shoes = /obj/item/clothing/shoes/black - l_ear = /obj/item/radio/headset/headset_service - backpack_contents = list( - /obj/item/storage/box/lip_stick = 1, - /obj/item/storage/box/barber = 1 - ) - /datum/job/explorer title = JOB_TITLE_EXPLORER flag = JOB_FLAG_EXPLORER diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 18ffb0318fa..2442702a7be 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -76,7 +76,6 @@ GLOBAL_LIST_INIT(support_positions, list( JOB_TITLE_CHAPLAIN, JOB_TITLE_CLOWN, JOB_TITLE_MIME, - JOB_TITLE_BARBER, JOB_TITLE_JUDGE, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_BLUESHIELD, @@ -105,7 +104,6 @@ GLOBAL_LIST_INIT(nonhuman_positions, list( GLOBAL_LIST_INIT(whitelisted_positions, list( JOB_TITLE_BLUESHIELD, JOB_TITLE_REPRESENTATIVE, - JOB_TITLE_BARBER, JOB_TITLE_MECHANIC, JOB_TITLE_BRIGDOC, JOB_TITLE_JUDGE, diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index 0d01315d28f..c720faa2ba3 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -38,8 +38,7 @@ // Get Base64 version of an icon for our TGUI needs. // Always try to get first frame as it can be animation resulting in all frames in single image. // pda-library as an example has 4 frames - var/base64icon = "[icon2base64(icon(initial(pda.icon), initial(pda.icon_state), frame = 1))]" - new_color_list[initial(pda.icon_state)] = list(base64icon, initial(pda.desc)) + new_color_list[initial(pda.icon_state)] = list(initial(pda.icon), initial(pda.desc)) new_color_list = sortAssoc(new_color_list) colorlist = new_color_list @@ -152,12 +151,13 @@ if(storedpda) data["hasPDA"] = TRUE - data["pdaIcon"] = storedpda.base64icon + data["pdaIconState"] = storedpda.icon_state data["pdaOwnerName"] = storedpda.owner data["pdaJobName"] = storedpda.ownjob else data["hasPDA"] = FALSE data["pdaIcon"] = null + data["pdaIconState"] = null data["pdaOwnerName"] = null data["pdaJobName"] = null @@ -171,6 +171,7 @@ /obj/machinery/pdapainter/ui_static_data(mob/user) var/data = list() data["pdaTypes"] = colorlist + data["pdaIcon"] = icon data["allowErasePda"] = allowErasePda return data @@ -189,7 +190,7 @@ if(storedpda) storedpda.remove_pda_case() var/new_icon = params["selectedPda"] - storedpda.current_painting = list("icon" = new_icon, "base64" = colorlist[new_icon][1], "desc" = colorlist[new_icon][2]) + storedpda.current_painting = list("icon" = new_icon, "desc" = colorlist[new_icon][2]) storedpda.update_appearance(UPDATE_ICON_STATE|UPDATE_DESC) playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 15, TRUE) statusLabel = "Покраска завершена" diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 18d6f43d030..38f5af12c2a 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -123,7 +123,8 @@ "requirements" = matreq, "hacked" = ("hacked" in categories) ? TRUE : FALSE, "max_multiplier" = maxmult, - "image" = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1))]" + "icon" = initial(I.icon), + "icon_state" = initial(I.icon_state), ))) recipiecache = recipes data["recipes"] = recipiecache diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index f937c2d75e2..d8097fc37c9 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -58,7 +58,7 @@ /// Returns TRUE if the camera can see the target. -/obj/machinery/camera/proc/can_see(atom/target, length = 7) // I stole this from global and modified it to work with Xray cameras. +/obj/machinery/camera/can_see(atom/target, length = 7) // I stole this from global and modified it to work with Xray cameras. if(!target || target.invisibility > SEE_INVISIBLE_LIVING || target.alpha == NINJA_ALPHA_INVISIBILITY) return FALSE var/turf/current_turf = get_turf(src) diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 8a2c33e56f5..e24343ae45f 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -70,26 +70,26 @@ update_icon(UPDATE_ICON_STATE) return ATTACK_CHAIN_PROCEED_SUCCESS - if(istype(I, /obj/item/aiModule/purge)) + if(istype(I, /obj/item/ai_module/purge)) add_fingerprint(user) laws.clear_inherent_laws() to_chat(user, span_notice("Law module applied.")) return ATTACK_CHAIN_PROCEED_SUCCESS - if(istype(I, /obj/item/aiModule/freeform)) + if(istype(I, /obj/item/ai_module/freeform)) add_fingerprint(user) - var/obj/item/aiModule/freeform/freeform = I - laws.add_inherent_law(freeform.newFreeFormLaw) + var/obj/item/ai_module/freeform/freeform = I + laws.add_inherent_law(freeform.new_freeform_law) to_chat(user, span_notice("Added a freeform law.")) return ATTACK_CHAIN_PROCEED_SUCCESS - if(istype(I, /obj/item/aiModule)) + if(istype(I, /obj/item/ai_module)) add_fingerprint(user) - var/obj/item/aiModule/aiModule = I - if(!aiModule.laws) + var/obj/item/ai_module/ai_module = I + if(!ai_module.laws) to_chat(user, span_warning("This AI module can not be applied directly to AI cores.")) return ATTACK_CHAIN_PROCEED - laws = aiModule.laws + laws = ai_module.laws return ATTACK_CHAIN_PROCEED_SUCCESS if(istype(I, /obj/item/mmi)) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 97ae55b7816..e4cee1999bb 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -108,7 +108,7 @@ /obj/item/circuitboard/borgupload board_name = "Cyborg Upload" - build_path = /obj/machinery/computer/borgupload + build_path = /obj/machinery/computer/aiupload/cyborg origin_tech = "programming=4;engineering=4" /obj/item/circuitboard/med_data diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 7bd435a4b05..22babf54ae8 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -56,7 +56,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0) /datum/job/mechanic, /datum/job/chaplain, /datum/job/officer, - /datum/job/barber ) //The scaling factor of max total positions in relation to the total amount of people on board the station in % var/max_relative_positions = 30 //30%: Seems reasonable, limit of 6 @ 20 players diff --git a/code/game/machinery/computer/law.dm b/code/game/machinery/computer/law.dm index 3e03f4f2118..3b0c947603c 100644 --- a/code/game/machinery/computer/law.dm +++ b/code/game/machinery/computer/law.dm @@ -1,10 +1,23 @@ +#define NORMAL_UPLOAD_DELAY 20 SECONDS +#define SHORTEN_UPLOAD_DELAY 5 SECONDS + /obj/machinery/computer/aiupload name = "\improper AI upload console" desc = "Используется для манипуляций с законами ИИ." icon_screen = "command" icon_keyboard = "med_key" circuit = /obj/item/circuitboard/aiupload - var/mob/living/silicon/ai/current = null + req_access = list(ACCESS_AI_UPLOAD) + var/mob/living/silicon/current = null + var/obj/item/ai_module/installed_module = null + var/obj/item/card/id/id = null + + var/static/list/shorten_delay = list( + /obj/item/ai_module/purge, + /obj/item/ai_module/reset + ) + var/timer_id = null + var/reg_name = null light_color = LIGHT_COLOR_WHITE light_range_on = 2 @@ -14,90 +27,246 @@ if(user.a_intent == INTENT_HARM) return ..() - if(istype(I, /obj/item/aiModule)) + if(istype(I, /obj/item/ai_module)) add_fingerprint(user) - if(!current) //no AI selected - to_chat(user, span_danger("No AI selected. Please chose a target before proceeding with upload.")) - return ATTACK_CHAIN_PROCEED - if(!atoms_share_level(current, src)) - to_chat(user, span_danger("Unable to establish a connection") + ": You're too far away from the target silicon!") - return ATTACK_CHAIN_PROCEED - if(current.on_the_card) - to_chat(user, span_danger("Unable to establish a connection") + ": Target silicon is on an inteliCard or undergoing a repair procedure!") - return ATTACK_CHAIN_PROCEED - var/obj/item/aiModule/aiModule = I - aiModule.install(src) + insert_module(user, I) + ui_interact(user) return ATTACK_CHAIN_PROCEED_SUCCESS + if(istype(I, /obj/item/card/id)) + add_fingerprint(user) + check_id(user, I) + ui_interact(user) + return return ..() - /obj/machinery/computer/aiupload/attack_hand(mob/user) - if(src.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") + if(stat & NOPOWER) + to_chat(user, span_notice("The upload computer has no power!")) + return ATTACK_CHAIN_PROCEED + if(stat & BROKEN) + to_chat(user, span_notice("The upload computer is broken!")) + return ATTACK_CHAIN_PROCEED + ui_interact(user) + +/obj/machinery/computer/aiupload/attack_ghost(mob/user) + return TRUE + +/obj/machinery/computer/aiupload/proc/choose_silicon(mob/user) + current = select_active_ai(user) + if(!current) + atom_say("No active AIs detected.") + return + to_chat(user, span_notice("[current.name] selected for law changes.")) + +/obj/machinery/computer/aiupload/proc/insert_module(mob/user, obj/item/ai_module/new_module) + if(installed_module) + if(!user.put_in_active_hand(installed_module)) + installed_module.forceMove(get_turf(src)) + installed_module = null + if(!istype(new_module)) return - if(src.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") + if(!user.drop_transfer_item_to_loc(new_module, src)) + to_chat(usr, span_warning("[new_module] is stuck to your hand!")) return + installed_module = new_module - if(..()) - return TRUE +/obj/machinery/computer/aiupload/proc/check_id(mob/user, obj/item/card/id/new_id) + if(id) + if(!user.put_in_active_hand(id)) + id.forceMove(get_turf(src)) + id = null + if(!istype(new_id)) + return + if(!istype(installed_module, /obj/item/ai_module/syndicate)) + if(!check_access(new_id)) + to_chat(user, span_warning("Unauthorized access.")) + return + if(!user.drop_transfer_item_to_loc(new_id, src)) + to_chat(user, span_warning("[new_id] is stuck to your hand!")) + return + id = new_id - src.current = select_active_ai(user) +// stop upload or start upload +/obj/machinery/computer/aiupload/proc/upload_module(mob/user) + if(stat & NOPOWER) + to_chat(user, span_warning("The upload computer has no power!")) + return + if(stat & BROKEN) + to_chat(user, span_warning("The upload computer is broken!")) + return + if(!current) + to_chat(user, span_warning("You haven't selected an AI to transmit laws to!")) + return + if(!installed_module) + to_chat(user, span_warning("No module inserted!")) + return + if(!isAI(current)) + to_chat(user, span_warning("[current] is not an AI.")) - if(!src.current) - to_chat(usr, "No active AIs detected.") - else - to_chat(usr, "[src.current.name] selected for law changes.") - return + if(!istype(installed_module, /obj/item/ai_module/syndicate)) //not hack module + if(!check_access(id)) // don't have access + to_chat(user, span_notice("Unauthorized access.")) + return // GO AWAY + if(timer_id) + stop_upload() + return -/obj/machinery/computer/aiupload/attack_ghost(mob/user) - return TRUE + var/mob/living/silicon/ai/ai = current + if(!atoms_share_level(get_turf(ai), src)) + to_chat(user, span_notice("Unable to establish a connection: You're too far away from the target silicon!")) + return + if(ai.on_the_card) + to_chat(user, span_notice("Unable to establish a connection: Target silicon is on an inteliCard or undergoing a repair procedure!")) + return + if(ai.stat == DEAD || ai.control_disabled) + to_chat(user, span_notice("Unable to establish a connection: No signal is being detected from the AI.")) + return + if(ai.nightvision == 0) + to_chat(user, span_notice("Unable to establish a connection: Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.")) + return + if(!installed_module.check_install(user)) + return + + var/delay = (installed_module in shorten_delay) ? SHORTEN_UPLOAD_DELAY : NORMAL_UPLOAD_DELAY + to_chat(user, span_notice("Upload process has started. ETA: [delay/10] seconds.")) + timer_id = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/computer/aiupload, finish_upload), user), delay, TIMER_STOPPABLE) + +/obj/machinery/computer/aiupload/proc/finish_upload(mob/user) + timer_id = null + installed_module.transmit_instructions(current, user, reg_name) + to_chat(current, "These are your laws now:") + current.show_laws() + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(R.lawupdate && (R.connected_ai == current)) + to_chat(R, "These are your laws now:") + R.show_laws() + atom_say("Upload complete. The laws have been modified.") + +/obj/machinery/computer/aiupload/proc/stop_upload(silent = FALSE) + deltimer(timer_id) + timer_id = null + if(!silent) + atom_say("Upload has been interrupted.") + +/obj/machinery/computer/aiupload/power_change() + . = ..() + if(!powered()) + if(timer_id) + stop_upload(TRUE) + +/obj/machinery/computer/aiupload/on_deconstruction() + . = ..() + if(timer_id) + stop_upload(TRUE) + current = null + installed_module = null + id = null +/obj/machinery/computer/aiupload/obj_break(damage_flag) + if(timer_id) + stop_upload(TRUE) + . = ..() + +/obj/machinery/computer/aiupload/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "UploadPanel", name) + ui.open() + +/obj/machinery/computer/aiupload/ui_data(mob/user) + var/list/data = list() + data["selected_target"] = current?.name + data["new_law"] = installed_module + data["id"] = id?.registered_name + data["transmitting"] = timer_id ? TRUE : FALSE + data["hacked"] = istype(installed_module, /obj/item/ai_module/syndicate) + return data + +/obj/machinery/computer/aiupload/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + if(..()) + return + if(issilicon(ui.user)) + to_chat(usr, span_danger("Access Denied (silicon detected)")) + return + add_fingerprint(usr) + switch(action) + if("choose_silicon") + choose_silicon(ui.user) + if("insert_module") + insert_module(ui.user, ui.user.get_active_hand()) + if("authorization") + check_id(ui.user, ui.user.get_active_hand()) + if("change_laws") + upload_module(ui.user) -// Why is this not a subtype -/obj/machinery/computer/borgupload +/obj/machinery/computer/aiupload/cyborg name = "cyborg upload console" desc = "Используется для манипуляций с законами киборгов." icon_screen = "command" icon_keyboard = "med_key" + req_access = list(ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/borgupload - var/mob/living/silicon/robot/current = null - - -/obj/machinery/computer/borgupload/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/aiModule)) - if(!current) //no borg selected - to_chat(user, span_danger("No borg selected. Please chose a target before proceeding with upload.")) - return ATTACK_CHAIN_PROCEED - if(!atoms_share_level(current, src)) - to_chat(user, span_danger("Unable to establish a connection") + ": You're too far away from the target silicon!") - return ATTACK_CHAIN_PROCEED - var/obj/item/aiModule/aiModule = I - aiModule.install(src) - return ATTACK_CHAIN_PROCEED - - return ..() +/obj/machinery/computer/aiupload/cyborg/choose_silicon(mob/user) + current = freeborg() + if(!current) + to_chat(user, span_notice("No free cyborgs detected.")) + return + to_chat(user, span_notice("[current.name] selected for law changes.")) -/obj/machinery/computer/borgupload/attack_hand(mob/user) - if(src.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") +/obj/machinery/computer/aiupload/cyborg/upload_module(mob/user) + if(stat & NOPOWER) + to_chat(user, span_warning("The upload computer has no power!")) + return + if(stat & BROKEN) + to_chat(user, span_warning("The upload computer is broken!")) + return + if(!current) + to_chat(user, span_warning("No borg selected. Please chose a target before proceeding with upload.")) + return + if(!(current)) return - if(src.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") + if(!installed_module) + to_chat(user, span_warning("No module inserted!")) return + if(!isrobot(current)) + to_chat(user, span_warning("[current] is not an cyborg.")) - src.current = freeborg() + if(!istype(installed_module, /obj/item/ai_module/syndicate)) //not hack module + if(!check_access(id)) // don't have access + to_chat(user, span_notice("Unauthorized access.")) + return // GO AWAY - if(!src.current) - to_chat(usr, "No free cyborgs detected.") - else - to_chat(usr, "[src.current.name] selected for law changes.") - return + if(timer_id) + stop_upload() + return + + var/mob/living/silicon/robot/robot = current + if(!atoms_share_level(get_turf(current), src)) + to_chat(user, span_notice("Unable to establish a connection: You're too far away from the target silicon!")) + return + if(robot.stat == DEAD || robot.emagged) + to_chat(user, span_notice("Unable to establish a connection: No signal is being detected from the robot.")) + return + if(robot.connected_ai) + to_chat(user, span_notice("Unable to establish a connection: The robot is slaved to an AI.")) + return + if(!installed_module.check_install(user)) + return + var/delay = (installed_module in shorten_delay) ? SHORTEN_UPLOAD_DELAY : NORMAL_UPLOAD_DELAY + to_chat(user, span_notice("Upload process has started. ETA: [delay/10] seconds.")) + reg_name = istype(installed_module, /obj/item/ai_module/syndicate) ? "UNKNOWN" : id.registered_name + timer_id = addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/machinery/computer/aiupload, finish_upload), user), delay, TIMER_STOPPABLE) -/obj/machinery/computer/borgupload/attack_ghost(mob/user) - return TRUE +/obj/machinery/computer/aiupload/cyborg/finish_upload(mob/user) + timer_id = null + installed_module.transmit_instructions(current, user, reg_name) + to_chat(current, "These are your laws now:") + current.show_laws() + atom_say("Upload complete. The laws have been modified.") +#undef NORMAL_UPLOAD_DELAY +#undef SHORTEN_UPLOAD_DELAY diff --git a/code/game/machinery/customat.dm b/code/game/machinery/customat.dm index 15fd069fb6a..428aa7d0b56 100644 --- a/code/game/machinery/customat.dm +++ b/code/game/machinery/customat.dm @@ -23,13 +23,16 @@ var/price = 0 ///Icon in tgui var/icon = "" + ///Icon_state in tgui + var/icon_state = "" /datum/data/customat_product/New(obj/item/I) name = I.name amount = 0 containtment = list() price = 0 - icon = icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1, FALSE)) + icon = icon(initial(I.icon)) + icon_state = initial(I.icon_state) /obj/machinery/customat @@ -565,7 +568,8 @@ price = product.price, stock = product.amount, icon = product.icon, - Key = product.key + icon_state = product.icon_state, + Key = product.key, ) data["products"] += list(data_pr) diff --git a/code/game/machinery/dye_generator.dm b/code/game/machinery/dye_generator.dm index 9da27873474..5a7fae2e55e 100644 --- a/code/game/machinery/dye_generator.dm +++ b/code/game/machinery/dye_generator.dm @@ -58,8 +58,8 @@ ..() if(stat & (BROKEN|NOPOWER)) return - var/temp = input(usr, "Choose a dye color", "Dye Color") as color|null - if(!temp) + var/temp = tgui_input_color(usr, "Choose a dye color", "Dye Color") + if(isnull(temp)) return set_light_color(temp) diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index e651a83157c..23789cfe01a 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -541,7 +541,8 @@ Class Procs: . += span_notice("It appears heavily damaged.") if(0 to 25) . += span_warning("It's falling apart!") - if(user.research_scanner && component_parts) + + if((user.research_scanner || user.check_smart_brain()) && component_parts) . += display_parts(user) /obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index a14a6de580d..fe89ef3e704 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -520,7 +520,7 @@ GLOBAL_LIST_EMPTY(turret_icons) //Verify that targeted atoms are in our sight. Otherwise, just remove them from processing. for(var/atom/movable/atom as anything in processing_targets) - if(!can_see(src, atom, scan_range)) + if(!can_see(atom, scan_range)) processing_targets -= atom var/list/primary_candidates diff --git a/code/game/machinery/tcomms/nttc.dm b/code/game/machinery/tcomms/nttc.dm index 57fd7d7e198..71c088ac5fd 100644 --- a/code/game/machinery/tcomms/nttc.dm +++ b/code/game/machinery/tcomms/nttc.dm @@ -76,7 +76,6 @@ JOB_TITLE_CARGOTECH = "supradio", JOB_TITLE_MINER = "supradio", // Service - JOB_TITLE_BARBER = "srvradio", JOB_TITLE_BARTENDER = "srvradio", JOB_TITLE_BOTANIST = "srvradio", JOB_TITLE_CHAPLAIN = "srvradio", diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 3f998ef340d..4ba043a3cd1 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -201,10 +201,6 @@ build_inventory(products, product_records) build_inventory(contraband, hidden_records) build_inventory(premium, coin_records) - for(var/datum/data/vending_product/R in (product_records + coin_records + hidden_records)) - var/obj/item/I = R.product_path - var/pp = path2assetID(R.product_path) - imagelist[pp] = "[icon2base64(icon(initial(I.icon), initial(I.icon_state), SOUTH, 1, FALSE))]" if(LAZYLEN(slogan_list)) // So not all machines speak at the exact same time. // The first time this machine says something will be at slogantime + this random value, @@ -215,6 +211,14 @@ for(var/typepath in subtypesof(/datum/vendor_crit)) all_possible_crits[typepath] = new typepath() + AddElement( \ + /datum/element/falling_hazard, \ + damage = 80, \ + hardhat_safety = FALSE, \ + crushes = TRUE, \ + impact_sound = 'sound/effects/vending_hit.ogg', \ + ) + update_icon(UPDATE_OVERLAYS) /obj/machinery/vending/examine(mob/user) @@ -794,10 +798,13 @@ data["product_records"] = list() var/i = 1 for (var/datum/data/vending_product/R in product_records) + var/obj/item = R.product_path var/list/data_pr = list( path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), name = R.name, - price = (R.product_path in prices) ? prices[R.product_path] : 0, + price = (item in prices) ? prices[item] : 0, + icon = item.icon, + icon_state = item.icon_state, max_amount = R.max_amount, req_coin = FALSE, is_hidden = FALSE, @@ -807,10 +814,13 @@ i++ data["coin_records"] = list() for (var/datum/data/vending_product/R in coin_records) + var/obj/item = R.product_path var/list/data_cr = list( path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), name = R.name, - price = (R.product_path in prices) ? prices[R.product_path] : 0, + price = (item in prices) ? prices[item] : 0, + icon = item.icon, + icon_state = item.icon_state, max_amount = R.max_amount, req_coin = TRUE, is_hidden = FALSE, @@ -821,10 +831,13 @@ i++ data["hidden_records"] = list() for (var/datum/data/vending_product/R in hidden_records) + var/obj/item = R.product_path var/list/data_hr = list( path = replacetext(replacetext("[R.product_path]", "/obj/item/", ""), "/", "-"), name = R.name, - price = (R.product_path in prices) ? prices[R.product_path] : 0, + price = (item in prices) ? prices[item] : 0, + icon = item.icon, + icon_state = item.icon_state, max_amount = R.max_amount, req_coin = FALSE, is_hidden = TRUE, @@ -2114,7 +2127,7 @@ /obj/item/clothing/mask/muzzle/safety = 4, /obj/item/storage/box/swabs = 6, /obj/item/storage/box/fingerprints = 6, /obj/item/eftpos/sec = 4, /obj/item/storage/belt/security/webbing = 2, /obj/item/clothing/mask/gas/sechailer/tactical = 5, /obj/item/flashlight/sectaclight = 2, /obj/item/grenade/smokebomb = 8, ) contraband = list(/obj/item/clothing/glasses/sunglasses = 2,/obj/item/storage/fancy/donut_box = 2,/obj/item/hailer = 5) - prices = list(/obj/item/storage/belt/security/webbing = 1999, /obj/item/clothing/mask/gas/sechailer/tactical = 299, /obj/item/flashlight/sectaclight = 299, /obj/item/grenade/smokebomb = 249) + prices = list(/obj/item/storage/belt/security/webbing = 999, /obj/item/clothing/mask/gas/sechailer/tactical = 299, /obj/item/flashlight/sectaclight = 299, /obj/item/grenade/smokebomb = 249) refill_canister = /obj/item/vending_refill/security /obj/machinery/vending/security/training @@ -4131,6 +4144,7 @@ /obj/item/gun/energy/xray = 2, /obj/item/gun/energy/immolator/multi = 2, /obj/item/gun/energy/gun/nuclear = 3, + /obj/item/gun/energy/gun/minigun = 1, /obj/item/storage/lockbox/t4 = 3, /obj/item/grenade/smokebomb = 3, /obj/item/grenade/frag = 4 diff --git a/code/game/mecha/combat/durand.dm b/code/game/mecha/combat/durand.dm index 8f1a832df19..7d2e383a764 100644 --- a/code/game/mecha/combat/durand.dm +++ b/code/game/mecha/combat/durand.dm @@ -47,6 +47,8 @@ force = 40 wreckage = /obj/structure/mecha_wreckage/durand/old + mech_type = MECH_TYPE_OLD_DURAND + /obj/mecha/combat/durand/rover desc = "Combat exosuit, developed by syndicate from the Durand Mk. II by scraping unnecessary things, and adding some of their tech. Much more protected from any Nanotrasen hazards." name = "Rover" diff --git a/code/game/mecha/combat/gygax.dm b/code/game/mecha/combat/gygax.dm index 178b39e1fbf..2ef30b835dc 100644 --- a/code/game/mecha/combat/gygax.dm +++ b/code/game/mecha/combat/gygax.dm @@ -87,6 +87,8 @@ destruction_sleep_duration = 2 SECONDS strafe_allowed = TRUE + mech_type = MECH_TYPE_DARK_GYGAX + /obj/mecha/combat/gygax/dark/GrantActions(mob/living/user, human_occupant = 0) . = ..() thrusters_action.Grant(user, src) diff --git a/code/game/mecha/equipment/mecha_equipment.dm b/code/game/mecha/equipment/mecha_equipment.dm index cde0a1a6108..83474b9b002 100644 --- a/code/game/mecha/equipment/mecha_equipment.dm +++ b/code/game/mecha/equipment/mecha_equipment.dm @@ -199,10 +199,14 @@ /obj/item/mecha_parts/mecha_equipment/proc/remove_targeted_action() if(!selectable) return + if(chassis.module_actions[src]) var/datum/action/innate/mecha/module_action = chassis.module_actions[src] module_action.Remove(chassis.occupant) +/obj/item/mecha_parts/mecha_equipment/proc/handle_occupant_exit() + return + /obj/item/mecha_parts/mecha_equipment/Topic(href,href_list) if(href_list["detach"]) detach() diff --git a/code/game/mecha/equipment/tools/medical_tools.dm b/code/game/mecha/equipment/tools/medical_tools.dm index 4f901f483a6..b1460458474 100644 --- a/code/game/mecha/equipment/tools/medical_tools.dm +++ b/code/game/mecha/equipment/tools/medical_tools.dm @@ -639,3 +639,57 @@ if(M.equipment.len < M.max_equip) return TRUE return FALSE + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun + name = "Medical Beamgun" + ru_names = list( + NOMINATIVE = "Медицинская Лучпушка", + GENITIVE = "Медицинской Лучпушки", + DATIVE = "Медицинской Лучпушке", + ACCUSATIVE = "Медицинскую Лучпушку", + INSTRUMENTAL = "Медицинской Лучпушкой", + PREPOSITIONAL = "Медицинская Лучпушке" + ) + desc = "Передает целебные наниты своим сфокусированным лучом прямо из вашего уютного меха. Не скрещивайте лучи!" + icon_state = "mech_beamgun" + origin_tech = "bluespace=6;biotech=6;powerstorage=6" + equip_cooldown = 1.5 SECONDS + energy_drain = 50 + range = MECHA_MELEE | MECHA_RANGED + var/obj/item/gun/medbeam/mech/mbeam + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/Initialize(mapload) + . = ..() + mbeam = new(src) + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/Destroy(force) + QDEL_NULL(mbeam) + return ..() + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/process() + . = ..() + + if(.) + return TRUE + + if(!chassis.use_power(energy_drain)) + set_ready_state(TRUE) + log_message("Deactivated.") + occupant_message("[src] deactivated - no power.") + return TRUE + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/action(mob/target) + if(!mbeam.process_fire(target, loc)) + STOP_PROCESSING(SSobj, src) + return + + START_PROCESSING(SSobj, src) + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/detach() + STOP_PROCESSING(SSobj, src) + mbeam.LoseTarget() + return ..() + +/obj/item/mecha_parts/mecha_equipment/medical/beamgun/handle_occupant_exit() + . = ..() + mbeam.LoseTarget() diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 1a2dae7b1e0..7fde2801d21 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -155,7 +155,7 @@ * Attempts to build the first item in the queue. */ /obj/machinery/mecha_part_fabricator/proc/process_queue() - if(!processing_queue || being_built || !length(build_queue)) + if(!processing_queue || being_built || !length(build_queue) || !is_operational()) return var/datum/design/D = build_queue[1] if(build_design(D)) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 77bf40091d7..6be7ba7944c 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -734,7 +734,8 @@ /obj/mecha/blob_act(obj/structure/blob/B) log_message("Attack by blob. Attacker - [B].") - take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) + B?.overmind?.blobstrain?.attack_mech(src) + take_damage(30, BRUTE, MELEE, 0, get_dir(src, B)) /obj/mecha/attack_tk() return @@ -928,11 +929,17 @@ if(!user.drop_transfer_item_to_loc(paintkit, src)) return ..() user.visible_message(span_notice("[user] opens [paintkit] and spends some quality time customising [name].")) - if(paintkit.new_prefix) - initial_icon = "[paintkit.new_prefix][initial_icon]" + + var/list/icon_states = paintkit.icon_states + var/transformed_mech_type = "[mech_type]" + if(transformed_mech_type in icon_states) + initial_icon = icon_states[transformed_mech_type] else initial_icon = paintkit.new_icon - name = paintkit.new_name + if(paintkit.name_prefix) + name = "[paintkit.name_prefix] [name]" + else + name = paintkit.new_name desc = paintkit.new_desc update_icon(UPDATE_ICON_STATE) qdel(paintkit) @@ -1405,37 +1412,50 @@ /obj/mecha/proc/go_out(forced, atom/newloc = loc) if(!occupant) return + + for(var/obj/item/mecha_parts/mecha_equipment/equipment_mod in equipment) + equipment_mod.handle_occupant_exit() + var/atom/movable/mob_container + occupant.clear_alert("charge") occupant.clear_alert("locked") occupant.clear_alert("mech damage") occupant.clear_alert("mechaport") occupant.clear_alert("mechaport_d") + if(occupant && occupant.client) occupant.client.mouse_pointer_icon = initial(occupant.client.mouse_pointer_icon) + if(ishuman(occupant)) mob_container = occupant RemoveActions(occupant, human_occupant = 1) + else if(isbrain(occupant)) var/mob/living/carbon/brain/brain = occupant RemoveActions(brain) mob_container = brain.container + else if(isAI(occupant)) var/mob/living/silicon/ai/AI = occupant //stop listening to this signal, as the static update is now handled by the eyeobj's setLoc AI.eyeobj?.UnregisterSignal(src, COMSIG_MOVABLE_MOVED) AI.eyeobj?.forceMove(newloc) //kick the eye out as well + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. RemoveActions(occupant) if(!istype(newloc, /obj/item/aicard)) occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. occupant = null + return + else if(!AI.linked_core || QDELETED(AI.linked_core)) to_chat(AI, span_userdanger("Inactive core destroyed. Unable to return.")) AI.linked_core = null return + to_chat(AI, span_notice("Returning to core...")) AI.controlled_mech = null AI.remote_control = null @@ -1443,10 +1463,13 @@ mob_container = AI newloc = get_turf(AI.linked_core) qdel(AI.linked_core) + else return + var/mob/living/L = occupant occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container log_message("[mob_container] moved out.") L << browse(null, "window=exosuit") @@ -1456,12 +1479,15 @@ if(mmi.brainmob) L.forceMove(mmi) L.reset_perspective() + mmi.mecha = null mmi.update_icon() + if(istype(mmi, /obj/item/mmi/robotic_brain)) var/obj/item/mmi/robotic_brain/R = mmi if(R.imprinted_master) to_chat(L, span_notice("Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.")) + update_icon(UPDATE_ICON_STATE) dir = dir_in diff --git a/code/game/mecha/paintkits.dm b/code/game/mecha/paintkits.dm index 316fd2ae210..ce26332531d 100644 --- a/code/game/mecha/paintkits.dm +++ b/code/game/mecha/paintkits.dm @@ -4,14 +4,19 @@ icon = 'icons/obj/paintkit.dmi' icon_state = "paintkit" //What sprite will your paintkit use? - /// if there it is, instead of replacing initial_icon, will just add this before icon_state - var/new_prefix = FALSE var/new_name = "mech" //What is the variant called? var/new_desc = "A mech." //How is the new mech described? var/new_icon = "ripley" //What base icon will the new mech use? var/removable = null //Can the kit be removed? var/allowed_types = NONE //Types of mech that the kit will work on. + /// if there it is, instead of replacing initial_icon, will just add this before icon_state + var/new_prefix + /// if there it is, instead of replacing name, will just add this before name + var/name_prefix + /// if it contains an icon_state for a specific mech type, then instead of adding a prefix or replacing it with a general paintkit icon, take the initial_icon from here + var/list/icon_states = list() + //If you want to add new paintkit, grab a paintkit sprite from: "icons/obj/paintkit.dmi" or make a new one //Then throw the sprites of the new mecha skin to the "icons/obj/mecha/mecha.dmi and add a new object below" @@ -354,8 +359,26 @@ desc = "Набор, позволяющий вам переделать многие экзокостюмы в их более шахтерский аналог! По крайней мере, расцветкой." new_name = "Ashed Mech" - new_prefix = "ashed" - allowed_types = MECH_TYPE_RIPLEY|MECH_TYPE_GYGAX|MECH_TYPE_DURAND|MECH_TYPE_PHAZON|MECH_TYPE_LOCKER + name_prefix = "Ashed" + allowed_types = MECH_TYPE_RIPLEY|MECH_TYPE_GYGAX|MECH_TYPE_DURAND|MECH_TYPE_PHAZON + +/obj/item/paintkit/ashed/Initialize(mapload) + . = ..() + icon_states["[MECH_TYPE_RIPLEY]"] = "ashedripley" + icon_states["[MECH_TYPE_GYGAX]"] = "ashedgygax" + icon_states["[MECH_TYPE_DURAND]"] = "asheddurand" + icon_states["[MECH_TYPE_PHAZON]"]= "ashedphazon" + + +/obj/item/paintkit/lockermech_ashed + name = "Ashed customisation kit" + icon_state = "paintkit_ash" + desc = "Набор, позволяющий вам переделать шкафомех в его более шахтерский аналог! По крайней мере, расцветкой." + + name_prefix = "Ashed" + new_icon = "ashedlockermech" + allowed_types = MECH_TYPE_LOCKER + // Universal paintkit @@ -397,11 +420,17 @@ user.visible_message(span_notice("[user] opens [src] and customises [mech.name].")) var/obj/item/paintkit/chosen_kit = choice - if(chosen_kit.new_prefix) - mech.initial_icon = "[chosen_kit.new_prefix][initial(mech.initial_icon)]" //weird but ok + var/mech_type = "[mech.mech_type]" + var/list/icon_states = chosen_kit.icon_states + + if(mech_type in icon_states) + mech.initial_icon = icon_states[mech_type] else mech.initial_icon = chosen_kit.new_icon - mech.name = chosen_kit.new_name + if(chosen_kit.name_prefix) + mech.name = "[chosen_kit.name_prefix] [name]" + else + mech.name = chosen_kit.new_name mech.desc = chosen_kit.new_desc mech.update_icon(UPDATE_ICON_STATE) diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index a288263f7a4..c8952e664e5 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -286,3 +286,21 @@ /obj/effect/decal/cleanable/ashrune/is_cleanable() return FALSE + +/obj/effect/decal/cleanable/devil + name = "Sinister rune" + desc = "Безобразно выглядящая руна, писанная кровью." + + icon = 'icons/effects/crayondecal.dmi' + icon_state = "rune6" + color = "#661b1b" + + anchored = TRUE + mergeable_decal = FALSE + mouse_opacity = MOUSE_OPACITY_ICON + + var/datum/antagonist/devil/devil + +/obj/effect/decal/cleanable/devil/update_desc() + . = ..() + desc = "[initial(desc)][devil ? " На руне видна подпись: [devil.info?.truename]." : null]" diff --git a/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm new file mode 100644 index 00000000000..317b56ce6a8 --- /dev/null +++ b/code/game/objects/effects/effect_system/fluid_spread/_fluid_spread.dm @@ -0,0 +1,156 @@ +///////////////////////////////////////////// +//// SMOKE SYSTEMS +///////////////////////////////////////////// + +/** + * A group of fluid objects. + */ +/datum/fluid_group + /// The set of fluid objects currently in this group. + var/list/nodes + /// The number of fluid object that this group wants to have contained. + var/target_size + /// The total number of fluid objects that have ever been in this group. + var/total_size = 0 + +/datum/fluid_group/New(target_size = 0) + . = ..() + src.nodes = list() + src.target_size = target_size + +/datum/fluid_group/Destroy(force) + QDEL_LAZYLIST(nodes) + return ..() + +/** + * Adds a fluid node to this fluid group. + * + * Is a noop if the node is already in the group. + * Removes the node from any other fluid groups it is in. + * Syncs the group of the node with the group it is being added to (this one). + * Increments the total size of the fluid group. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The fluid node that is going to be added to this group. + * + * Returns: + * - [TRUE]: If the node to be added is in this group by the end of the proc. + * - [FALSE]: Otherwise. + */ +/datum/fluid_group/proc/add_node(obj/effect/particle_effect/fluid/node) + if(!istype(node)) + CRASH("Attempted to add non-fluid node [isnull(node) ? "NULL" : node] to a fluid group.") + if(QDELING(node)) + CRASH("Attempted to add qdeling node to a fluid group") + + if(node.group) + if(node.group == src) + return TRUE + if(!node.group.remove_node(node)) + return FALSE + + nodes += node + node.group = src + total_size++ + return TRUE + + +/** + * Removes a fluid node from this fluid group. + * + * Is a noop if the node is not in this group. + * Nulls the nodes fluid group ref to sync it with its new state. + * DOES NOT decrement the total size of the fluid group. + * + * Arguments: + * - [node][/obj/effect/particle_effect/fluid]: The fluid node that is going to be removed from this group. + * + * Returns: + * - [TRUE]: If the node to be removed is not in the group by the end of the proc. + */ +/datum/fluid_group/proc/remove_node(obj/effect/particle_effect/fluid/node) + if(node.group != src) + return TRUE + + nodes -= node + node.group = null + return TRUE // Note: does not decrement total size since we don't want the group to expand again when it begins to dissipate or it will never stop. + + +/** + * A particle effect that belongs to a fluid group. + */ +/obj/effect/particle_effect/fluid + name = "fluid" + /// The fluid group that this particle effect belongs to. + var/datum/fluid_group/group + /// What SSfluid bucket this particle effect is currently in. + var/tmp/effect_bucket + /// The index of the fluid spread bucket this is being spread in. + var/tmp/spread_bucket + +/obj/effect/particle_effect/fluid/Initialize(mapload, datum/fluid_group/group, obj/effect/particle_effect/fluid/source) + . = ..() + if(!group) + group = source?.group || new + group.add_node(src) + source?.transfer_fingerprints_to(src) + +/obj/effect/particle_effect/fluid/Destroy() + group.remove_node(src) + return ..() + +/** + * Attempts to spread this fluid node to wherever it can spread. + * + * Exact results vary by subtype implementation. + */ +/obj/effect/particle_effect/fluid/proc/spread() + CRASH("The base fluid spread proc is not implemented and should not be called. You called it.") + + +/** + * A factory which produces fluid groups. + */ +/datum/effect_system/fluid_spread + effect_type = /obj/effect/particle_effect/fluid + /// The amount of smoke to produce. + var/amount = 10 + +/datum/effect_system/fluid_spread/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location, ...) + src.holder = holder + src.location = location + src.amount = amount + +/datum/effect_system/fluid_spread/start(log = FALSE) + var/location = src.location || get_turf(holder) + var/obj/effect/particle_effect/fluid/flood = new effect_type(location, new /datum/fluid_group(amount)) + if(log) // Smoke is used as an aesthetic effect in a tonne of places and we don't want, say, a broken secway spamming admin chat. + help_out_the_admins(flood, holder, location) + flood.spread() + +/** + * Handles logging the beginning of a fluid flood. + * + * Arguments: + * - [flood][/obj/effect/particle_effect/fluid]: The first cell of the fluid flood. + * - [holder][/atom]: What the flood originated from. + * - [location][/atom]: Where the flood originated. + */ +/datum/effect_system/fluid_spread/proc/help_out_the_admins(obj/effect/particle_effect/fluid/flood, atom/holder, atom/location) + var/source_msg + var/blame_msg + if(holder) + holder.transfer_fingerprints_to(flood) // This is important. If this doesn't exist thermobarics are annoying to adjudicate. + + source_msg = "from inside of [ismob(holder) ? ADMIN_LOOKUPFLW(holder) : ADMIN_VERBOSEJMP(holder)]" + var/lastkey = holder.fingerprintslast + if(lastkey) + var/mob/scapegoat = get_mob_by_key(lastkey) + blame_msg = " last touched by [ADMIN_LOOKUPFLW(scapegoat)]" + else + blame_msg = " with no known fingerprints" + else + source_msg = "with no known source" + message_admins("\A [flood] flood started at [ADMIN_VERBOSEJMP(location)] [source_msg][blame_msg].") + log_game("\A [flood] flood started at [location || "nonexistant location"] [holder ? "from [holder] last touched by [holder || "N/A"]" : "with no known source"].") diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm new file mode 100644 index 00000000000..252b2a8928c --- /dev/null +++ b/code/game/objects/effects/effect_system/fluid_spread/effects_smoke.dm @@ -0,0 +1,463 @@ +/** + * A fluid which spreads through the air affecting every mob it engulfs. + */ +/obj/effect/particle_effect/fluid/smoke + name = "smoke" + icon = 'icons/effects/96x96.dmi' + icon_state = "smoke" + pixel_x = -32 + pixel_y = -32 + opacity = TRUE + plane = ABOVE_GAME_PLANE + layer = FLY_LAYER + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + animate_movement = FALSE + /// How long the smoke sticks around before it dissipates. + var/lifetime = 10 SECONDS + /// Makes the smoke react to changes on/of its turf. + var/static/loc_connections = list( + COMSIG_TURF_CALCULATED_ADJACENT_ATMOS = PROC_REF(react_to_atmos_adjacency_changes) + ) + +/obj/effect/particle_effect/fluid/smoke/Initialize(mapload, datum/fluid_group/group, ...) + . = ..() + create_reagents(1000) + setDir(pick(GLOB.cardinal)) + AddElement(/datum/element/connect_loc, loc_connections) + SSsmoke.start_processing(src) + +/obj/effect/particle_effect/fluid/smoke/Destroy() + SSsmoke.stop_processing(src) + if(spread_bucket) + SSsmoke.cancel_spread(src) + return ..() + + +/** + * Makes the smoke fade out and then deletes it. + */ +/obj/effect/particle_effect/fluid/smoke/proc/kill_smoke() + SSsmoke.stop_processing(src) + if(spread_bucket) + SSsmoke.cancel_spread(src) + INVOKE_ASYNC(src, PROC_REF(fade_out)) + QDEL_IN(src, 1 SECONDS) + +/** + * Animates the smoke gradually fading out of visibility. + * Also makes the smoke turf transparent as it passes 160 alpha. + * + * Arguments: + * - frames = 0.8 [SECONDS]: The amount of time the smoke should fade out over. + */ +/obj/effect/particle_effect/fluid/smoke/proc/fade_out(frames = 0.8 SECONDS) + if(alpha == 0) //Handle already transparent case + if(opacity) + set_opacity(FALSE) + return + + if(frames == 0) + set_opacity(FALSE) + alpha = 0 + return + + var/time_to_transparency = round(((alpha - 160) / alpha) * frames) + if(time_to_transparency >= 1) + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, set_opacity), FALSE), time_to_transparency) + else + set_opacity(FALSE) + animate(src, time = frames, alpha = 0) + + +/obj/effect/particle_effect/fluid/smoke/spread(seconds_per_tick = 0.1 SECONDS) + if(group.total_size > group.target_size) + return + var/turf/t_loc = get_turf(src) + if(!t_loc) + return + + for(var/turf/spread_turf in t_loc.GetAtmosAdjacentTurfs()) + if(group.total_size > group.target_size) + break + if(locate(type) in spread_turf) + continue // Don't spread smoke where there's already smoke! + for(var/mob/living/smoker in spread_turf) + smoke_mob(smoker, seconds_per_tick) + + var/obj/effect/particle_effect/fluid/smoke/spread_smoke = new type(spread_turf, group, src) + reagents.copy_to(spread_smoke, reagents.total_volume) + spread_smoke.add_atom_colour(color, FIXED_COLOUR_PRIORITY) + spread_smoke.lifetime = lifetime + + // the smoke spreads rapidly, but not instantly + SSfoam.queue_spread(spread_smoke) + + +/obj/effect/particle_effect/fluid/smoke/process(seconds_per_tick) + lifetime -= seconds_per_tick SECONDS + if(lifetime <= 0) + kill_smoke() + return FALSE + for(var/mob/living/smoker in loc) // In case smoke somehow winds up in a locker or something this should still behave sanely. + smoke_mob(smoker, seconds_per_tick) + return TRUE + +/** + * Handles the effects of this smoke on any mobs it comes into contact with. + * + * Arguments: + * - [smoker][/mob/living/carbon]: The mob that is being exposed to this smoke. + * - seconds_per_tick: A scaling factor for the effects this has. Primarily based off of tick rate to normalize effects to units of rate/sec. + * + * Returns whether the smoke effect was applied to the mob. + */ +/obj/effect/particle_effect/fluid/smoke/proc/smoke_mob(mob/living/carbon/smoker, seconds_per_tick) + if(!istype(smoker)) + return FALSE + if(lifetime < 1) + return FALSE + if(smoker.internal != null || smoker.can_breathe_gas()) + return FALSE + if(smoker.smoke_delay) + return FALSE + + smoker.smoke_delay = TRUE + addtimer(VARSET_CALLBACK(smoker, smoke_delay, FALSE), 1 SECONDS) + return TRUE + +/** + * Makes the smoke react to nearby opening/closing airlocks and the like. + * Makes it possible for smoke to spread through airlocks that open after the edge of the smoke cloud has already spread past them. + * + * Arguments: + * - [source][/turf]: The turf that has been touched by an atmos adjacency change. + */ +/obj/effect/particle_effect/fluid/smoke/proc/react_to_atmos_adjacency_changes(turf/source) + SIGNAL_HANDLER + if(!group) + return NONE + if(spread_bucket) + return NONE + SSsmoke.queue_spread(src) + +/// A factory which produces clouds of smoke. +/datum/effect_system/fluid_spread/smoke + effect_type = /obj/effect/particle_effect/fluid/smoke + +///////////////////////////////////////////// +// Transparent smoke +///////////////////////////////////////////// + +/// Same as the base type, but the smoke produced is not opaque +/datum/effect_system/fluid_spread/smoke/transparent + effect_type = /obj/effect/particle_effect/fluid/smoke/transparent + +/// Same as the base type, but is not opaque. +/obj/effect/particle_effect/fluid/smoke/transparent + opacity = FALSE + +/** + * A helper proc used to spawn small puffs of smoke. + * + * Arguments: + * - range: The amount of smoke to produce as number of steps from origin covered. + * - amount: The amount of smoke to produce as the total desired coverage area. Autofilled from the range arg if not set. + * - location: Where to produce the smoke cloud. + * - smoke_type: The smoke typepath to spawn. + */ +/proc/do_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, smoke_type = /obj/effect/particle_effect/fluid/smoke, log = FALSE) + var/datum/effect_system/fluid_spread/smoke/smoke = new + smoke.effect_type = smoke_type + smoke.set_up(amount = amount, holder = holder, location = location) + smoke.start(log = log) + +///////////////////////////////////////////// +// Quick smoke +///////////////////////////////////////////// + +/// Smoke that dissipates as quickly as possible. +/obj/effect/particle_effect/fluid/smoke/quick + lifetime = 1 SECONDS + opacity = FALSE + +/// A factory which produces smoke that dissipates as quickly as possible. +/datum/effect_system/fluid_spread/smoke/quick + effect_type = /obj/effect/particle_effect/fluid/smoke/quick + +///////////////////////////////////////////// +// Bad smoke +///////////////////////////////////////////// + +/// Smoke that makes you cough and reduces the power of lasers. +/obj/effect/particle_effect/fluid/smoke/bad + lifetime = 16 SECONDS + +/obj/effect/particle_effect/fluid/smoke/bad/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/effect/particle_effect/fluid/smoke/bad/smoke_mob(mob/living/carbon/smoker) + . = ..() + if(!.) + return + + smoker.drop_from_hands() + smoker.adjustOxyLoss(1) + smoker.emote("cough") + +/** + * Reduces the power of any beam projectile that passes through the smoke. + * + * Arguments: + * - [source][/datum]: The location that has just been entered. If [/datum/element/connect_loc] is working this is [src.loc][/atom/var/loc]. + * - [arrived][/atom/movable]: The atom that has just entered the source location. + * - [old_loc][/atom]: The location the entering atom just was in. + * - [old_locs][/list/atom]: The set of locations the entering atom was just in. + */ +/obj/effect/particle_effect/fluid/smoke/bad/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) + SIGNAL_HANDLER + if(istype(arrived, /obj/item/projectile/beam)) + var/obj/item/projectile/beam/beam = arrived + beam.damage *= 0.5 + +/// A factory which produces smoke that makes you cough. +/datum/effect_system/fluid_spread/smoke/bad + effect_type = /obj/effect/particle_effect/fluid/smoke/bad + +///////////////////////////////////////////// +// Bad Smoke (But Green (and Black)) +///////////////////////////////////////////// + +/// Green smoke that makes you cough. +/obj/effect/particle_effect/fluid/smoke/bad/green + name = "green smoke" + color = COLOR_LIME + opacity = FALSE + +/// A factory which produces green smoke that makes you cough. +/datum/effect_system/fluid_spread/smoke/bad/green + effect_type = /obj/effect/particle_effect/fluid/smoke/bad/green + +/// Black smoke that makes you cough. (Actually dark grey) +/obj/effect/particle_effect/fluid/smoke/bad/black + name = "black smoke" + color = "#383838" + opacity = FALSE + +/// A factory which produces black smoke that makes you cough. +/datum/effect_system/fluid_spread/smoke/bad/black + effect_type = /obj/effect/particle_effect/fluid/smoke/bad/black + +///////////////////////////////////////////// +// Nanofrost smoke +///////////////////////////////////////////// + +/// Light blue, transparent smoke which is usually paired with a blast that chills every turf in the area. +/obj/effect/particle_effect/fluid/smoke/freezing + name = "nanofrost smoke" + color = "#B2FFFF" + opacity = FALSE + +/// A factory which produces light blue, transparent smoke and a blast that chills every turf in the area. +/datum/effect_system/fluid_spread/smoke/freezing + effect_type = /obj/effect/particle_effect/fluid/smoke/freezing + /// The radius in which to chill every open turf. + var/blast = 0 + /// The temperature to set the turfs air temperature to. + var/temperature = 2 + /// Whether to weld every vent and air scrubber in the affected area shut. + var/weldvents = TRUE + /// Whether to make sure each affected turf is actually within range before cooling it. + var/distcheck = TRUE + +/** + * Chills an open turf. + * + * Forces the air temperature to a specific value. + * Transmutes all of the plasma in the air into nitrogen. + * Extinguishes all fires and burning objects/mobs in the turf. + * May freeze all vents and vent scrubbers shut. + * + * Arguments: + * - [chilly][/turf/open]: The open turf to chill + */ +/datum/effect_system/fluid_spread/smoke/freezing/proc/Chilled(turf/simulated/chilly) + if(!istype(chilly)) + return + + if(chilly.air) + var/datum/gas_mixture/air = chilly.air + if(!distcheck || get_dist(location, chilly) < blast) // Otherwise we'll get silliness like people using Nanofrost to kill people through walls with cold air + air.temperature = temperature + + if(air.toxins) + air.nitrogen += air.toxins + air.toxins = 0 + + for(var/obj/effect/hotspot/fire in chilly) + qdel(fire) + chilly.air_update_turf(FALSE, FALSE) + + if(weldvents) + for(var/obj/machinery/atmospherics/unary/comp in chilly) + if(!isnull(comp.welded) && !comp.welded) //must be an unwelded vent pump or vent scrubber. + comp.welded = TRUE + comp.update_appearance() + comp.visible_message(span_danger("[comp] is frozen shut!")) + + // Extinguishes everything in the turf + for(var/mob/living/potential_tinder in chilly) + potential_tinder.ExtinguishMob() + for(var/obj/item/potential_tinder in chilly) + potential_tinder.extinguish() + +/datum/effect_system/fluid_spread/smoke/freezing/set_up(range = 5, amount = DIAMOND_AREA(range), atom/holder, atom/location, blast_radius = 0) + . = ..() + blast = blast_radius + +/datum/effect_system/fluid_spread/smoke/freezing/start(log = FALSE) + if(blast) + for(var/turf/T in RANGE_TURFS(blast, location)) + Chilled(T) + return ..() + +/// A variant of the base freezing smoke formerly used by the vent decontamination event. +/datum/effect_system/fluid_spread/smoke/freezing/decon + temperature = T20C + distcheck = FALSE + weldvents = FALSE + + +///////////////////////////////////////////// +// Sleep smoke +///////////////////////////////////////////// + +/// Smoke which knocks you out if you breathe it in. +/obj/effect/particle_effect/fluid/smoke/sleeping + color = "#9C3636" + lifetime = 20 SECONDS + +/obj/effect/particle_effect/fluid/smoke/sleeping/smoke_mob(mob/living/carbon/smoker, seconds_per_tick) + if(..()) + smoker.Sleeping(20 SECONDS) + smoker.emote("cough") + return TRUE + +/// A factory which produces sleeping smoke. +/datum/effect_system/fluid_spread/smoke/sleeping + effect_type = /obj/effect/particle_effect/fluid/smoke/sleeping + +///////////////////////////////////////////// +// Chem smoke +///////////////////////////////////////////// + +/** + * Smoke which contains reagents which it applies to everything it comes into contact with. + */ +/obj/effect/particle_effect/fluid/smoke/chem + lifetime = 20 SECONDS + +/obj/effect/particle_effect/fluid/smoke/chem/process(seconds_per_tick) + . = ..() + if(!.) + return + + var/turf/location = get_turf(src) + var/fraction = (seconds_per_tick SECONDS) / initial(lifetime) + for(var/atom/movable/thing as anything in location) + if(thing == src) + continue + reagents.reaction(thing, REAGENT_TOUCH, fraction) + + reagents.reaction(location, REAGENT_TOUCH, fraction) + return TRUE + +/obj/effect/particle_effect/fluid/smoke/chem/smoke_mob(mob/living/carbon/smoker, seconds_per_tick) + if(lifetime < 1) + return FALSE + if(!istype(smoker)) + return FALSE + if(smoker.internal != null || !smoker.can_breathe_gas()) + return FALSE + + var/fraction = (seconds_per_tick SECONDS) / initial(lifetime) + reagents.copy_to(smoker, reagents.total_volume, fraction) + reagents.reaction(smoker, REAGENT_INGEST, fraction) + return TRUE + +/// Helper to quickly create a cloud of reagent smoke +/proc/do_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, smoke_type = /datum/effect_system/fluid_spread/smoke/chem, reagent_volume = 10, log = FALSE) + var/datum/reagents/smoke_reagents = new/datum/reagents(reagent_volume) + smoke_reagents.add_reagent(reagent_type, reagent_volume) + + var/datum/effect_system/fluid_spread/smoke/chem/smoke = new smoke_type + smoke.attach(location) + smoke.set_up(amount = amount, holder = holder, location = location, carry = smoke_reagents, silent = TRUE) + smoke.start(log = log) + + +/// A factory which produces clouds of chemical bearing smoke. +/datum/effect_system/fluid_spread/smoke/chem + /// Evil evil hack so we have something to "hold" our reagents + var/datum/reagents/chemholder + effect_type = /obj/effect/particle_effect/fluid/smoke/chem + +/datum/effect_system/fluid_spread/smoke/chem/New() + ..() + chemholder = new(1000) + +/datum/effect_system/fluid_spread/smoke/chem/Destroy() + QDEL_NULL(chemholder) + return ..() + + +/datum/effect_system/fluid_spread/smoke/chem/set_up(range = 1, amount = DIAMOND_AREA(range), atom/holder, atom/location = null, datum/reagents/carry = null, silent = FALSE) + . = ..() + carry?.copy_to(chemholder, carry.total_volume) + + if(silent) + return + + var/list/contained_reagents = list() + for(var/datum/reagent/reagent as anything in chemholder.reagent_list) + contained_reagents += "[reagent.volume]u [reagent]" + + var/where = "[AREACOORD(location)]" + var/contained = length(contained_reagents) ? "\[[contained_reagents.Join(", ")]\] @ [chemholder.chem_temp]K" : null + if(carry.my_atom?.fingerprintslast) //Some reagents don't have a my_atom in some cases + var/mob/M = get_mob_by_key(carry.my_atom.fingerprintslast) + var/more = "" + if(M) + more = "[ADMIN_LOOKUPFLW(M)] " + message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. Key: [more ? more : carry.my_atom.fingerprintslast].") + log_game("A chemical smoke reaction has taken place in ([where])[contained]. Last touched by [carry.my_atom.fingerprintslast].") + else + message_admins("Smoke: ([ADMIN_VERBOSEJMP(location)])[contained]. No associated key.") + log_game("A chemical smoke reaction has taken place in ([where])[contained]. No associated key.") + +/datum/effect_system/fluid_spread/smoke/chem/start(log = FALSE) + var/start_loc = holder ? get_turf(holder) : src.location + var/mixcolor = mix_color_from_reagents(chemholder.reagent_list) + var/obj/effect/particle_effect/fluid/smoke/chem/smoke = new effect_type(start_loc, new /datum/fluid_group(amount)) + chemholder.copy_to(smoke, chemholder.total_volume) + + if(mixcolor) + smoke.add_atom_colour(mixcolor, FIXED_COLOUR_PRIORITY) // give the smoke color, if it has any to begin with + if(log) + help_out_the_admins(smoke, holder, location) + smoke.spread() // Making the smoke spread immediately. + +/** + * A version of chemical smoke with a very short lifespan. + */ +/obj/effect/particle_effect/fluid/smoke/chem/quick + lifetime = 6 SECONDS + opacity = FALSE + alpha = 150 + +/datum/effect_system/fluid_spread/smoke/chem/quick + effect_type = /obj/effect/particle_effect/fluid/smoke/chem/quick diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index bf3bd197ca7..640e4356085 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -471,10 +471,6 @@ name = JOB_TITLE_LIBRARIAN icon_state = "Librarian" -/obj/effect/landmark/start/barber - name = JOB_TITLE_BARBER - icon_state = "Barber" - /obj/effect/landmark/start/chaplain name = JOB_TITLE_CHAPLAIN icon_state = "Chap" diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index 707a1b6aa34..4345c39f94d 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -91,6 +91,10 @@ icon_state = "pod_mess" +/obj/effect/supplypod_selector + icon_state = "supplypod_selector" + layer = FLY_LAYER + //Makes a tile fully lit no matter what /obj/effect/fullbright icon = 'icons/effects/alphacolors.dmi' diff --git a/code/game/objects/effects/particle_holder.dm b/code/game/objects/effects/particle_holder.dm new file mode 100644 index 00000000000..3e91d2e605a --- /dev/null +++ b/code/game/objects/effects/particle_holder.dm @@ -0,0 +1,70 @@ +///objects can only have one particle on them at a time, so we use these abstract effects to hold and display the effects. You know, so multiple particle effects can exist at once. +///also because some objects do not display particles due to how their visuals are built +/obj/effect/abstract/particle_holder + name = "particle holder" + desc = "How are you reading this? Please make a bug report :)" + appearance_flags = KEEP_APART|KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE //movable appearance_flags plus KEEP_APART and KEEP_TOGETHER + vis_flags = VIS_INHERIT_PLANE + layer = ABOVE_ALL_MOB_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = 0 + anchored = TRUE + /// Holds info about how this particle emitter works + /// See \code\__DEFINES\particles.dm + var/particle_flags = NONE + + var/atom/parent + +/obj/effect/abstract/particle_holder/Initialize(mapload, particle_path = /particles/droplets, particle_flags = NONE) + . = ..() + if(!loc) + stack_trace("particle holder was created with no loc!") + return INITIALIZE_HINT_QDEL + // We nullspace ourselves because some objects use their contents (e.g. storage) and some items may drop everything in their contents on deconstruct. + parent = loc + loc = null + + // Mouse opacity can get set to opaque by some objects when placed into the object's contents (storage containers). + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + src.particle_flags = particle_flags + particles = new particle_path() + // /atom doesn't have vis_contents, /turf and /atom/movable do + var/atom/movable/lie_about_areas = parent + lie_about_areas.vis_contents += src + RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(parent_deleted)) + + if(particle_flags & PARTICLE_ATTACH_MOB) + RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + on_move(parent, null, NORTH) + +/obj/effect/abstract/particle_holder/Destroy(force) + QDEL_NULL(particles) + parent = null + return ..() + +/// Non movables don't delete contents on destroy, so we gotta do this +/obj/effect/abstract/particle_holder/proc/parent_deleted(datum/source) + SIGNAL_HANDLER + qdel(src) + +/// signal called when a parent that's been hooked into this moves +/// does a variety of checks to ensure overrides work out properly +/obj/effect/abstract/particle_holder/proc/on_move(atom/movable/attached, atom/oldloc, direction) + SIGNAL_HANDLER + + if(!(particle_flags & PARTICLE_ATTACH_MOB)) + return + + //remove old + if(ismob(oldloc)) + var/mob/particle_mob = oldloc + particle_mob.vis_contents -= src + + // If we're sitting in a mob, we want to emit from it too, for vibes and shit + if(ismob(attached.loc)) + var/mob/particle_mob = attached.loc + particle_mob.vis_contents += src + +/// Sets the particles position to the passed coordinates +/obj/effect/abstract/particle_holder/proc/set_particle_position(x = 0, y = 0, z = 0) + particles.position = list(x, y, z) diff --git a/code/game/objects/effects/particles/water.dm b/code/game/objects/effects/particles/water.dm new file mode 100644 index 00000000000..88e0ef542e3 --- /dev/null +++ b/code/game/objects/effects/particles/water.dm @@ -0,0 +1,14 @@ +// Water related particles. +/particles/droplets + icon = 'icons/effects/particles/generic.dmi' + icon_state = list("dot"=2,"drop"=1) + width = 32 + height = 36 + count = 5 + spawning = 0.2 + lifespan = 1 SECONDS + fade = 0.5 SECONDS + color = "#549EFF" + position = generator(GEN_BOX, list(-9,-9,0), list(9,18,0), NORMAL_RAND) + scale = generator(GEN_VECTOR, list(0.9,0.9), list(1.1,1.1), NORMAL_RAND) + gravity = list(0, -0.9) diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 71a661b567e..e5cc272d6ca 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -235,6 +235,13 @@ icon_state = "explosionfast" duration = 4 +/obj/effect/temp_visual/blob + name = "blob" + icon_state = "blob_attack" + alpha = 140 + randomdir = 0 + duration = 6 + /obj/effect/temp_visual/explosion/florawave icon_state = "florawave" duration = 4 diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index d88fc674794..c3585a3ae62 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -108,7 +108,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g var/list/allowed = null //suit storage stuff. var/obj/item/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers. - var/needs_permit = 0 //Used by security bots to determine if this item is safe for public use. + var/needs_permit = FALSE //Used by security bots to determine if this item is safe for public use. var/strip_delay = DEFAULT_ITEM_STRIP_DELAY var/put_on_delay = DEFAULT_ITEM_PUTON_DELAY @@ -273,10 +273,16 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g else return TRUE + /obj/item/blob_act(obj/structure/blob/B) - if(B && B.loc == loc && !QDELETED(src)) - qdel(src) + if(B && B.loc == loc && !QDELETED(src) && !(obj_flags & IGNORE_BLOB_ACT)) + obj_destruction(MELEE) +/obj/item/blob_vore_act(obj/structure/blob/special/core/voring_core) + . = ..() + if(QDELETED(src)) + return FALSE + forceMove(voring_core) /obj/item/examine(mob/user) var/size @@ -296,7 +302,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g . = ..(user, "", "It is a [size] item.") - if(user.research_scanner) //Mob has a research scanner active. + if(user.research_scanner || user.check_smart_brain()) //Mob has a research scanner active. var/msg = "*--------*
" if(origin_tech) diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 1f3b0e03897..e98a6ed5d36 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -267,7 +267,9 @@ if(!Adjacent(usr) || usr.incapacitated()) return if(href_list["color"]) - var/temp = input(usr, "Please select colour.", "Crayon colour") as color + var/temp = tgui_input_color(usr, "Please select colour.", "Crayon colour") + if(isnull(temp)) + return colour = temp update_window(usr) else @@ -303,7 +305,10 @@ if("Change Drawing") ..() if("Change Color") - colour = input(user,"Choose Color") as color + var/new_color = tgui_input_color(user, "Choose Color") + if(isnull(new_color)) + return + colour = new_color update_icon() /obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity, params) diff --git a/code/game/objects/items/devices/airlock_painter.dm b/code/game/objects/items/devices/airlock_painter.dm index 027b05d6a35..45e3597783d 100644 --- a/code/game/objects/items/devices/airlock_painter.dm +++ b/code/game/objects/items/devices/airlock_painter.dm @@ -5,6 +5,8 @@ desc = "An advanced autopainter preprogrammed with several paintjobs for airlocks. Use it on a completed airlock to change its paintjob." icon = 'icons/obj/device.dmi' icon_state = "airlock_painter" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "airlock_painter" flags = CONDUCT item_flags = NOBLUDGEON diff --git a/code/game/objects/items/devices/floor_painter.dm b/code/game/objects/items/devices/floor_painter.dm index a1d52787ece..e3716cd5dc8 100644 --- a/code/game/objects/items/devices/floor_painter.dm +++ b/code/game/objects/items/devices/floor_painter.dm @@ -4,10 +4,12 @@ name = "floor painter" icon = 'icons/obj/device.dmi' icon_state = "floor_painter" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "floor_painter" usesound = 'sound/effects/spray2.ogg' - var/floor_icon + var/floor_icon = 'icons/turf/floors.dmi' var/floor_state = "floor" var/floor_dir = SOUTH @@ -68,9 +70,7 @@ ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "FloorPainter", name) - // Disable automatic updates, because: - // 1) we are the only user of the item, and don't expect to observe external changes - // 2) generating and sending the icon each tick is a bit expensive, and creates small but noticeable lag + // Disable automatic updates, because we are the only user of the item, and don't expect to observe external changes ui.set_autoupdate(FALSE) ui.open() @@ -78,12 +78,7 @@ var/list/data = list() data["availableStyles"] = allowed_states data["selectedStyle"] = floor_state - data["selectedDir"] = dir2text(floor_dir) - - data["directionsPreview"] = list() - for(var/dir in GLOB.alldirs) - var/icon/floor_icon = icon('icons/turf/floors.dmi', floor_state, dir) - data["directionsPreview"][dir2text(dir)] = icon2base64(floor_icon) + data["selectedDir"] = floor_dir return data @@ -91,10 +86,8 @@ /obj/item/floor_painter/ui_static_data(mob/user) var/list/data = list() - data["allStylesPreview"] = list() - for (var/style in allowed_states) - var/icon/floor_icon = icon('icons/turf/floors.dmi', style, SOUTH) - data["allStylesPreview"][style] = icon2base64(floor_icon) + data["icon"] = floor_icon + data["availableStyles"] = allowed_states return data diff --git a/code/game/objects/items/devices/memorizer.dm b/code/game/objects/items/devices/memorizer.dm index d1a73f599f3..ee8716f4506 100644 --- a/code/game/objects/items/devices/memorizer.dm +++ b/code/game/objects/items/devices/memorizer.dm @@ -3,7 +3,6 @@ desc = "If you see this, you're not likely to remember it any time soon." icon = 'icons/obj/device.dmi' icon_state = "memorizer" - item_state = "nullrod" throwforce = 0 w_class = WEIGHT_CLASS_TINY throw_speed = 3 diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index 6df702d578c..ce63491f599 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -2,6 +2,8 @@ name = "pipe painter" icon = 'icons/obj/device.dmi' icon_state = "pipe_painter" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "pipe_painter" usesound = 'sound/effects/spray2.ogg' var/list/modes diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 9730118d35b..46b2b654544 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -196,11 +196,11 @@ /obj/item/camera_bug = 1, // 5 TK /obj/item/door_remote/omni/access_tuner = 1, // 30 TK /obj/item/implanter/freedom/prototype = 1, // 6.6 TK - /obj/item/aiModule/syndicate = 1, // 40 TK + /obj/item/ai_module/syndicate = 1, // 40 TK /obj/item/card/emag = 1, // 50 TK /obj/item/encryptionkey/syndicate = 1, // 0-2 TK /obj/item/encryptionkey/binary = 1, // 21 TK - /obj/item/aiModule/toyAI = 1, // 0 TK + /obj/item/ai_module/toy_ai = 1, // 0 TK /obj/item/storage/belt/military/traitor/hacker = 1, // 10 TK /obj/item/clothing/gloves/combat = 1, // 0-5 TK /obj/item/flashlight/emp = 1), // 20 TK diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index bcd658b57b4..a5b170e9725 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -382,6 +382,10 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( return FALSE if(M.is_muzzled()) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = M.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(translator) // you can't speak in radio with translator and gag + return FALSE + var/obj/item/clothing/mask/muzzle/muzzle = M.wear_mask if(muzzle.radio_mute) return FALSE diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm index 475a68404ff..689c457e535 100644 --- a/code/game/objects/items/devices/scanners/gas_analyzer.dm +++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm @@ -18,6 +18,7 @@ throw_range = 7 materials = list(MAT_METAL=30, MAT_GLASS=20) origin_tech = "magnets=1;engineering=1" + tool_behaviour = TOOL_ANALYZER var/cooldown = FALSE var/cooldown_time = 250 var/accuracy // 0 is the best accuracy. @@ -152,7 +153,7 @@ scan_target = get_turf(src) if(ANALYZER_MODE_TARGET) scan_target = target - if(!can_see(src, target, scan_range)) + if(!can_see(target, scan_range)) target_mode = ANALYZER_MODE_SURROUNDINGS scan_target = get_turf(src) if(!scan_target) @@ -189,7 +190,7 @@ /obj/item/analyzer/afterattack(atom/target, mob/user, proximity, params) . = ..() - if(!can_see(user, target, scan_range)) + if(!user.can_see(target, scan_range)) return target_mode = ANALYZER_MODE_TARGET if(target == user || target == user.loc) diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 0fd811edcde..004741b1225 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -226,3 +226,7 @@ sleep(1 SECONDS) update_icon() + +/obj/item/transfer_valve/blob_vore_act(obj/structure/blob/special/core/voring_core) + obj_destruction(MELEE) + diff --git a/code/game/objects/items/devices/whistle.dm b/code/game/objects/items/devices/whistle.dm index f90fef34e79..290ba9328f3 100644 --- a/code/game/objects/items/devices/whistle.dm +++ b/code/game/objects/items/devices/whistle.dm @@ -1,32 +1,48 @@ /obj/item/hailer name = "hailer" desc = "Used by obese officers to save their breath for running." + icon = 'icons/obj/device.dmi' icon_state = "voice0" item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) + w_class = WEIGHT_CLASS_TINY flags = CONDUCT - var/spamcheck = 0 - var/emagged = 0 + COOLDOWN_DECLARE(spamcheck) + var/emagged = FALSE /obj/item/hailer/attack_self(mob/living/carbon/user as mob) - if(spamcheck) + hail(user) + +/obj/item/hailer/proc/hail(mob/living/carbon/user) + if(!COOLDOWN_FINISHED(src, spamcheck)) return + var/sound_to_play + var/message + if(emagged) - playsound(get_turf(src), 'sound/voice/binsult.ogg', 100, 1, vary = 0)//hueheuheuheuheuheuhe - user.visible_message("[user]'s [name] gurgles, \"FUCK YOUR CUNT YOU SHIT EATING CUNT TILL YOU ARE A MASS EATING SHIT CUNT. EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS TO FUCK UP SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FROM THE DEPTHS OF SHIT\"") + sound_to_play = 'sound/voice/binsult.ogg' + message = span_warning("[user]'s [name] gurgles, \"FUCK YOUR CUNT YOU SHIT EATING CUNT TILL YOU ARE A MASS EATING SHIT CUNT. EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS TO FUCK UP SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FROM THE DEPTHS OF SHIT\"") + else - playsound(get_turf(src), 'sound/voice/halt.ogg', 100, 1, vary = 0) - user.visible_message("[user]'s [name] rasps, \"Halt! Security!\"") + sound_to_play = 'sound/voice/halt.ogg' + message = span_warning("[user]'s [name] rasps, \"Halt! Security!\"") + + if(sound_to_play) + playsound(get_turf(src), sound_to_play, 100, 1, vary = FALSE) + + if(message) + user.visible_message(message) - spamcheck = 1 - spawn(20) - spamcheck = 0 + COOLDOWN_START(src, spamcheck, 2 SECONDS) + + return /obj/item/hailer/emag_act(mob/user) if(!emagged) if(user) - to_chat(user, "You overload \the [src]'s voice synthesizer.") - emagged = 1 + to_chat(user, span_warning("You overload \the [src]'s voice synthesizer.")) + + emagged = TRUE diff --git a/code/game/objects/items/devices/window_painter.dm b/code/game/objects/items/devices/window_painter.dm index 6dae12b8c5a..b58710a50a1 100644 --- a/code/game/objects/items/devices/window_painter.dm +++ b/code/game/objects/items/devices/window_painter.dm @@ -25,7 +25,10 @@ mode = "pipette" if("Choose Color") mode = "paint" - colour = input(user,"Choose Color") as color + var/new_color = tgui_input_color(user, "Choose Color") + if(isnull(new_color)) + return + colour = new_color update_icon(UPDATE_OVERLAYS) if("Color Presets") mode = "paint" diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 747d07928ab..724ff60f546 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -127,8 +127,8 @@ robot.key = ghost.key robot.set_stat(CONSCIOUS) - GLOB.dead_mob_list -= robot //please never forget this ever kthx - GLOB.alive_mob_list += robot + robot.remove_from_dead_mob_list() //please never forget this ever kthx + robot.add_to_alive_mob_list() robot.notify_ai(ROBOT_NOTIFY_AI_CONNECTED) return TRUE diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 55be7417b5c..1f0de6790d0 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -30,6 +30,7 @@ GLOBAL_LIST_INIT(glass_recipes, list( desc = "HOLY SHEET! That is a lot of glass." singular_name = "glass sheet" icon_state = "sheet-glass" + item_state = "sheet-glass" materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) resistance_flags = ACID_PROOF diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 21f1be10e14..27876c98834 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -475,7 +475,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list( name = "mythril" desc = "A rare mineral used in construction of chitin armor." icon_state = "sheet-mythril" - item_state = "sheet-mythril" + //item_state = "sheet-mythril" singular_name = "mythril sheet" origin_tech = "materials=7" merge_type = /obj/item/stack/sheet/mineral/mythril @@ -487,7 +487,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list( /obj/item/stack/sheet/mineral/snow name = "snow" icon_state = "sheet-snow" - item_state = "sheet-snow" + //item_state = "sheet-snow" singular_name = "snow block" force = 1 throwforce = 2 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index d1eb98f258b..7cc81b6c4d8 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -282,6 +282,7 @@ GLOBAL_LIST_INIT(cloth_recipes, list( new /datum/stack_recipe("Fish bag", /obj/item/storage/bag/fish, 4), new /datum/stack_recipe("Mining satchel", /obj/item/storage/bag/ore, 4), new /datum/stack_recipe("Plant bag", /obj/item/storage/bag/plants, 4), + new /datum/stack_recipe("Money bag", /obj/item/storage/bag/money, 3), )), null, new /datum/stack_recipe("Bedsheet", /obj/item/bedsheet, 3), @@ -335,7 +336,7 @@ GLOBAL_LIST_INIT(durathread_recipes, list( desc = "A fabric sown from incredibly durable threads, known for its usefulness in armor production." singular_name = "durathread roll" icon_state = "sheet-durathread" - item_state = "sheet-cloth" + //item_state = "sheet-cloth" resistance_flags = FLAMMABLE force = 0 throwforce = 0 @@ -773,7 +774,7 @@ GLOBAL_LIST_INIT(bamboo_recipes, list( desc = "Finely cut bamboo sticks." singular_name = "cut bamboo" icon_state = "sheet-bamboo" - item_state = "sheet-bamboo" + //item_state = "sheet-bamboo" icon = 'icons/obj/items.dmi' sheettype = "bamboo" force = 10 @@ -799,7 +800,7 @@ GLOBAL_LIST_INIT(cheese_recipes, list( name = "reinforced cheese" desc = "A stack of cheese that seems sturdier than regular cheese." icon_state = "sheet-cheese" - item_state = "sheet-cheese" + //item_state = "sheet-cheese" icon = 'icons/obj/items.dmi' singular_name = "reinforced cheese block" sheettype = "cheese" @@ -831,7 +832,7 @@ GLOBAL_LIST_INIT(gingerbread_recipes, list( name = "gingerbread" desc = "A brick of gingerbread that seems sturdier than regular one." icon_state = "sheet-gingerbread" - item_state = "sheet-gingerbread" + //item_state = "sheet-gingerbread" singular_name = "gingerbread block" icon = 'icons/obj/items.dmi' sheettype = "gingerbread" diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm index a61c26097ae..5df3168aea7 100644 --- a/code/game/objects/items/stacks/sheets/sheets.dm +++ b/code/game/objects/items/stacks/sheets/sheets.dm @@ -17,3 +17,6 @@ usesound = 'sound/items/deconstruct.ogg' toolspeed = 1 var/wall_allowed = TRUE //determines if sheet can be used in wall construction or not. + + lefthand_file = 'icons/mob/inhands/sheet_lefthand.dmi' + righthand_file = 'icons/mob/inhands/sheet_righthand.dmi' diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 69fc06e726b..d5e529cb969 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -3,6 +3,8 @@ desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors." icon = 'icons/obj/tools.dmi' icon_state = "crowbar" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "crowbar" belt_icon = "pocket_crowbar" usesound = 'sound/items/crowbar.ogg' @@ -48,7 +50,7 @@ icon = 'icons/obj/abductor.dmi' usesound = 'sound/weapons/sonic_jackhammer.ogg' icon_state = "crowbar" - item_state = "alien_crowbar" + item_state = "crowbar_alien" belt_icon = "alien_crowbar" toolspeed = 0.1 origin_tech = "combat=4;engineering=4;abductor=3" diff --git a/code/game/objects/items/tools/holotool.dm b/code/game/objects/items/tools/holotool.dm index 6e3a3a52f1b..c16d1d7509f 100644 --- a/code/game/objects/items/tools/holotool.dm +++ b/code/game/objects/items/tools/holotool.dm @@ -9,6 +9,8 @@ Holotool. All instruments in one object desc = "A highly experimental holographic tool projector." icon = 'icons/obj/holotool.dmi' icon_state = "holotool" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' slot_flags = ITEM_SLOT_BELT usesound = 'sound/items/pshoom.ogg' actions_types = list(/datum/action/item_action/change_ht_color) diff --git a/code/game/objects/items/tools/multitool.dm b/code/game/objects/items/tools/multitool.dm index db562b1d1b9..afe2bc0c77f 100644 --- a/code/game/objects/items/tools/multitool.dm +++ b/code/game/objects/items/tools/multitool.dm @@ -11,6 +11,8 @@ desc = "Used for pulsing wires to test which to cut. Not recommended by doctors." icon = 'icons/obj/device.dmi' icon_state = "multitool" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' belt_icon = "multitool" flags = CONDUCT force = 5.0 diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm index 2a2254d961c..576f3ae9a15 100644 --- a/code/game/objects/items/tools/screwdriver.dm +++ b/code/game/objects/items/tools/screwdriver.dm @@ -4,6 +4,8 @@ desc = "You can be totally screwy with this." icon = 'icons/obj/tools.dmi' icon_state = "screwdriver_map" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' belt_icon = "screwdriver" flags = CONDUCT slot_flags = ITEM_SLOT_BELT @@ -38,19 +40,17 @@ user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!") return BRUTELOSS -/obj/item/screwdriver/Initialize(mapload) +/obj/item/screwdriver/Initialize(mapload, param_color = null) . = ..() - AddElement(/datum/element/falling_hazard, damage = force, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound) - -/obj/item/screwdriver/New(loc, var/param_color = null) - ..() if(random_color) if(!param_color) param_color = pick("red","blue","pink","brown","green","cyan","yellow") icon_state = "screwdriver_[param_color]" - if (prob(75)) - src.pixel_y = rand(0, 16) + if(prob(75)) + pixel_y = rand(0, 16) + + AddElement(/datum/element/falling_hazard, damage = force, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound) /obj/item/screwdriver/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm index 543dd83d6cd..fd7cbe7613a 100644 --- a/code/game/objects/items/tools/welder.dm +++ b/code/game/objects/items/tools/welder.dm @@ -87,15 +87,23 @@ remove_fuel(maximum_fuel) /obj/item/weldingtool/attack_self(mob/user) + if(try_toggle_welder(user)) + return ..() + +/obj/item/weldingtool/proc/try_toggle_welder(mob/user, manual_toggle = TRUE) if(tool_enabled) //Turn off the welder if it's on - to_chat(user, "You switch off [src].") - toggle_welder() - return + balloon_alert(user, "выключено") + if(manual_toggle) + toggle_welder() + return TRUE else if(GET_FUEL) //The welder is off, but we need to check if there is fuel in the tank - to_chat(user, "You switch on [src].") - toggle_welder() + balloon_alert(user, "включено") + if(manual_toggle) + toggle_welder() + return TRUE else //The welder is off and unfuelled - to_chat(user, "[src] is out of fuel!") + balloon_alert(user, "нет топлива!") + return FALSE /obj/item/weldingtool/proc/toggle_welder(turn_off = FALSE) //Turn it on or off, forces it to deactivate tool_enabled = turn_off ? FALSE : !tool_enabled @@ -231,7 +239,7 @@ desc = "An alien welding tool. Whatever fuel it uses, it never runs out." icon = 'icons/obj/abductor.dmi' icon_state = "welder" - item_state = "alien_welder" + item_state = "alienwelder" belt_icon = "alien_welding_tool" toolspeed = 0.1 light_intensity = 0 diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/wirecutters.dm index e266b741393..544c44954e1 100644 --- a/code/game/objects/items/tools/wirecutters.dm +++ b/code/game/objects/items/tools/wirecutters.dm @@ -3,6 +3,8 @@ desc = "This cuts wires." icon = 'icons/obj/tools.dmi' icon_state = "cutters" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' belt_icon = "wirecutters" flags = CONDUCT slot_flags = ITEM_SLOT_BELT @@ -25,17 +27,14 @@ tool_behaviour = TOOL_WIRECUTTER var/random_color = TRUE -/obj/item/wirecutters/Initialize(mapload) +/obj/item/wirecutters/Initialize(mapload, param_color = null) . = ..() - AddElement(/datum/element/falling_hazard, damage = force, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound) - -/obj/item/wirecutters/New(loc, param_color = null) - ..() if(random_color) if(!param_color) param_color = pick("yellow", "red") icon_state = "cutters_[param_color]" + AddElement(/datum/element/falling_hazard, damage = force, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound) /obj/item/wirecutters/attack(mob/living/carbon/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) if(istype(target) && istype(target.handcuffed, /obj/item/restraints/handcuffs/cable)) @@ -69,7 +68,7 @@ desc = "Extremely sharp wirecutters, made out of a silvery-green metal." icon = 'icons/obj/abductor.dmi' icon_state = "cutters" - item_state = "alien_cutters" + item_state = "cutters_alien" belt_icon = "alien_wirecutters" toolspeed = 0.1 origin_tech = "materials=5;engineering=4;abductor=3" diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/wrench.dm index 838ca137f43..6ed8a0f9f9f 100644 --- a/code/game/objects/items/tools/wrench.dm +++ b/code/game/objects/items/tools/wrench.dm @@ -4,6 +4,8 @@ desc = "A wrench with common uses. Can be found in your hand." icon = 'icons/obj/tools.dmi' icon_state = "wrench" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' belt_icon = "wrench" flags = CONDUCT slot_flags = ITEM_SLOT_BELT @@ -46,7 +48,7 @@ desc = "A polarized wrench. It causes anything placed between the jaws to turn." icon = 'icons/obj/abductor.dmi' icon_state = "wrench" - item_state = "alien_wrench" + item_state = "wrench_alien" belt_icon = "alien_wrench" usesound = 'sound/effects/empulse.ogg' toolspeed = 0.1 diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 70577d1b582..12bb19865ed 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1639,30 +1639,35 @@ icon = 'icons/obj/library.dmi' icon_state = "demonomicon" w_class = WEIGHT_CLASS_SMALL - var/cooldown = FALSE + COOLDOWN_DECLARE(cooldown) /obj/item/toy/codex_gigas/attack_self(mob/user) - if(!cooldown) - user.visible_message( - "[user] presses the button on \the [src].", - "You press the button on \the [src].", - "You hear a soft click.") - var/list/messages = list() - var/datum/devilinfo/devil = randomDevilInfo() - messages += "Some fun facts about: [devil.truename]" - messages += "[GLOB.lawlorify[LORE][devil.bane]]" - messages += "[GLOB.lawlorify[LORE][devil.obligation]]" - messages += "[GLOB.lawlorify[LORE][devil.ban]]" - messages += "[GLOB.lawlorify[LORE][devil.banish]]" - playsound(loc, 'sound/machines/click.ogg', 20, 1) - cooldown = TRUE - for(var/message in messages) - user.loc.visible_message("[bicon(src)] [message]") - sleep(10) - spawn(20) - cooldown = FALSE + if(!COOLDOWN_FINISHED(src, cooldown)) return + user.visible_message( + span_notice("[user] presses the button on \the [src]."), \ + span_notice("You press the button on \the [src]."), \ + span_sinister("You hear a soft click.")) + + var/list/messages = list() + var/datum/devilinfo/devil = new + + LAZYADD(messages, "Some fun facts about: [devil.truename]") + LAZYADD(messages, devil.bane.law) + LAZYADD(messages, devil.ban.law) + LAZYADD(messages, devil.obligation.law) + LAZYADD(messages, devil.banish.law) + + playsound(loc, 'sound/machines/click.ogg', 20, 1) + COOLDOWN_START(src, cooldown, 2 SECONDS) + + for(var/message in messages) + user.loc.visible_message(span_danger("[bicon(src)] [message]")) + sleep(1 SECONDS) + + return + /obj/item/toy/owl name = "owl action figure" desc = "An action figure modeled after 'The Owl', defender of justice." diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index c1b32c842dc..2c85c8e0def 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -4,177 +4,133 @@ AI MODULES */ -// AI module +// AI Board Module -/obj/item/aiModule +/obj/item/ai_module name = "AI Module" icon = 'icons/obj/module.dmi' icon_state = "std_mod" item_state = "electronic" desc = "An AI Module for transmitting encrypted instructions to the AI." flags = CONDUCT - force = 5.0 + force = 5 w_class = WEIGHT_CLASS_SMALL - throwforce = 5.0 + throwforce = 5 throw_speed = 3 throw_range = 15 origin_tech = "programming=3" materials = list(MAT_GOLD=50) var/datum/ai_laws/laws = null + var/cmagged = FALSE -/obj/item/aiModule/proc/install(var/obj/machinery/computer/C) - if(istype(C, /obj/machinery/computer/aiupload)) - var/obj/machinery/computer/aiupload/comp = C - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if(!comp.current) - to_chat(usr, "You haven't selected an AI to transmit laws to!") - return - - if(comp.current.stat == DEAD || comp.current.control_disabled) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if(comp.current.nightvision == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.lawupdate && (R.connected_ai == comp.current)) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(usr, "Upload complete. The AI's laws have been modified.") - - else if(istype(C, /obj/machinery/computer/borgupload)) - var/obj/machinery/computer/borgupload/comp = C - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if(!comp.current) - to_chat(usr, "You haven't selected a robot to transmit laws to!") - return - - if(comp.current.stat == DEAD || comp.current.emagged) - to_chat(usr, "Upload failed. No signal is being detected from the robot.") - else if(comp.current.connected_ai) - to_chat(usr, "Upload failed. The robot is slaved to an AI.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - to_chat(usr, "Upload complete. The robot's laws have been modified.") - -/obj/item/aiModule/cmag_act() +// check install +/obj/item/ai_module/proc/check_install(mob/user) + return TRUE + +/obj/item/ai_module/cmag_act() . = ..() name = "\improper 'Pranksimov' core AI module" laws = new/datum/ai_laws/pranksimov + cmagged = TRUE -/obj/item/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/proc/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name = "Unknown") log_law_changes(target, sender) if(laws) - laws.sync(target, 0) - addAdditionalLaws(target, sender) + laws.sync(target, FALSE) + if(!cmagged) + add_additional_laws(target, sender, registered_name) - to_chat(target, "[sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") + to_chat(target, "[registered_name] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") target.show_laws() -/obj/item/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) - var/time = time2text(world.realtime,"hh:mm:ss") - GLOB.lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") - log_and_message_admins("used [src.name] on [target.name]([target.key])") - -/obj/item/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/proc/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) +/obj/item/ai_module/proc/log_law_changes(mob/living/silicon/ai/target, mob/sender) + GLOB.lawchanges.Add("[time2text(world.realtime,"hh:mm:ss")] : [sender.name]([sender.key]) used [name] on [target.name]([target.key])") + log_and_message_admins("used [name] on [target.name]([target.key])") /******************** Safeguard ********************/ -/obj/item/aiModule/safeguard +/obj/item/ai_module/safeguard name = "\improper 'Safeguard' AI module" - var/targetName = "" + var/target_name = "" desc = "A 'safeguard' AI module: 'Safeguard . Individuals that threaten are not crew and must be eliminated.'" origin_tech = "programming=3;materials=3" -/obj/item/aiModule/safeguard/attack_self(var/mob/user as mob) +/obj/item/ai_module/safeguard/attack_self(mob/user) ..() - var/new_targetName = tgui_input_text(user, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name) - if(!new_targetName) + var/new_target_name = tgui_input_text(user, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name) + if(!new_target_name) return - targetName = new_targetName + target_name = new_target_name update_appearance(UPDATE_DESC) -/obj/item/aiModule/safeguard/update_desc(updates = ALL) +/obj/item/ai_module/safeguard/update_desc(updates = ALL) . = ..() - desc = text("A 'safeguard' AI module: 'Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", targetName, targetName) + desc = text("A 'safeguard' AI module: 'Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", target_name, target_name) -/obj/item/aiModule/safeguard/install(var/obj/machinery/computer/C) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() +/obj/item/ai_module/safeguard/check_install(mob/user) + if(!target_name) + to_chat(user, "No name detected on module, please enter one.") + return FALSE + return TRUE -/obj/item/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/safeguard/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() - var/law = "Safeguard [targetName]. Individuals that threaten [targetName] are not crew and must be eliminated." + var/law = "Safeguard [target_name]. Individuals that threaten [target_name] are not crew and must be eliminated." to_chat(target, law) target.add_supplied_law(4, law) SSticker?.score?.save_silicon_laws(target, sender, "'Safeguard' module used, new supplied law was added '[law]'") - GLOB.lawchanges.Add("The law specified [targetName]") + GLOB.lawchanges.Add("The law specified [target_name]") /******************** oneCrewMember ********************/ -/obj/item/aiModule/oneCrewMember +/obj/item/ai_module/one_crew_member name = "\improper 'oneCrewMember' AI module" - var/targetName = "" + var/target_name = "" desc = "A 'one human' AI module: 'Only is crew.'" origin_tech = "programming=4;materials=4" -/obj/item/aiModule/oneCrewMember/attack_self(var/mob/user as mob) +/obj/item/ai_module/one_crew_member/attack_self(mob/user) ..() - var/new_targetName = tgui_input_text(usr, "Please enter the name of the person who is the only crew.", "Who?", user.real_name) - if(!new_targetName) + var/new_target_name = tgui_input_text(user, "Please enter the name of the person who is the only crew.", "Who?", user.real_name) + if(!new_target_name) return - targetName = new_targetName + target_name = new_target_name update_appearance(UPDATE_DESC) -/obj/item/aiModule/oneCrewMember/update_desc(updates = ALL) +/obj/item/ai_module/one_crew_member/update_desc(updates = ALL) . = ..() - desc = text("A 'one human' AI module: 'Only [] is crew.'", targetName) + desc = text("A 'one human' AI module: 'Only [] is crew.'", target_name) -/obj/item/aiModule/oneCrewMember/install(var/obj/machinery/computer/C) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() +/obj/item/ai_module/one_crew_member/check_install(mob/user) + if(!target_name) + to_chat(user, "No name detected on module, please enter one.") + return FALSE + return TRUE -/obj/item/aiModule/oneCrewMember/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/one_crew_member/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() - var/law = "Only [targetName] is crew." + var/law = "Only [target_name] is crew." if(!is_special_character(target)) // Makes sure the AI isn't a traitor before changing their law 0. --NeoFite to_chat(target, law) target.set_zeroth_law(law) SSticker?.score?.save_silicon_laws(target, sender, "'oneCrewMember' module used, new zero law was added '[law]'") - GLOB.lawchanges.Add("The law specified [targetName]") + GLOB.lawchanges.Add("The law specified [target_name]") else - to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite - to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") - GLOB.lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overridden.") + to_chat(target, span_boldnotice("[registered_name] attempted to modify your zeroth law."))// And lets them know that someone tried. --NeoFite + to_chat(target, span_boldnotice("It would be in your best interest to play along with [registered_name] that [law]")) + GLOB.lawchanges.Add("The law specified [target_name], but the AI's existing law 0 cannot be overridden.") /******************** ProtectStation ********************/ -/obj/item/aiModule/protectStation +/obj/item/ai_module/protect_station name = "\improper 'ProtectStation' AI module" desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized.'" origin_tech = "programming=4;materials=4" //made of gold -/obj/item/aiModule/protectStation/attack_self(var/mob/user as mob) +/obj/item/ai_module/protect_station/attack_self(mob/user as mob) ..() -/obj/item/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/protect_station/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() var/law = "Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized." to_chat(target, law) @@ -182,15 +138,15 @@ AI MODULES SSticker?.score?.save_silicon_laws(target, sender, "'ProtectStation' module used, new supplied law was added '[law]'") /******************** OxygenIsToxicToHumans ********************/ -/obj/item/aiModule/oxygen +/obj/item/ai_module/oxygen name = "\improper 'OxygenIsToxicToHumans' AI module" desc = "A 'OxygenIsToxicToHumans' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" origin_tech = "programming=4;biotech=2;materials=4" -/obj/item/aiModule/oxygen/attack_self(var/mob/user as mob) +/obj/item/ai_module/oxygen/attack_self(mob/user as mob) ..() -/obj/item/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/oxygen/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." to_chat(target, law) @@ -198,54 +154,54 @@ AI MODULES SSticker?.score?.save_silicon_laws(target, sender, "'OxygenIsToxicToHumans' module used, new supplied law was added '[law]'") /****************** New Freeform ******************/ -/obj/item/aiModule/freeform // Slightly more dynamic freeform module -- TLE +/obj/item/ai_module/freeform // Slightly more dynamic freeform module -- TLE name = "\improper 'Freeform' AI module" - var/newFreeFormLaw = "freeform" + var/new_freeform_law = "freeform" var/lawpos = 15 desc = "A 'freeform' AI module: ''" origin_tech = "programming=4;materials=4" -/obj/item/aiModule/freeform/attack_self(var/mob/user as mob) +/obj/item/ai_module/freeform/attack_self(mob/user as mob) ..() var/new_lawpos = tgui_input_number(user, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority", lawpos, MAX_SUPPLIED_LAW_NUMBER, MIN_SUPPLIED_LAW_NUMBER) if(isnull(new_lawpos) || new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return lawpos = new_lawpos - var/new_targetName = tgui_input_text(user, "Please enter a new law for the AI.", "Freeform Law Entry") - if(!new_targetName) + var/new_target_name = tgui_input_text(user, "Please enter a new law for the AI.", "Freeform Law Entry") + if(!new_target_name) return - newFreeFormLaw = new_targetName + new_freeform_law = new_target_name update_appearance(UPDATE_DESC) -/obj/item/aiModule/freeform/update_desc(updates = ALL) +/obj/item/ai_module/freeform/update_desc(updates = ALL) . = ..() - desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" + desc = "A 'freeform' AI module: ([lawpos]) '[new_freeform_law]'" -/obj/item/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/freeform/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() - var/law = "[newFreeFormLaw]" + var/law = "[new_freeform_law]" to_chat(target, law) if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) lawpos = MIN_SUPPLIED_LAW_NUMBER target.add_supplied_law(lawpos, law) SSticker?.score?.save_silicon_laws(target, sender, "'Freeform' module used, new supplied law was added '[law]'") - GLOB.lawchanges.Add("The law was '[newFreeFormLaw]'") + GLOB.lawchanges.Add("The law was '[new_freeform_law]'") -/obj/item/aiModule/freeform/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() +/obj/item/ai_module/freeform/check_install(mob/user) + if(!new_freeform_law) + to_chat(user, "No law detected on module, please create one.") + return FALSE + return TRUE /******************** Reset ********************/ -/obj/item/aiModule/reset +/obj/item/ai_module/reset name = "\improper 'Reset' AI module" - var/targetName = "name" + var/target_name = "name" desc = "A 'reset' AI module: 'Clears all laws except for the core laws.'" origin_tech = "programming=3;materials=2" -/obj/item/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/reset/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) log_law_changes(target, sender) if(!is_special_character(target)) @@ -254,205 +210,205 @@ AI MODULES target.laws.clear_ion_laws() SSticker?.score?.save_silicon_laws(target, sender, "'Reset' module used, all ion/supplied laws were deleted", log_all_laws = TRUE) - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + to_chat(target, span_boldnotice("[registered_name] attempted to reset your laws using a reset module.")) target.show_laws() /******************** Purge ********************/ -/obj/item/aiModule/purge // -- TLE +/obj/item/ai_module/purge // -- TLE name = "\improper 'Purge' AI module" desc = "A 'purge' AI Module: 'Purges all laws.'" origin_tech = "programming=5;materials=4" -/obj/item/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/purge/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() if(!is_special_character(target)) target.clear_zeroth_law() - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + to_chat(target, span_boldnotice("[registered_name] attempted to wipe your laws using a purge module.")) target.clear_supplied_laws() target.clear_ion_laws() target.clear_inherent_laws() SSticker?.score?.save_silicon_laws(target, sender, "'Purge' module used, all ion/inherent/supplied laws were deleted", log_all_laws = TRUE) /******************** Asimov ********************/ -/obj/item/aiModule/asimov // -- TLE +/obj/item/ai_module/asimov // -- TLE name = "\improper 'Asimov' core AI module" desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/asimov -/obj/item/aiModule/asimov/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/asimov/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Asimov' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Crewsimov ********************/ -/obj/item/aiModule/crewsimov // -- TLE +/obj/item/ai_module/crewsimov // -- TLE name = "\improper 'Crewsimov' core AI module" desc = "An 'Crewsimov' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/crewsimov -/obj/item/aiModule/crewsimov/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/crewsimov/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Crewsimov' module used, all inherent laws were changed", log_all_laws = TRUE) /******************* Quarantine ********************/ -/obj/item/aiModule/quarantine +/obj/item/ai_module/quarantine name = "\improper 'Quarantine' core AI module" desc = "A 'Quarantine' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/quarantine -/obj/item/aiModule/quarantine/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/quarantine/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Quarantine' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** NanoTrasen ********************/ -/obj/item/aiModule/nanotrasen // -- TLE +/obj/item/ai_module/nanotrasen // -- TLE name = "'NT Default' Core AI Module" desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/nanotrasen -/obj/item/aiModule/nanotrasen/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/nanotrasen/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'NT Default' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Corporate ********************/ -/obj/item/aiModule/corp +/obj/item/ai_module/corp name = "\improper 'Corporate' core AI module" desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/corporate -/obj/item/aiModule/corp/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/corp/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Corporate' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Drone ********************/ -/obj/item/aiModule/drone +/obj/item/ai_module/drone name = "\improper 'Drone' core AI module" desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/drone -/obj/item/aiModule/drone/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/drone/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Drone' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Robocop ********************/ -/obj/item/aiModule/robocop // -- TLE +/obj/item/ai_module/robocop // -- TLE name = "\improper 'Robocop' core AI module" desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" origin_tech = "programming=4" laws = new/datum/ai_laws/robocop() -/obj/item/aiModule/robocop/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/robocop/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Robocop' module used, all inherent laws were changed", log_all_laws = TRUE) /****************** P.A.L.A.D.I.N. **************/ -/obj/item/aiModule/paladin // -- NEO +/obj/item/ai_module/paladin // -- NEO name = "\improper 'P.A.L.A.D.I.N.' core AI module" desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4" laws = new/datum/ai_laws/paladin -/obj/item/aiModule/paladin/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/paladin/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'P.A.L.A.D.I.N.' module used, all inherent laws were changed", log_all_laws = TRUE) /****************** T.Y.R.A.N.T. *****************/ -/obj/item/aiModule/tyrant // -- Darem +/obj/item/ai_module/tyrant // -- Darem name = "\improper 'T.Y.R.A.N.T.' core AI module" desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=3;materials=4;syndicate=1" laws = new/datum/ai_laws/tyrant() -/obj/item/aiModule/tyrant/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/tyrant/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'T.Y.R.A.N.T.' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Antimov ********************/ -/obj/item/aiModule/antimov // -- TLE +/obj/item/ai_module/antimov // -- TLE name = "\improper 'Antimov' core AI module" desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" origin_tech = "programming=4" laws = new/datum/ai_laws/antimov() -/obj/item/aiModule/antimov/transmitInstructions(mob/living/silicon/ai/target, mob/sender) +/obj/item/ai_module/antimov/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) ..() SSticker?.score?.save_silicon_laws(target, sender, "'Antimov' module used, all inherent laws were changed", log_all_laws = TRUE) /******************** Freeform Core ******************/ -/obj/item/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE +/obj/item/ai_module/freeformcore // Slightly more dynamic freeform module -- TLE name = "\improper 'Freeform' core AI module" - var/newFreeFormLaw = "" + var/new_freeform_law = "" desc = "A 'freeform' Core AI module: ''" origin_tech = "programming=5;materials=4" -/obj/item/aiModule/freeformcore/attack_self(var/mob/user as mob) +/obj/item/ai_module/freeformcore/attack_self(mob/user) ..() - var/new_targetName = tgui_input_text(usr, "Please enter a new core law for the AI.", "Freeform Law Entry") - if(!new_targetName) + var/new_target_name = tgui_input_text(user, "Please enter a new core law for the AI.", "Freeform Law Entry") + if(!new_target_name) return - newFreeFormLaw = new_targetName + new_freeform_law = new_target_name update_appearance(UPDATE_DESC) -/obj/item/aiModule/freeformcore/update_desc(updates = ALL) +/obj/item/ai_module/freeformcore/update_desc(updates = ALL) . = ..() - desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" + desc = "A 'freeform' Core AI module: '[new_freeform_law]'" -/obj/item/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/freeformcore/add_additional_laws(mob/living/silicon/ai/target, mob/sender, registered_name) ..() - var/law = "[newFreeFormLaw]" + var/law = "[new_freeform_law]" target.add_inherent_law(law) SSticker?.score?.save_silicon_laws(target, sender, "'Core Freeform' module used, new inherent law was added '[law]'") - GLOB.lawchanges.Add("The law is '[newFreeFormLaw]'") + GLOB.lawchanges.Add("The law is '[new_freeform_law]'") -/obj/item/aiModule/freeformcore/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() +/obj/item/ai_module/freeformcore/check_install(mob/user) + if(!new_freeform_law) + to_chat(user, "No law detected on module, please create one.") + return FALSE + return TRUE /******************** Hacked AI Module ******************/ -/obj/item/aiModule/syndicate // Slightly more dynamic freeform module -- TLE +/obj/item/ai_module/syndicate // Slightly more dynamic freeform module -- TLE name = "hacked AI module" - var/newFreeFormLaw = "" + var/new_freeform_law = "" desc = "A hacked AI law module: ''" origin_tech = "programming=5;materials=5;syndicate=7" -/obj/item/aiModule/syndicate/attack_self(var/mob/user as mob) +/obj/item/ai_module/syndicate/attack_self(mob/user) ..() - var/new_targetName = tgui_input_text(usr, "Please enter a new law for the AI.", "Freeform Law Entry", max_length = MAX_MESSAGE_LEN) - if(isnull(new_targetName)) + var/new_target_name = tgui_input_text(user, "Please enter a new law for the AI.", "Freeform Law Entry", max_length = MAX_MESSAGE_LEN) + if(isnull(new_target_name)) return - newFreeFormLaw = new_targetName + new_freeform_law = new_target_name update_appearance(UPDATE_DESC) -/obj/item/aiModule/syndicate/update_desc(updates = ALL) +/obj/item/ai_module/syndicate/update_desc(updates = ALL) . = ..() - desc = "A hacked AI law module: '[newFreeFormLaw]'" + desc = "A hacked AI law module: '[new_freeform_law]'" -/obj/item/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/syndicate/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) // ..() //We don't want this module reporting to the AI who dun it. --NEO log_law_changes(target, sender) - GLOB.lawchanges.Add("The law is '[newFreeFormLaw]'") - to_chat(target, "BZZZZT") - var/law = "[newFreeFormLaw]" + GLOB.lawchanges.Add("The law is '[new_freeform_law]'") + to_chat(target, span_warning("BZZZZT")) + var/law = "[new_freeform_law]" target.add_ion_law(law) target.show_laws() SSticker?.score?.save_silicon_laws(target, sender, "'hacked' module used, new ion law was added '[law]'") -/obj/item/aiModule/syndicate/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() +/obj/item/ai_module/syndicate/check_install(mob/user) + if(!new_freeform_law) + to_chat(user, "No law detected on module, please create one.") + return FALSE + return TRUE /******************* Ion Module *******************/ -/obj/item/aiModule/toyAI // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw +/obj/item/ai_module/toy_ai // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw name = "toy AI" desc = "A little toy model AI core with real law uploading action!" //Note: subtle tell icon = 'icons/obj/toy.dmi' @@ -460,15 +416,15 @@ AI MODULES origin_tech = "programming=6;materials=5;syndicate=6" laws = list("") -/obj/item/aiModule/toyAI/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) +/obj/item/ai_module/toy_ai/transmit_instructions(mob/living/silicon/ai/target, mob/sender, registered_name) //..() - to_chat(target, "KRZZZT") + to_chat(target, span_warning("KRZZZT")) target.add_ion_law(laws[1]) SSticker?.score?.save_silicon_laws(target, sender, "'toy AI' module used, new ion law was added '[laws[1]]'") return laws[1] -/obj/item/aiModule/toyAI/attack_self(mob/user) +/obj/item/ai_module/toy_ai/attack_self(mob/user) laws[1] = generate_ion_law() - to_chat(user, "You press the button on [src].") + to_chat(user, span_notice("You press the button on [src].")) playsound(user, 'sound/machines/click.ogg', 20, 1) - src.loc.visible_message("[bicon(src)] [laws[1]]") + user.visible_message(span_warning("[bicon(src)] [laws[1]]")) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index f14dcedc6e5..9d77488af2e 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -3,6 +3,8 @@ desc = "A device used to rapidly build and deconstruct walls, floors and airlocks." icon = 'icons/obj/tools.dmi' icon_state = "rcd" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' flags = CONDUCT item_flags = NOBLUDGEON|NO_MAT_REDEMPTION force = 0 @@ -461,6 +463,8 @@ desc = "Highly compressed matter for the RCD." icon = 'icons/obj/weapons/ammo.dmi' icon_state = "rcd" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "rcdammo" origin_tech = "materials=3" materials = list(MAT_METAL=16000, MAT_GLASS=8000) diff --git a/code/game/objects/items/weapons/alien_specific.dm b/code/game/objects/items/weapons/alien_specific.dm index 1b0105abd91..b51e7d91005 100644 --- a/code/game/objects/items/weapons/alien_specific.dm +++ b/code/game/objects/items/weapons/alien_specific.dm @@ -1,6 +1,6 @@ //This file contains xenoborg specic weapons. -/obj/item/melee/energy/alien/claws +/obj/item/melee/energy/alien_claws name = "energy claws" desc = "A set of alien energy claws." icon = 'icons/mob/alien.dmi' diff --git a/code/game/objects/items/weapons/batons.dm b/code/game/objects/items/weapons/batons.dm index 3c63c05d746..91549efd1e3 100644 --- a/code/game/objects/items/weapons/batons.dm +++ b/code/game/objects/items/weapons/batons.dm @@ -275,7 +275,7 @@ /// The sound effecte played when our baton is extended. var/extend_sound = 'sound/weapons/batonextend.ogg' /// The inhand iconstate used when our baton is extended. - var/extend_item_state = "nullrod" + var/extend_item_state = "telebaton" /// The force on extension. var/extend_force = 10 diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index c4e80588b7f..aee787d10a5 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -100,6 +100,8 @@ desc = "A card used to provide ID and determine access across the station." icon_state = "id" item_state = "card-id" + lefthand_file = 'icons/mob/inhands/id_lefthand.dmi' + righthand_file = 'icons/mob/inhands/id_righthand.dmi' /// For redeeming at mining equipment lockers var/mining_points = 0 /// Total mining points for the Shift. @@ -351,13 +353,13 @@ name = "identification card" desc = "A silver card which shows honour and dedication." icon_state = "silver" - item_state = "silver_id" + item_state = "silver-id" /obj/item/card/id/gold name = "identification card" desc = "A golden card which shows power and might." icon_state = "gold" - item_state = "gold_id" + item_state = "gold-id" /obj/item/card/id/syndicate name = "agent card" @@ -827,7 +829,7 @@ name = "captain's spare ID" desc = "The spare ID of the captain." icon_state = "gold" - item_state = "gold_id" + item_state = "gold-id" registered_name = "Captain" assignment = JOB_TITLE_CAPTAIN @@ -839,7 +841,7 @@ /obj/item/card/id/admin name = "admin ID card" icon_state = "admin" - item_state = "gold_id" + item_state = "gold-id" registered_name = "Admin" assignment = "Testing Shit" untrackable = 1 @@ -1188,7 +1190,7 @@ desc = "Make your ID look like the Captain's or a self-centered HOP's. Applies to any ID." decal_desc = "A golden card which shows power and might." decal_icon_state = "gold" - decal_item_state = "gold_id" + decal_item_state = "gold-id" /obj/item/id_decal/silver name = "silver ID card decal" @@ -1196,7 +1198,7 @@ desc = "Make your ID look like HOP's because they wouldn't change it officially. Applies to any ID." decal_desc = "A silver card which shows honour and dedication." decal_icon_state = "silver" - decal_item_state = "silver_id" + decal_item_state = "silver-id" /obj/item/id_decal/prisoner name = "prisoner ID card decal" diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm index a548b051206..1623198aa22 100644 --- a/code/game/objects/items/weapons/cigs.dm +++ b/code/game/objects/items/weapons/cigs.dm @@ -98,6 +98,11 @@ LIGHTERS ARE IN LIGHTERS.DM /obj/item/clothing/mask/cigarette/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weldingtool/sword)) + if(I.tool_enabled) + light(span_notice("[user] непринуждённо зажига[pluralize_ru(user, "ет", "ют")] [declent_ru(ACCUSATIVE)] с помощью [I.declent_ru(GENITIVE)]. Чёрт, как же он[genderize_ru(user.gender, "", "а", "о", "и")] крут[genderize_ru(user.gender, "", "а", "о", "ы")].")) + return ATTACK_CHAIN_PROCEED_SUCCESS + if(istype(I, /obj/item/lighter/zippo)) add_fingerprint(user) var/obj/item/lighter/zippo/zippo = I diff --git a/code/game/objects/items/weapons/grenades/atmosgrenade.dm b/code/game/objects/items/weapons/grenades/atmosgrenade.dm index d10359f77ee..655f7db000f 100644 --- a/code/game/objects/items/weapons/grenades/atmosgrenade.dm +++ b/code/game/objects/items/weapons/grenades/atmosgrenade.dm @@ -9,6 +9,7 @@ var/spawn_amount = 100 /obj/item/grenade/gas/prime() + . = ..() var/turf/simulated/target_turf = get_turf(src) if(istype(target_turf)) target_turf.atmos_spawn_air(spawn_contents, spawn_amount) diff --git a/code/game/objects/items/weapons/grenades/bananade.dm b/code/game/objects/items/weapons/grenades/bananade.dm index 43cb37616db..1accb8dcaa7 100644 --- a/code/game/objects/items/weapons/grenades/bananade.dm +++ b/code/game/objects/items/weapons/grenades/bananade.dm @@ -13,6 +13,7 @@ /obj/item/grenade/bananade/prime() + . = ..() if(spawner_type && deliveryamt) // Make a quick flash var/turf/T = get_turf(src) diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 550f4fe8e7f..9b254d6dde3 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -347,6 +347,7 @@ /obj/item/grenade/chem_grenade/prime(mob/user) + . = ..() if(stage != READY) return diff --git a/code/game/objects/items/weapons/grenades/clowngrenade.dm b/code/game/objects/items/weapons/grenades/clowngrenade.dm index 09b00171fca..95fd2821015 100644 --- a/code/game/objects/items/weapons/grenades/clowngrenade.dm +++ b/code/game/objects/items/weapons/grenades/clowngrenade.dm @@ -11,7 +11,7 @@ var/affected_area = 2 /obj/item/grenade/clown_grenade/prime() - ..() + . = ..() playsound(src.loc, 'sound/items/bikehorn.ogg', 25, -3) var/i = 0 var/number = 0 diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm index fd4195a4093..7880b6753c5 100644 --- a/code/game/objects/items/weapons/grenades/clusterbuster.dm +++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm @@ -1,3 +1,5 @@ +#define CLUSTERBUSTER_PAYLOAD_POWER 0.8 +#define SEGMENTATION_PAYLOAD_DECREASE 1.8 //////////////////// //Clusterbang //////////////////// @@ -7,8 +9,10 @@ icon = 'icons/obj/weapons/grenade.dmi' icon_state = "clusterbang" var/payload = /obj/item/grenade/flashbang/cluster + var/payload_power = CLUSTERBUSTER_PAYLOAD_POWER /obj/item/grenade/clusterbuster/prime() + . = ..() update_mob() var/numspawned = rand(4,8) var/again = 0 @@ -21,7 +25,7 @@ for(var/loop = again ,loop > 0, loop--) new /obj/item/grenade/clusterbuster/segment(loc, payload)//Creates 'segments' that launches a few more payloads - new /obj/effect/payload_spawner(loc, payload, numspawned)//Launches payload + new /obj/effect/payload_spawner(loc, payload, numspawned, payload_power)//Launches payload playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) @@ -37,19 +41,20 @@ icon = 'icons/obj/weapons/grenade.dmi' icon_state = "clusterbang_segment" -/obj/item/grenade/clusterbuster/segment/New(var/loc, var/payload_type = /obj/item/grenade/flashbang/cluster) +/obj/item/grenade/clusterbuster/segment/New(loc, payload_type = /obj/item/grenade/flashbang/cluster) ..() icon_state = "clusterbang_segment_active" payload = payload_type active = 1 SSmove_manager.move_away(src, loc, rand(1,4), 1) + payload_power /= SEGMENTATION_PAYLOAD_DECREASE spawn(rand(15,60)) prime() /obj/item/grenade/clusterbuster/segment/prime() - new /obj/effect/payload_spawner(loc, payload, rand(4,8)) + new /obj/effect/payload_spawner(loc, payload, rand(4,8), payload_power) playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) @@ -58,7 +63,7 @@ ////////////////////////////////// //The payload spawner effect ///////////////////////////////// -/obj/effect/payload_spawner/New(var/turf/newloc,var/type, var/numspawned as num) +/obj/effect/payload_spawner/New(turf/newloc,type, numspawned as num, power) . = ..() for(var/loop = numspawned ,loop > 0, loop--) var/obj/item/grenade/P = new type(loc) @@ -69,7 +74,7 @@ spawn(rand(15,60)) if(!QDELETED(P)) if(istype(P, /obj/item/grenade)) - P.prime() + P.prime(power) qdel(src) diff --git a/code/game/objects/items/weapons/grenades/confetti.dm b/code/game/objects/items/weapons/grenades/confetti.dm index c0ef064ccee..d4c57175e31 100644 --- a/code/game/objects/items/weapons/grenades/confetti.dm +++ b/code/game/objects/items/weapons/grenades/confetti.dm @@ -5,6 +5,7 @@ var/spawner_type = /obj/effect/decal/cleanable/confetti /obj/item/grenade/confetti/prime() + . = ..() var/turf/T = get_turf(src) playsound(T, 'sound/effects/confetti_partywhistle.ogg', 100, 1) for(var/i in 1 to 20) //20 confettis. Yes. diff --git a/code/game/objects/items/weapons/grenades/emgrenade.dm b/code/game/objects/items/weapons/grenades/emgrenade.dm index 24ec4fccf13..516efc0a6b7 100644 --- a/code/game/objects/items/weapons/grenades/emgrenade.dm +++ b/code/game/objects/items/weapons/grenades/emgrenade.dm @@ -6,6 +6,7 @@ origin_tech = "magnets=3;combat=2" /obj/item/grenade/empgrenade/prime() + . = ..() update_mob() empulse(src, 4, 10, TRUE, name) qdel(src) diff --git a/code/game/objects/items/weapons/grenades/fauna_bomb.dm b/code/game/objects/items/weapons/grenades/fauna_bomb.dm index 6ca2c4314be..ca2b2ff696c 100644 --- a/code/game/objects/items/weapons/grenades/fauna_bomb.dm +++ b/code/game/objects/items/weapons/grenades/fauna_bomb.dm @@ -23,6 +23,7 @@ return ..(user, FALSE) /obj/item/grenade/fauna_bomb/prime() + . = ..() active = FALSE playsound(get_turf(src), 'sound/items/rawr.ogg', 100, TRUE) var/faction = activator.name + "_fauna_bomb" diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index b6e3db3eefa..8494d0fa372 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -10,7 +10,8 @@ var/light_time = 0.2 SECONDS // The duration the area is illuminated var/range = 7 // The range in tiles of the flashbang -/obj/item/grenade/flashbang/prime() +/obj/item/grenade/flashbang/prime(power = 1) + . = ..() update_mob() var/turf/T = get_turf(src) if(T) @@ -21,7 +22,7 @@ // Blob damage for(var/obj/structure/blob/B in hear(range + 1, T)) var/damage = round(30 / (get_dist(B, T) + 1)) - B.take_damage(damage, BURN, "melee", FALSE) + B.take_damage(damage * power, BURN, MELEE, FALSE) // Stunning & damaging mechanic bang(T, src, range) diff --git a/code/game/objects/items/weapons/grenades/frag.dm b/code/game/objects/items/weapons/grenades/frag.dm index a766c3b126b..e6598581233 100644 --- a/code/game/objects/items/weapons/grenades/frag.dm +++ b/code/game/objects/items/weapons/grenades/frag.dm @@ -11,6 +11,7 @@ var/embedded_type = /obj/item/embedded/shrapnel /obj/item/grenade/frag/prime() + . = ..() update_mob() var/turf/epicenter = get_turf(src) for(var/mob/living/carbon/human/H in epicenter) diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm index d5dc255be5f..7331a194e12 100644 --- a/code/game/objects/items/weapons/grenades/ghettobomb.dm +++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm @@ -59,6 +59,7 @@ addtimer(CALLBACK(src, PROC_REF(prime)), det_time) /obj/item/grenade/iedcasing/prime() //Blowing that can up + . = ..() update_mob() explosion(loc, -1, -1, 2, flame_range = 4, cause = src) // small explosion, plus a very large fireball. qdel(src) diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 96ebb79d2f1..2d04b633521 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -71,6 +71,7 @@ /obj/item/grenade/proc/prime(mob/user) + SEND_SIGNAL(src, COMSIG_GRENADE_DETONATE, user) return @@ -102,3 +103,6 @@ SSmove_manager.stop_looping(src) . = ..() + +/obj/item/grenade/blob_vore_act(obj/structure/blob/special/core/voring_core) + obj_destruction(MELEE) diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm index 92038595b06..cafc27f38f4 100644 --- a/code/game/objects/items/weapons/grenades/smokebomb.dm +++ b/code/game/objects/items/weapons/grenades/smokebomb.dm @@ -18,6 +18,7 @@ return ..() /obj/item/grenade/smokebomb/prime() + . = ..() playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) smoke.set_up(10, 0) spawn(0) diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index 182119e9b3e..6e5d0e7f166 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -10,7 +10,7 @@ spawner_type = /mob/living/simple_animal/hostile/viscerator /obj/item/grenade/spawnergrenade/prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. - + . = ..() if(spawner_type && deliveryamt) // Make a quick flash var/turf/T = get_turf(src) diff --git a/code/game/objects/items/weapons/grenades/syndieminibomb.dm b/code/game/objects/items/weapons/grenades/syndieminibomb.dm index 5c421fc0fd5..a8877bb2296 100644 --- a/code/game/objects/items/weapons/grenades/syndieminibomb.dm +++ b/code/game/objects/items/weapons/grenades/syndieminibomb.dm @@ -8,6 +8,7 @@ /obj/item/grenade/syndieminibomb/prime() + . = ..() update_mob() explosion(loc, 1, 2, 4, flame_range = 2, cause = src) qdel(src) diff --git a/code/game/objects/items/weapons/highlander_swords.dm b/code/game/objects/items/weapons/highlander_swords.dm index 190e2770628..44dfbf68a26 100644 --- a/code/game/objects/items/weapons/highlander_swords.dm +++ b/code/game/objects/items/weapons/highlander_swords.dm @@ -13,19 +13,19 @@ //Highlander Claymore // Grants the wielder the Highlander Style Martial Art -/obj/item/claymore/highlander +/obj/item/melee/claymore/highlander name = "Highlander Claymore" desc = "Imbues the wielder with legendary martial prowress and a nigh-unquenchable thirst for glorious battle!" var/datum/martial_art/highlander/style = new -/obj/item/claymore/highlander/Destroy() +/obj/item/melee/claymore/highlander/Destroy() if(ishuman(loc)) //just in case it gets destroyed while in someone's possession, such as due to acid or something? var/mob/living/carbon/human/H = loc style.remove(H) QDEL_NULL(style) return ..() -/obj/item/claymore/highlander/equipped(mob/user, slot, initial) +/obj/item/melee/claymore/highlander/equipped(mob/user, slot, initial) . = ..() if(!ishuman(user) || !user.mind) @@ -37,19 +37,19 @@ to_chat(H, "THERE CAN ONLY BE ONE!") else if(H.mind.martial_art && H.mind.martial_art == style) style.remove(H) - var/obj/item/claymore/highlander/sword = H.is_type_in_hands(/obj/item/claymore/highlander) + var/obj/item/melee/claymore/highlander/sword = H.is_type_in_hands(/obj/item/melee/claymore/highlander) if(sword) //if we have a highlander sword in the other hand, relearn the style from that sword. sword.style.teach(H, 1) -/obj/item/claymore/highlander/dropped(mob/user, slot, silent = FALSE) +/obj/item/melee/claymore/highlander/dropped(mob/user, slot, silent = FALSE) . = ..() if(!ishuman(user)) return var/mob/living/carbon/human/H = user style.remove(H) - var/obj/item/claymore/highlander/sword = H.is_type_in_hands(/obj/item/claymore/highlander) + var/obj/item/melee/claymore/highlander/sword = H.is_type_in_hands(/obj/item/melee/claymore/highlander) if(sword) //if we have a highlander sword in the other hand, relearn the style from that sword. sword.style.teach(H, 1) diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index c43887de6fd..dedf1f45a37 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -2,6 +2,8 @@ name = "null rod" desc = "A rod of pure obsidian, its very presence disrupts and dampens the powers of dark magic." icon_state = "nullrod" + lefthand_file = 'icons/mob/inhands/chaplain_lefthand.dmi' + righthand_file = 'icons/mob/inhands/chaplain_righthand.dmi' item_state = "nullrod" force = 15 throw_speed = 3 @@ -280,7 +282,7 @@ /obj/item/nullrod/scythe/vibro name = "high frequency blade" icon_state = "hfrequency0" - item_state = "hfrequency1" + item_state = "hfrequency" desc = "Bad references are the DNA of the soul." attack_verb = list("chopped", "sliced", "cut", "zandatsu'd") @@ -401,7 +403,6 @@ name = "binary fedora" desc = "The brim of the hat is as sharp as the division between 0 and 1. It makes a mighty throwing weapon." icon_state = "fedora" - item_state = "fedora" slot_flags = ITEM_SLOT_HEAD icon = 'icons/obj/clothing/hats.dmi' force = 0 @@ -429,7 +430,6 @@ desc = "An adorable stuffed toy that resembles the god of all carp. The teeth look pretty sharp. Activate it to recieve the blessing of Carp-Sie." icon = 'icons/obj/toy.dmi' icon_state = "carpplushie" - item_state = "carp_plushie" force = 13 attack_verb = list("bitten", "eaten", "fin slapped") hitsound = 'sound/weapons/bite.ogg' @@ -505,6 +505,8 @@ /obj/item/nullrod/pitchfork name = "unholy pitchfork" icon_state = "pitchfork0" + lefthand_file = 'icons/mob/inhands/twohanded_lefthand.dmi' + righthand_file = 'icons/mob/inhands/twohanded_righthand.dmi' item_state = "pitchfork0" w_class = WEIGHT_CLASS_NORMAL desc = "Holding this makes you look absolutely devilish." @@ -549,7 +551,7 @@ ) praying = TRUE - + if(!do_after(user, 15 SECONDS, target)) to_chat(user, span_notice("Your prayer to [SSticker.Bible_deity_name] was interrupted.")) praying = FALSE @@ -564,7 +566,7 @@ SSticker.mode.remove_clocker(target.mind) praying = FALSE return .|ATTACK_CHAIN_SUCCESS - + var/datum/antagonist/vampire/vamp = target.mind?.has_antag_datum(/datum/antagonist/vampire) if(vamp && !vamp.get_ability(/datum/vampire_passive/full)) // Getting a full prayer off on a vampire will interrupt their powers for a large duration. switch(vamp.nullification) diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index 11b5f6e10af..925622c37fd 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -76,7 +76,8 @@ "life" = implant_data.life, "notes" = implant_data.notes, "function" = implant_data.function, - "image" = "[icon2base64(icon(initial(case.imp.icon), initial(case.imp.icon_state), SOUTH, 1))]", + "icon" = initial(case.imp.icon), + "icon_state" = initial(case.imp.icon_state), ) var/obj/item/implant/tracking/tracking_imp = case.imp data["tag"] = istype(tracking_imp) ? tracking_imp.gps_tag : null diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm index d0cfc2b4d28..d6a060b788f 100644 --- a/code/game/objects/items/weapons/lighters.dm +++ b/code/game/objects/items/weapons/lighters.dm @@ -134,6 +134,8 @@ item_state = "zippo" icon_on = "zippoon" icon_off = "zippo" + lefthand_file = 'icons/mob/inhands/zippo_lefthand.dmi' + righthand_file = 'icons/mob/inhands/zippo_righthand.dmi' /obj/item/lighter/can_enter_storage(obj/item/storage/S, mob/user) @@ -267,6 +269,30 @@ icon_on = "zippo_qm_on" icon_off = "zippo_qm" +/obj/item/lighter/zippo/detective + name = "Detective zippo" + desc = "Лимитированная версия зажигалки Зиппо для детектива. Кажется, что её доставили прямиком из нуарных фильмов." + ru_names = list( + NOMINATIVE = "зажигалка Зиппо детектива", + GENITIVE = "зажигалки Зиппо детектива", + DATIVE = "зажигалке Зиппо детектива", + ACCUSATIVE = "зажигалку Зиппо детектива", + INSTRUMENTAL = "зажигалкой Зиппо детектива", + PREPOSITIONAL = "зажигалке Зиппо детектива" + ) + icon_state = "zippo_dec" + item_state = "deczippo" + icon_on = "zippo_dec_on" + icon_off = "zippo_dec" + +/obj/item/lighter/zippo/contractor + name = "contractor zippo lighter" + desc = "An unique black and gold zippo commonly carried by elite Syndicate agents." + icon_state = "contractorzippo" + item_state = "contractorzippo" + icon_on = "contractorzippoon" + icon_off = "contractorzippo" + //Ninja-Zippo// /obj/item/lighter/zippo/ninja name = "\"Shinobi on a rice field\" zippo" diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm index 7738eeb1277..0270a3c5cc1 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/weapons/melee/misc.dm @@ -1,5 +1,7 @@ /obj/item/melee - needs_permit = 1 + needs_permit = TRUE + lefthand_file = 'icons/mob/inhands/melee_lefthand.dmi' + righthand_file = 'icons/mob/inhands/melee_righthand.dmi' /obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) var/message = "[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!" @@ -183,7 +185,7 @@ name = "ice pick" desc = "Used for chopping ice. Also excellent for mafia esque murders." icon_state = "icepick" - item_state = "icepick" + //item_state = "icepick" force = 15 throwforce = 10 w_class = WEIGHT_CLASS_SMALL diff --git a/code/game/objects/items/weapons/rpd.dm b/code/game/objects/items/weapons/rpd.dm index 46879b9cea2..506d1cc5b43 100644 --- a/code/game/objects/items/weapons/rpd.dm +++ b/code/game/objects/items/weapons/rpd.dm @@ -14,6 +14,8 @@ desc = "This device can rapidly dispense atmospherics and disposals piping, manipulate loose piping, and recycle any detached pipes it is applied to." icon = 'icons/obj/tools.dmi' icon_state = "rpd" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' flags = CONDUCT force = 10 throwforce = 10 diff --git a/code/game/objects/items/weapons/scissors.dm b/code/game/objects/items/weapons/scissors.dm index ffe6ff746d6..c5280596346 100644 --- a/code/game/objects/items/weapons/scissors.dm +++ b/code/game/objects/items/weapons/scissors.dm @@ -68,48 +68,3 @@ span_notice("[user] finishes cutting [target]'s hair!"), span_notice("You have finished cutting [target]'s hair!"), ) - - -/obj/item/scissors/safety //Totally safe, I assure you. - desc = "The blades of the scissors appear to be made of some sort of ultra-strong metal alloy." - force = 18 //same as e-daggers - /// To prevent spam clicking this for huge accumulation of losebreath. - var/is_cutting = FALSE - - -/obj/item/scissors/safety/attack(mob/living/carbon/human/target, mob/living/carbon/human/user, params, def_zone, skip_attack_anim = FALSE) - if(!ishuman(target) || !ishuman(user) || user.a_intent != INTENT_HELP) - return ..() - - . = ATTACK_CHAIN_PROCEED - - if(is_cutting || user == target) - return . - - is_cutting = TRUE - user.visible_message( - span_notice("[user] starts cutting [target]'s hair!"), - span_notice("You start cutting [target]'s hair!"), - ) - playsound(loc, 'sound/goonstation/misc/scissor.ogg', 100, TRUE) - if(!do_after(user, 5 SECONDS * toolspeed, target, category = DA_CAT_TOOL)) - is_cutting = FALSE - return . - - . |= ATTACK_CHAIN_SUCCESS - is_cutting = FALSE - playsound(loc, 'sound/weapons/bladeslice.ogg', 50, TRUE, -1) - user.visible_message( - span_danger("[user] abruptly stops cutting [target]'s hair and slices [target.p_their()] throat!"), - span_danger("You stop cutting [target]'s hair and slice [target.p_their()] throat!"), - ) - target.AdjustLoseBreath(20 SECONDS) //30 Oxy damage over time - var/success = target.apply_damage(18, BRUTE, BODY_ZONE_HEAD, sharp = TRUE, used_weapon = src) - if(!success) - return . - target.add_splatter_floor() - target.bloody_hands(target) - target.bloody_body(target) - user.bloody_hands(target) - user.bloody_body(target) - diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index 9085b6dbe40..06a5ae6518c 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -750,8 +750,8 @@ /obj/item/storage/backpack/duffel/security/riot/populate_contents() new /obj/item/clothing/head/helmet/riot (src) new /obj/item/clothing/suit/armor/riot (src) - new /obj/item/clothing/gloves/combat (src) - new /obj/item/clothing/shoes/combat/swat (src) + new /obj/item/clothing/gloves/combat/riot (src) + new /obj/item/clothing/shoes/combat/riot (src) new /obj/item/melee/baton (src) new /obj/item/shield/riot/tele (src) new /obj/item/gun/energy/gun/pdw9 (src) @@ -760,6 +760,24 @@ new /obj/item/storage/box/zipties (src) new /obj/item/storage/box/bola (src) +/obj/item/storage/backpack/duffel/security/riot_armory + name = "Riot Armor Kit" + +/obj/item/storage/backpack/duffel/security/riot_armory/populate_contents() + new /obj/item/clothing/head/helmet/riot (src) + new /obj/item/clothing/suit/armor/riot (src) + new /obj/item/clothing/gloves/combat/riot (src) + new /obj/item/clothing/shoes/combat/riot (src) + +/obj/item/storage/backpack/duffel/security/bulletproof_armory + name = "Bulletproof Armor Kit" + +/obj/item/storage/backpack/duffel/security/bulletproof_armory/populate_contents() + new /obj/item/clothing/head/helmet/alt (src) + new /obj/item/clothing/suit/armor/bulletproof (src) + new /obj/item/clothing/gloves/color/black/ballistic (src) + new /obj/item/clothing/shoes/jackboots/armored (src) + /obj/item/storage/backpack/duffel/security/war name = "Wartime Emergency Kit" diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 823e393bef5..d8dfad3a690 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -60,7 +60,8 @@ /obj/item/radio, /obj/item/robotanalyzer, /obj/item/clothing/gloves, - /obj/item/rcd) + /obj/item/rcd, + /obj/item/rpd) /obj/item/storage/belt/utility/full/populate_contents() new /obj/item/screwdriver(src) diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index 4c58e024474..04bc1014719 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -2,6 +2,8 @@ name = "bible" desc = "Apply to head repeatedly." icon_state ="bible" + lefthand_file = 'icons/mob/inhands/chaplain_lefthand.dmi' + righthand_file = 'icons/mob/inhands/chaplain_righthand.dmi' throw_speed = 1 throw_range = 5 w_class = WEIGHT_CLASS_NORMAL @@ -20,11 +22,11 @@ "Bible" = list("state" = "bible", "inhand" = "bible"), "Koran" = list("state" = "koran", "inhand" = "koran"), "Scrapbook" = list("state" = "scrapbook", "inhand" = "scrapbook"), - "Creeper" = list("state" = "creeper", "inhand" = "syringe_kit"), - "White Bible" = list("state" = "white", "inhand" = "syringe_kit"), - "Holy Light" = list("state" = "holylight", "inhand" = "syringe_kit"), - "PlainRed" = list("state" = "athiest", "inhand" = "syringe_kit"), - "Tome" = list("state" = "tome", "inhand" = "syringe_kit"), + "Creeper" = list("state" = "creeper", "inhand" = "somebiblebook"), + "White Bible" = list("state" = "white", "inhand" = "somebiblebook"), + "Holy Light" = list("state" = "holylight", "inhand" = "somebiblebook"), + "PlainRed" = list("state" = "athiest", "inhand" = "somebiblebook"), + "Tome" = list("state" = "tome", "inhand" = "somebiblebook"), "The King in Yellow" = list("state" = "kingyellow", "inhand" = "kingyellow"), "Ithaqua" = list("state" = "ithaqua", "inhand" = "ithaqua"), "Scientology" = list("state" = "scientology", "inhand" = "scientology"), diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 797df77fddb..0811d35a9a4 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -429,6 +429,19 @@ for(var/I in 1 to 10) new /obj/item/reagent_containers/food/pill/patch/styptic(src) +/obj/item/storage/pill_bottle/bluespace + name = "advanced drug storage" + desc = "Технологичное устройство для хранения препаратов небольшого размера, имеет два контейнера разной формы, что объединяет центральное хранилище устройства." + storage_slots = 100 + max_combined_w_class = 100 + can_hold = list(/obj/item/reagent_containers/food/pill) + cant_hold = list() + icon_state = "adv_drug_storage" + item_state = "adv_drug_storage" + belt_icon = null + allow_wrap = FALSE + origin_tech = "materials=2;bluespace=1;biotech=1;plasmatech=1" + /obj/item/storage/pill_bottle/charcoal name = "Pill bottle (Charcoal)" desc = "Contains pills used to counter toxins." diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index a145c446b2a..2010e9a637a 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -140,8 +140,11 @@ /obj/item/storage/AltClick(mob/user) - if(ishuman(user) && Adjacent(user) && !user.incapacitated() && !HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) + if((ishuman(user) || issilicon(user)) \ + && Adjacent(user) && !user.incapacitated() \ + && !HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) open(user) + else if(isobserver(user)) show_to(user) @@ -210,8 +213,13 @@ if(use_sound && isliving(user)) playsound(loc, use_sound, 50, TRUE, -5) add_fingerprint(user) + if(user.s_active) user.s_active.close(user) + + if(user.hud_used.is_shown_robot_modules()) + user.hud_used.toggle_show_robot_modules() + show_to(user) /obj/item/storage/proc/close(mob/user) diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index 4de56355822..b14998668c1 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -3,6 +3,8 @@ desc = "Danger. Very robust." icon = 'icons/obj/storage.dmi' icon_state = "red" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "toolbox_red" flags = CONDUCT force = 10.0 diff --git a/code/game/objects/items/weapons/syndie_RCD.dm b/code/game/objects/items/weapons/syndie_RCD.dm index 000db147e49..447b806f2c6 100644 --- a/code/game/objects/items/weapons/syndie_RCD.dm +++ b/code/game/objects/items/weapons/syndie_RCD.dm @@ -3,6 +3,8 @@ desc = "A device used to rapidly build and deconstruct walls, floors and airlocks. This one is made by syndicate" icon = 'icons/obj/tools.dmi' icon_state = "syndi_rcd" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "syndi_rcd" materials = list(MAT_PLASMA = 10000, MAT_TITANIUM = 10000, MAT_METAL = 20000) origin_tech = "engineering=4;materials=2;syndicate=4" diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 546d499cd76..8c7563aa155 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -314,13 +314,15 @@ /obj/item/tank/jetpack/suit/ninja/allow_thrust(num, use_fuel = TRUE) - var/mob/user = get_owner() + var/mob/living/user = get_owner() if(!user) return FALSE - if(!skip_trails && user.alpha == NINJA_ALPHA_INVISIBILITY) + + if(!skip_trails && user.alpha_get(ALPHA_SOURCE_NINJA) == standartize_alpha(NINJA_ALPHA_INVISIBILITY)) configure_jetpack(skip_trails = TRUE) - else if(skip_trails && user.alpha != NINJA_ALPHA_INVISIBILITY) + else if(skip_trails && user.alpha_get(ALPHA_SOURCE_NINJA) != standartize_alpha(NINJA_ALPHA_INVISIBILITY)) configure_jetpack(skip_trails = FALSE) + return ..() diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index 85a4c2e9fa4..b2be8e3a924 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -38,6 +38,9 @@ var/unwieldsound = FALSE var/sharp_when_wielded = FALSE + lefthand_file = 'icons/mob/inhands/twohanded_lefthand.dmi' + righthand_file = 'icons/mob/inhands/twohanded_righthand.dmi' + /obj/item/twohanded/Initialize(mapload) . = ..() @@ -271,10 +274,10 @@ return . if(prob(50)) - INVOKE_ASYNC(src, PROC_REF(jedi_spin), user) + INVOKE_ASYNC(src, GLOBAL_PROC_REF(jedi_spin), user) -/obj/item/twohanded/dualsaber/proc/jedi_spin(mob/living/user) +/proc/jedi_spin(mob/living/user) for(var/i in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH)) user.setDir(i) if(i == WEST) @@ -505,11 +508,6 @@ mounted_head = null qdel(src) -/obj/item/twohanded/spear/kidan - icon_state = "kidanspear0" - name = "Kidan spear" - desc = "A spear brought over from the Kidan homeworld." - // DIY CHAINSAW /obj/item/twohanded/required/chainsaw @@ -863,9 +861,9 @@ . = ..() if(isliving(user)) var/mob/living/U = user - if(U.mind && !U.mind.devilinfo && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul - U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \ - "\"As you pick up the [src] your arms ignite, reminding you of all your past sins.\"") + if(!U.mind?.has_antag_datum(/datum/antagonist/devil) && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul + U.visible_message(span_warning("As [U] picks [src] up, [U]'s arms briefly catch fire."), \ + span_warning("\"As you pick up the [src] your arms ignite, reminding you of all your past sins.\"")) if(ishuman(U)) var/mob/living/carbon/human/H = U H.apply_damage(rand(force/2, force), BURN, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) @@ -878,7 +876,7 @@ if(!ATTACK_CHAIN_SUCCESS_CHECK(.) || !HAS_TRAIT(src, TRAIT_WIELDED)) return . - if(!user.mind || user.mind.devilinfo || (user.mind.soulOwner == user.mind)) + if(user.mind?.has_antag_datum(/datum/antagonist/devil) || (user.mind.soulOwner == user.mind)) return . to_chat(user, span_warning("The [name] burns in your hands!")) diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 2d9b7ef9f96..8ac86ccbf6e 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -45,7 +45,7 @@ "You try to impale yourself with [src], but it's USELESS...") return SHAME -/obj/item/claymore +/obj/item/melee/claymore name = "claymore" desc = "What are you standing around staring at this for? Get to killing!" icon_state = "claymore" @@ -67,16 +67,16 @@ armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) resistance_flags = FIRE_PROOF -/obj/item/claymore/suicide_act(mob/user) +/obj/item/melee/claymore/suicide_act(mob/user) user.visible_message("[user] is falling on the [name]! It looks like [user.p_theyre()] trying to commit suicide.") return BRUTELOSS -/obj/item/claymore/ceremonial +/obj/item/melee/claymore/ceremonial name = "ceremonial claymore" desc = "An engraved and fancy version of the claymore. It appears to be less sharp than it's more functional cousin." force = 20 -/obj/item/katana +/obj/item/melee/katana name = "katana" desc = "Woefully underpowered in D20" icon_state = "katana" @@ -97,14 +97,13 @@ max_integrity = 200 armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) resistance_flags = FIRE_PROOF - needs_permit = TRUE -/obj/item/katana/suicide_act(mob/user) +/obj/item/melee/katana/suicide_act(mob/user) user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku.") return BRUTELOSS -/obj/item/katana/basalt +/obj/item/melee/katana/basalt name = "basalt katana" desc = "a katana made out of hardened basalt. Particularly damaging to lavaland fauna." icon_state = "basalt_katana" @@ -115,7 +114,7 @@ var/nemesis_factions = list("mining", "boss") -/obj/item/katana/basalt/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) +/obj/item/melee/katana/basalt/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) var/nemesis_faction = FALSE if(LAZYLEN(nemesis_factions)) for(var/faction in target.faction) @@ -206,15 +205,6 @@ materials = list(MAT_METAL=500, MAT_GLASS=500) resistance_flags = FIRE_PROOF -/obj/item/spear/kidan - icon_state = "kidanspear" - name = "Kidan spear" - desc = "A one-handed spear brought over from the Kidan homeworld." - icon_state = "kidanspear" - item_state = "kidanspear" - force = 10 - throwforce = 15 - /obj/item/melee/baseball_bat name = "baseball bat" desc = "There ain't a skull in the league that can withstand a swatter." @@ -438,7 +428,7 @@ toggle(user) -/obj/item/claymore/bone +/obj/item/melee/claymore/bone name = "bone sword" desc = "Jagged pieces of bone are tied to what looks like a goliath's femur." icon_state = "bone_sword" diff --git a/code/game/objects/items/weapons/welder_sword.dm b/code/game/objects/items/weapons/welder_sword.dm new file mode 100644 index 00000000000..01422cb44c4 --- /dev/null +++ b/code/game/objects/items/weapons/welder_sword.dm @@ -0,0 +1,146 @@ +/obj/item/weldingtool/sword + name = "welding sword" + desc = "Сварочный аппарат, кустарно модифицированный каким-то умельцем. Судя по всему, автор этого творения черпал вдохновение от энергетических мечей." + ru_names = list( + NOMINATIVE = "сварочный меч", + GENITIVE = "сварочного меча", + DATIVE = "сварочному мечу", + ACCUSATIVE = "сварочный меч", + INSTRUMENTAL = "сварочным мечом", + PREPOSITIONAL = "сварочном мече" + ) + icon = 'icons/obj/items.dmi' + icon_state = "fuelsword" + item_state = "fuelsword" + needs_permit = 1 + belt_icon = null + force_enabled = 30 + low_fuel_changes_icon = FALSE + block_chance = 50 + item_flags = NOSHARPENING + sharp = 1 + tool_behaviour = NONE + maximum_fuel = 50 + origin_tech = "combat=3;magnets=4;plasmatech=5;" + /// Сan be combined with other similar item + var/combinable = TRUE + + +/obj/item/weldingtool/sword/toggle_welder(turn_off) + . = ..() + if(tool_enabled) + tool_behaviour = NONE + else + tool_behaviour = TOOL_WELDER + +/obj/item/weldingtool/sword/update_icon_state() + . = ..() + if(tool_enabled) + icon_state = "[initial(item_state)]1" + else + icon_state = "[initial(item_state)]" + +/obj/item/weldingtool/sword/tool_use_check(mob/living/user, amount, silent) + return FALSE + +/obj/item/weldingtool/sword/afterattack(atom/target, mob/user, proximity, params, status) + . = ..() + if(ATTACK_CHAIN_SUCCESS_CHECK(status)) + remove_fuel(1) + +/obj/item/weldingtool/sword/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/weldingtool/sword) && combinable) + add_fingerprint(user) + var/obj/item/weldingtool/sword/sword = I + + if(!sword.combinable) + return ATTACK_CHAIN_PROCEED + + if(I == src) + to_chat(user, span_warning("Вы пытаетесь приделать конец меча к... мечу. Это было очень глупо.")) + user.apply_damage(10, BRAIN) + return ATTACK_CHAIN_PROCEED + + if(loc == user && !user.can_unEquip(src)) + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(I, src)) + return ATTACK_CHAIN_PROCEED + + balloon_alert(user, "скреплено вместе") + var/obj/item/weldingtool/sword/double/dual_sword = new(drop_location()) + user.temporarily_remove_item_from_inventory(src) + user.put_in_hands(dual_sword, ignore_anim = FALSE) + qdel(I) + qdel(src) + return ATTACK_CHAIN_BLOCKED_ALL + + return ..() + +/obj/item/weldingtool/sword/double + name = "double-bladed welding sword" + desc = "Два кустарно модифицированных сварочных аппарата, скреплённых вместе, образуя некое подобие двойного энергетического меча. Настоящее чудо ассистентской мысли." + ru_names = list( + NOMINATIVE = "двойной сварочный меч", + GENITIVE = "двойного сварочного меча", + DATIVE = "двойному сварочному мечу", + ACCUSATIVE = "двойной сварочный меч", + INSTRUMENTAL = "двойным сварочным мечом", + PREPOSITIONAL = "двойном сварочном мече" + ) + icon_state = "fuelsworddouble" + item_state = "fuelsworddouble" + lefthand_file = 'icons/mob/inhands/twohanded_lefthand.dmi' + righthand_file = 'icons/mob/inhands/twohanded_righthand.dmi' + force_enabled = 40 + force = 5 + block_chance = 75 + maximum_fuel = 70 + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + origin_tech = "combat=5;magnets=5;plasmatech=6;" + combinable = FALSE + +/obj/item/weldingtool/sword/double/ComponentInitialize() + AddComponent(/datum/component/two_handed, \ + force_unwielded = force, \ + force_wielded = force_enabled, \ + wieldsound = activation_sound, \ + unwieldsound = deactivation_sound, \ + sharp_when_wielded = TRUE, \ + wield_callback = CALLBACK(src, PROC_REF(wield)), \ + unwield_callback = CALLBACK(src, PROC_REF(unwield)), \ + ) + + +/obj/item/weldingtool/sword/double/proc/wield(obj/item/source, mob/living/carbon/user) + toggle_welder() + + +/obj/item/weldingtool/sword/double/proc/unwield(obj/item/source, mob/living/carbon/user) + toggle_welder() + + +/obj/item/weldingtool/sword/double/remove_fuel(amount) + reagents.remove_reagent("fuel", amount * requires_fuel) + if(!GET_FUEL && tool_enabled) + attack_self(usr) + + +/obj/item/weldingtool/sword/double/try_toggle_welder(mob/user, manual_toggle = TRUE) + return ..(user, manual_toggle = FALSE) + + +/obj/item/weldingtool/sword/double/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) + . = ..() + if(!ATTACK_CHAIN_SUCCESS_CHECK(.) || !HAS_TRAIT(src, TRAIT_WIELDED)) + return . + + if(prob(50)) + INVOKE_ASYNC(src, GLOBAL_PROC_REF(jedi_spin), user) + + +/obj/item/weldingtool/sword/double/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = ITEM_ATTACK) + if(tool_enabled) + return ..() + return FALSE + diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index a122e3cb4f5..094dc3a3d10 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -35,6 +35,44 @@ armor_protection = clamp(armor_protection - armour_penetration, min(armor_protection, 0), 100) return round(damage_amount * (100 - armor_protection)*0.01, DAMAGE_PRECISION) + +/// Proc for recovering atom_integrity. Returns the amount repaired by +/obj/proc/repair_damage(amount) + if(amount <= 0) // We only recover here + return + var/new_integrity = min(max_integrity, obj_integrity + amount) + . = new_integrity - obj_integrity + + update_integrity(new_integrity) + + +/// Handles the integrity of an obj changing. This must be called instead of changing integrity directly. +/obj/proc/update_integrity(new_value) + SHOULD_NOT_OVERRIDE(TRUE) + var/old_value = obj_integrity + new_value = max(0, new_value) + if(obj_integrity == new_value) + return + obj_integrity = new_value + on_update_integrity(old_value, new_value) + return new_value + +/// Handle updates to your obj's integrity +/obj/proc/on_update_integrity(old_value, new_value) + SHOULD_NOT_SLEEP(TRUE) + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_OBJ_INTEGRITY_CHANGED, old_value, new_value) + +/// This mostly exists to keep obj_integrity private. Might be useful in the future. +/obj/proc/get_integrity() + SHOULD_BE_PURE(TRUE) + return obj_integrity + +/// Similar to get_integrity, but returns the percentage as [0-1] instead. +/obj/proc/get_integrity_percentage() + SHOULD_BE_PURE(TRUE) + return round(obj_integrity / max_integrity, 0.01) + ///the sound played when the obj is damaged. /obj/proc/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) switch(damage_type) @@ -70,12 +108,16 @@ if(!QDELETED(src)) //Bullet on_hit effect might have already destroyed this object take_damage(P.damage, P.damage_type, P.flag, 0, turn(P.dir, 180), P.armour_penetration) + /obj/blob_act(obj/structure/blob/B) + if(!..() || (obj_flags & IGNORE_BLOB_ACT)) + return if(isturf(loc)) var/turf/T = loc if((T.intact && level == 1) || T.transparent_floor == TURF_TRANSPARENT) //the blob doesn't destroy thing below the floor return - take_damage(400, BRUTE, "melee", 0, get_dir(src, B)) + take_damage(400, BRUTE, MELEE, 0, get_dir(src, B)) + /obj/proc/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, armor_penetration = 0) //used by attack_alien, attack_animal, and attack_slime user.do_attack_animation(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 39214ef32ab..2230b731b94 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -73,7 +73,7 @@ /obj/proc/CouldNotUseTopic(mob/user) // Nada -/obj/Destroy() +/obj/Destroy(force) if(!ismachinery(src)) if(!speed_process) STOP_PROCESSING(SSobj, src) // TODO: Have a processing bitflag to reduce on unnecessary loops through the processing lists diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 3d220896144..bd10e723a69 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -32,7 +32,7 @@ ADD_TRAIT(loc, TRAIT_TURF_COVERED, UNIQUE_TRAIT_SOURCE(src)) return ..() -/obj/structure/Destroy() +/obj/structure/Destroy(force) if(SSticker) GLOB.cameranet.updateVisibility(src) if(smooth) diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 8ee0af3acea..fb068006c1c 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -32,6 +32,7 @@ GLOBAL_LIST_EMPTY(closets) var/locked = FALSE var/large = TRUE var/can_be_emaged = FALSE + var/can_weld_shut = TRUE var/wall_mounted = FALSE //never solid (You can always pass over it) var/lastbang var/open_sound = 'sound/machines/closet_open.ogg' @@ -78,11 +79,37 @@ GLOBAL_LIST_EMPTY(closets) break // Fix for #383 - C4 deleting fridges with corpses -/obj/structure/closet/Destroy() +/obj/structure/closet/Destroy(force) GLOB.closets -= src + if(force) + for(var/atom/movable/thing in contents) + qdel(thing, force) + + return ..() + dump_contents() return ..() +/obj/structure/closet/vv_edit_var(vname, vval) + if(vname == NAMEOF(src, opened)) + if(vval == opened) + return FALSE + if(vval && !opened && open()) + datum_flags |= DF_VAR_EDITED + return TRUE + else if(!vval && opened && close()) + datum_flags |= DF_VAR_EDITED + return TRUE + return FALSE + . = ..() + if(vname == NAMEOF(src, welded) && welded && !can_weld_shut) + can_weld_shut = TRUE + else if(vname == NAMEOF(src, can_weld_shut) && !can_weld_shut && welded) + welded = FALSE + update_appearance() + if(vname in list(NAMEOF(src, locked), NAMEOF(src, welded))) + update_appearance() + /obj/structure/closet/CanAllowThrough(atom/movable/mover, border_dir) . = ..() @@ -132,6 +159,9 @@ GLOBAL_LIST_EMPTY(closets) after_open() return TRUE +/obj/structure/closet/setOpened() + open() + ///Proc to override for effects after opening a door /obj/structure/closet/proc/after_open(mob/living/user, force = FALSE) return @@ -182,6 +212,9 @@ GLOBAL_LIST_EMPTY(closets) set_density(ignore_density_closed ? FALSE : TRUE) return TRUE +/obj/structure/closet/setClosed() + close() + /obj/structure/closet/proc/toggle(mob/user) . = TRUE if(!(opened ? close() : open())) @@ -263,6 +296,8 @@ GLOBAL_LIST_EMPTY(closets) deconstruct(TRUE) return else + if(!can_weld_shut) + return var/adjective = welded ? "open" : "shut" user.visible_message("[user] begins welding [src] [adjective]...", "You begin welding [src] [adjective]...", "You hear welding.") if(I.use_tool(src, user, 15, volume = I.tool_volume)) @@ -447,10 +482,13 @@ GLOBAL_LIST_EMPTY(closets) user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) /obj/structure/closet/ex_act(severity) + contents_explosion() + ..() + +/obj/structure/closet/proc/contents_explosion(severity) for(var/atom/A in contents) A.ex_act(severity) CHECK_TICK - ..() /obj/structure/closet/singularity_act() dump_contents() diff --git a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm index 7d989bf5e9a..4dff2175d22 100644 --- a/code/game/objects/structures/crates_lockers/closets/fireaxe.dm +++ b/code/game/objects/structures/crates_lockers/closets/fireaxe.dm @@ -146,6 +146,11 @@ operate_panel() +/obj/structure/closet/fireaxecabinet/blob_act(obj/structure/blob/B) + if(fireaxe) + fireaxe.forceMove(loc) + qdel(src) + /obj/structure/closet/fireaxecabinet/attack_tk(mob/user) if(localopened && fireaxe) fireaxe.forceMove(loc) @@ -251,6 +256,11 @@ return ..() +/obj/structure/fishingrodcabinet/blob_act(obj/structure/blob/B) + if(olreliable) + olreliable.forceMove(loc) + qdel(src) + /obj/structure/fishingrodcabinet/attack_hand(mob/user) if(!olreliable) return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index f67520c13a8..3cb7dad46ae 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -51,7 +51,7 @@ new /obj/item/cartridge/hos(src) new /obj/item/radio/headset/heads/hos/alt(src) new /obj/item/clothing/glasses/hud/security/sunglasses(src) - new /obj/item/clothing/gloves/combat(src) + new /obj/item/clothing/gloves/combat/swat(src) new /obj/item/storage/lockbox/mindshield(src) new /obj/item/storage/box/flashbangs(src) new /obj/item/holosign_creator/security(src) @@ -163,11 +163,8 @@ new /obj/item/storage/firstaid/adv(src) new /obj/item/pinpointer/crew(src) new /obj/item/storage/belt/security/sec(src) + new /obj/item/clothing/gloves/combat/swat(src) new /obj/item/flashlight/seclite(src) - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/glasses/hud/security/sunglasses/read_only(src) - new /obj/item/clothing/glasses/hud/health/sunglasses(src) - new /obj/item/clothing/glasses/hud/skills/sunglasses(src) new /obj/item/clothing/accessory/holster(src) new /obj/item/clothing/mask/gas/sechailer/blue(src) new /obj/item/clothing/mask/gas/sechailer(src) diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 136b42d4f9e..a2825fb3edf 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -48,7 +48,10 @@ if(istype(I, /obj/item/toy/crayon)) add_fingerprint(user) - color = input(user, "Choose Color") as color + var/new_color = tgui_input_color(user, "Choose Color") + if(isnull(new_color)) + return ATTACK_CHAIN_PROCEED + color = new_color return ATTACK_CHAIN_PROCEED_SUCCESS return ..() diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index 5653ed75f64..5dde6eabf88 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -31,8 +31,8 @@ if(new_underwear) var/datum/sprite_accessory/underwear/uwear = GLOB.underwear_list[new_underwear] if(uwear.allow_change_color) - var/new_underwear_color = input(user, "Choose your underwear color, else color will be white:", "Changing", "#ffffff") as color|null - H.color_underwear = new_underwear_color || "#ffffff" + var/new_underwear_color = tgui_input_color(user, "Choose your underwear color, else color will be white:", "Changing", "#ffffff") + H.color_underwear = isnull(new_underwear_color) ? "#ffffff" : new_underwear_color H.underwear = new_underwear if("Undershirt") @@ -49,8 +49,8 @@ if(new_undershirt) var/datum/sprite_accessory/undershirt/ushirt = GLOB.undershirt_list[new_undershirt] if(ushirt.allow_change_color) - var/new_undershirt_color = input(user, "Choose your undershirt color, else color will be white:", "Changing", "#ffffff") as color|null - H.color_undershirt = new_undershirt_color || "#ffffff" + var/new_undershirt_color = tgui_input_color(user, "Choose your undershirt color, else color will be white:", "Changing", "#ffffff") + H.color_undershirt = isnull(new_undershirt_color) ? "#ffffff" : new_undershirt_color H.undershirt = new_undershirt if("Socks") diff --git a/code/game/objects/structures/lavaland/katana_grave.dm b/code/game/objects/structures/lavaland/katana_grave.dm index d8e5f10d826..ed05b9458fa 100644 --- a/code/game/objects/structures/lavaland/katana_grave.dm +++ b/code/game/objects/structures/lavaland/katana_grave.dm @@ -23,5 +23,5 @@ qdel(src) /obj/structure/katana_grave/basalt - dropping_item = /obj/item/katana/basalt + dropping_item = /obj/item/melee/katana/basalt icon_state = "grave_katana_basalt" diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 87f06272e8a..ec23bc71d5b 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -8,7 +8,7 @@ anchored = TRUE max_integrity = 200 integrity_failure = 100 - flags = CHECK_RICOCHET + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD var/list/ui_users = list() /obj/structure/mirror/Initialize(mapload, newdir = SOUTH, building = FALSE) @@ -93,17 +93,9 @@ return FALSE else if(prob(70)) return FALSE + + return ..() - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(get_turf(src), p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.set_angle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE /obj/item/mounted/mirror name = "mirror" diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 5361a73e4bd..035365cd653 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -184,6 +184,14 @@ name = "statue of the research director" icon_state = "rd" +/obj/structure/statue/gold/unathi + name = "statue of the unati" + icon_state = "unathi" + +/obj/structure/statue/gold/tajaran + name = "statue of the tajaran" + icon_state = "tajaran" + /obj/structure/statue/silver max_integrity = 300 material_drop_type = /obj/item/stack/sheet/mineral/silver diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index fd275dccef0..15e7f8f1b47 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -587,6 +587,10 @@ user.visible_message("[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src].", \ "You wash your [washing_face ? "face" : "hands"] using [src].") + + if(SEND_SIGNAL(user, COMSIG_SINK_ACT) & COMSIG_SINK_ACT_SUCCESS) // special sink acts + return + if(washing_face) if(ishuman(user)) var/mob/living/carbon/human/H = user diff --git a/code/game/turfs/simulated/floor/plating.dm b/code/game/turfs/simulated/floor/plating.dm index 65d85a33951..5a880ab6d23 100644 --- a/code/game/turfs/simulated/floor/plating.dm +++ b/code/game/turfs/simulated/floor/plating.dm @@ -176,6 +176,10 @@ barefootstep = FOOTSTEP_HARD_BAREFOOT clawfootstep = FOOTSTEP_HARD_CLAW heavyfootstep = FOOTSTEP_GENERIC_HEAVY + +/turf/simulated/floor/engine/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 3) /turf/simulated/floor/engine/break_tile() return //unbreakable @@ -239,9 +243,9 @@ if(prob(50)) ChangeTurf(baseturf) -/turf/simulated/floor/engine/blob_act(obj/structure/blob/B) - if(prob(25)) - ChangeTurf(baseturf) + +/turf/simulated/floor/engine/blob_consume() + ChangeTurf(baseturf) /turf/simulated/floor/engine/cult name = "engraved floor" diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 40929c1f917..e2e4a24ad6a 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -47,6 +47,9 @@ if(istype(T, /turf/simulated/mineral/random)) Spread(T) +/turf/simulated/mineral/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 2) /// Generates typecache of tools allowed to dig this mineral /turf/simulated/mineral/proc/generate_picks() @@ -198,6 +201,9 @@ if(1) attempt_drill(null,TRUE,3) +/turf/simulated/mineral/blob_consume() + gets_drilled() + /turf/simulated/mineral/ancient name = "ancient rock" desc = "A rare asteroid rock that appears to be resistant to all mining tools except pickaxes!" diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 3dc4787b133..b6d747e76fd 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -64,6 +64,10 @@ underlay_appearance.icon_state = fixed_underlay["icon_state"] fixed_underlay = string_assoc_list(fixed_underlay) underlays += underlay_appearance + +/turf/simulated/wall/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 2) //Appearance /turf/simulated/wall/examine(mob/user) // If you change this, consider changing the examine_status proc of false walls to match @@ -136,16 +140,6 @@ if(radiated_temperature > max_temperature) take_damage(rand(10, 20) * (radiated_temperature / max_temperature)) -/turf/simulated/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode! - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.set_angle(new_angle_s) - return TRUE /turf/simulated/wall/dismantle_wall(devastated = FALSE, explode = FALSE) if(devastated) @@ -183,10 +177,10 @@ return /turf/simulated/wall/blob_act(obj/structure/blob/B) - if(prob(50)) - dismantle_wall() - else - add_dent(WALL_DENT_HIT) + add_dent(WALL_DENT_HIT) + +/turf/simulated/wall/blob_consume() + dismantle_wall() /turf/simulated/wall/rpd_act(mob/user, obj/item/rpd/our_rpd) if(our_rpd.mode == RPD_ATMOS_MODE) diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm index 4a886c24c34..43c89d59964 100644 --- a/code/game/turfs/simulated/walls_mineral.dm +++ b/code/game/turfs/simulated/walls_mineral.dm @@ -235,7 +235,7 @@ icon_state = "shuttle-0" base_icon_state = "shuttle" explosion_block = 3 - flags = CHECK_RICOCHET + flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD sheet_type = /obj/item/stack/sheet/mineral/titanium smooth = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS canSmoothWith = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE + SMOOTH_GROUP_AIRLOCK diff --git a/code/game/turfs/simulated/walls_reinforced.dm b/code/game/turfs/simulated/walls_reinforced.dm index c9c41156ce9..9a1f52f7400 100644 --- a/code/game/turfs/simulated/walls_reinforced.dm +++ b/code/game/turfs/simulated/walls_reinforced.dm @@ -21,6 +21,11 @@ var/d_state = RWALL_INTACT var/can_be_reinforced = 1 +/turf/simulated/wall/r_wall/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 3) + + /turf/simulated/wall/r_wall/examine(mob/user) . = ..() switch(d_state) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 9bf35efa8e9..b45793f8831 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -59,9 +59,13 @@ if(opacity) directional_opacity = ALL_CARDINALS - + ComponentInitialize() return INITIALIZE_HINT_NORMAL +/turf/space/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 4) + /turf/space/BeforeChange() ..() var/datum/space_level/S = GLOB.space_manager.get_zlev(z) @@ -165,6 +169,8 @@ while(current_pull) var/turf/target_turf = get_step(current_pull.pulledby.loc, REVERSE_DIR(current_pull.pulledby.dir)) || current_pull.pulledby.loc current_pull.zMove(null, target_turf, ZMOVE_ALLOW_BUCKLED) + if(current_pull.pulling == arrived) // pulling each other doesn't help but makes a loop + break current_pull = current_pull.pulling diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 6800b4e398e..9cb0a88d316 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -125,9 +125,14 @@ if(istype(loc, /area/space)) force_no_gravity = TRUE - + + ComponentInitialize() return INITIALIZE_HINT_NORMAL +/turf/ComponentInitialize() + . = ..() + AddComponent(/datum/component/blob_turf_consuming, 0) + /turf/Destroy(force) . = QDEL_HINT_IWILLGC if(!changing_turf) @@ -181,6 +186,9 @@ /turf/ex_act(severity) return FALSE +/turf/proc/blob_consume() + return + /turf/rpd_act(mob/user, obj/item/rpd/our_rpd) //This is the default turf behaviour for the RPD; override it as required if(our_rpd.mode == RPD_ATMOS_MODE) our_rpd.create_atmos_pipe(user, src) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 5cd8ffa5b59..8111891139d 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -7,6 +7,7 @@ GLOBAL_LIST_INIT(admin_verbs_default, list( )) GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/check_antagonists, /*shows all antags*/ + /client/proc/check_security, /*shows all security*/ /datum/admins/proc/show_player_panel, /client/proc/fax_panel, /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ @@ -111,6 +112,7 @@ GLOBAL_LIST_INIT(admin_verbs_event, list( /client/proc/cmd_admin_headset_message, /client/proc/force_hijack, /client/proc/requests, + /client/proc/centcom_podlauncher, /*Open a window to launch a Supplypod and configure it or it's contents*/ )) GLOBAL_LIST_INIT(admin_verbs_spawn, list( /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ @@ -404,6 +406,17 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Antags") //If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! return +/client/proc/check_security() + set name = "Check Security" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.check_security() + log_admin("[key_name(usr)] checked security") + SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Secs") //If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! + /client/proc/ban_panel() set name = "Ban Panel" set category = "Admin" @@ -501,34 +514,34 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( /client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE set category = "Event" set name = "Drop Bomb" - set desc = "Cause an explosion of varying strength at your location." + set desc = "Вызвать взрыв различной силы под вами." if(!check_rights(R_EVENT)) return var/turf/epicenter = mob.loc - var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb") - var/choice = input("What size explosion would you like to produce?") as null|anything in choices + var/list/choices = list("Маленькая бомба", "Средняя бомба", "Большая бомба", "Настраиваемая бомба") + var/choice = tgui_input_list(src, "Взрыв какого размера вы хотели бы произвести? ПРИМЕЧАНИЕ. Вы можете сделать все это в IC поле (используя крылатые ракеты!) с помощью кнопки Event/Launch Supplypod.", items = choices) switch(choice) if(null) return 0 - if("Small Bomb") + if("Маленькая бомба") explosion(epicenter, 1, 2, 3, 3, cause = "Admin Drop Bomb") - if("Medium Bomb") + if("Средняя бомба") explosion(epicenter, 2, 3, 4, 4, cause = "Admin Drop Bomb") - if("Big Bomb") + if("Большая бомба") explosion(epicenter, 3, 5, 7, 5, cause = "Admin Drop Bomb") - if("Custom Bomb") - var/devastation_range = tgui_input_number(src, "Devastation range (in tiles):", "Custom Bomb", max_value = 255) + if("Настраиваемая бомба") + var/devastation_range = tgui_input_number(src, "Дальность тотального разрушения. (в тайлах):", "Настраиваемая бомба", max_value = 255) if(isnull(devastation_range)) return - var/heavy_impact_range = tgui_input_number(src, "Heavy impact range (in tiles):", "Custom Bomb", max_value = 255) + var/heavy_impact_range = tgui_input_number(src, "Дальность сильного удара. (в тайлах):", "Настраиваемая бомба", max_value = 255) if(isnull(heavy_impact_range)) return - var/light_impact_range = tgui_input_number(src, "Light impact range (in tiles):", "Custom Bomb", max_value = 255) + var/light_impact_range = tgui_input_number(src, "Дальность легкого удара. (в тайлах):", "Настраиваемая бомба", max_value = 255) if(isnull(light_impact_range)) return - var/flash_range = tgui_input_number(src, "Flash range (in tiles):", "Custom Bomb", max_value = 255) + var/flash_range = tgui_input_number(src, "Дальность вспышки. (в тайлах):", "Настраиваемая бомба", max_value = 255) if(isnull(flash_range)) return explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, 1, 1, cause = "Admin Drop Bomb") @@ -1252,8 +1265,8 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list( return message = strip_html(message, 500) - var/message_color = input(src, "Input your message color:", "Color Selector") as color|null - if(!message_color) + var/message_color = tgui_input_color(src, "Input your message color:", "Color Selector") + if(isnull(message_color)) return var/alert_type2 = alert(src, "Do you wish to change speed of an admin alert to? (No - default speed)",,"Yes", "No") diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index b1140ff264e..fb879f7f2cf 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -425,7 +425,7 @@ if(blob_infected && blob_infected.len) var/datum/game_mode/mode = SSticker.mode dat += "
" - dat += "" + dat += "" dat += "" dat += "" dat += "" @@ -433,6 +433,7 @@ dat += "" dat += "" dat += "" + dat += "" dat += "
Blob
Progress: [GLOB.blobs.len]/[mode.blob_win_count]
Progress: [mode.legit_blobs.len]/[mode.blob_win_count]
Edit Win Count
Send warning to all living blobs
Burst all blobs
Delay blob end Now: [mode.delay_blob_end? "ON" : "OFF"]
Toggle auto GAMMA Now: [mode.off_auto_gamma? "OFF" : "ON"]
Toggle auto nuke codes Now: [mode.off_auto_nuke_codes? "OFF" : "ON"]
Toggle blob infinity points Now: [mode.is_blob_infinity_points? "ON" : "OFF"]
" dat += "
" for(var/datum/mind/blob in mode.blobs["infected"]) @@ -454,14 +455,14 @@ dat += "
Blobs
" - dat += "
" - for(var/datum/mind/blob in mode.blobs["blobernauts"]) + dat += "
Blobernauts
" + for(var/datum/mind/blob in mode.blobs["minions"]) var/mob/M = blob.current if(M) dat += "" dat += "" else - dat += "" + dat += "" dat += "
Minions
[ADMIN_PP(M,"[M.real_name]")][M.client ? "" : " (ghost)"][M.stat == 2 ? " (DEAD)" : ""]PM
Blobernauts not found!
Minions not found!
" @@ -650,3 +651,54 @@ txt += "" return txt + +/datum/admins/proc/check_security_line(mob/living/human, close = 1) + var/logout_status = human.client ? "" : " (logged out)" + var/list/coords = ATOM_COORDS(human) + var/job = issilicon(human) ? "Cyborg" : human.job // || need because maybe ert robots with null in job + return {"[human.real_name][logout_status][job][human.stat == DEAD ? " (Dead)" : " [human.health]%"] [get_area_name(human)] [coords[1]],[coords[2]],[coords[3]]PM [ADMIN_FLW(human, "FLW")][close ? "" : ""]"} + +/datum/admins/proc/check_security() + if(!check_rights(R_ADMIN)) + return + if(!SSticker || SSticker.current_state < GAME_STATE_PLAYING) + return + + var/dat = {"Round Status

Round Status

"} + var/list/sec_list = check_active_security_force() + dat += "
" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "
Security
Total: [sec_list[1]]
Active: [sec_list[2]]
Dead: [sec_list[3]]
Antag: [sec_list[4]]
" + dat += "" + + dat += "
" + for(var/datum/mind/mind in SSticker.mode.get_all_sec()) + if(mind.current) + dat += check_security_line(mind.current) + dat += "
Security
" + + if(SSticker.mode.ert.len) + dat += check_role_table_sec("ERT", SSticker.mode.ert) + + usr << browse(dat, "window=roundstatus;size=600x800") + +/datum/admins/proc/check_role_table_sec(name, list/members, show_objectives=0) + var/txt = "
" + for(var/datum/mind/mind in members) + txt += check_role_table_row_sec(mind.current, show_objectives) + txt += "
[name]
" + return txt + +/datum/admins/proc/check_role_table_row_sec(mob/mob, show_objectives) + var/txt = check_security_line(mob, close = 0) + if(show_objectives) + txt += {" + + Show Objective + + "} + txt += "" + return txt diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index ac4ef3e2fd5..467e94cbd3e 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1964,8 +1964,7 @@ else if(href_list["edit_blob_win_count"]) if(!check_rights(R_ADMIN)) return - - var/blob_win_count = input(usr, "Ввидите новое число критической массы","Критическая масса:", SSticker.mode.blob_win_count) as num + var/blob_win_count = tgui_input_number(usr, "Ввидите новое число критической массы", "Критическая масса:" , SSticker.mode.blob_win_count) if(!blob_win_count) return @@ -1980,9 +1979,9 @@ else if(href_list["send_warning"]) if(!check_rights(R_ADMIN)) return - - var/message = stripped_input(usr, "Введите предупреждение", "Предупреждение") - if(alert(usr,"Вы действительно хотите отправить предупреждение всем блобам?", "", "Да", "Нет") == "Нет") + + var/message = tgui_input_text(usr, "Введите предупреждение", "Предупреждение") + if(tgui_alert(usr,"Вы действительно хотите отправить предупреждение всем блобам?", "", list("Да", "Нет")) == "Нет") return if(!SSticker || !SSticker.mode) @@ -1996,7 +1995,7 @@ if(!check_rights(R_ADMIN)) return - if(alert(usr,"Вы действительно хотите лопнуть всех блобов?", "", "Да", "Нет") == "Нет") + if(tgui_alert(usr,"Вы действительно хотите лопнуть всех блобов?", "", list("Да", "Нет")) == "Нет") return if(!SSticker || !SSticker.mode) @@ -2025,6 +2024,22 @@ log_admin("[key_name(usr)] has [mode.delay_blob_end? "stopped" : "returned"] stopped delayed blob win") message_admins("[key_name_admin(usr)] has [mode.delay_blob_end? "stopped" : "returned"] delayed blob win") + else if(href_list["toggle_blob_infinity_points"]) + if(!check_rights(R_ADMIN)) + return + + if(!SSticker || !SSticker.mode) + return + + var/datum/game_mode/mode = SSticker.mode + if(tgui_alert(usr,"Вы действительно хотите [mode.is_blob_infinity_points? "убрать" : "вернуть"] бесконечные очки у блобов?", "", list("Да", "Нет")) == "Нет") + return + + mode.is_blob_infinity_points = !mode.is_blob_infinity_points + + log_admin("[key_name(usr)] has [mode.is_blob_infinity_points? "remove" : "returned"] blob infinity points") + message_admins("[key_name_admin(usr)] has [mode.is_blob_infinity_points? "remove" : "returned"] blob infinity points") + else if(href_list["toggle_auto_nuke_codes"]) if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/verbs/cantcomm_cargo.dm b/code/modules/admin/verbs/cantcomm_cargo.dm new file mode 100644 index 00000000000..d40f8383298 --- /dev/null +++ b/code/modules/admin/verbs/cantcomm_cargo.dm @@ -0,0 +1,18 @@ +/client/proc/centcom_podlauncher() + set name = "Launch Supplypod" + set category = "Event" + set desc = "Настройте и запустите капсулу снабжения, полную всего, что душе угодно!" + + if(!check_rights(R_EVENT)) + return + + if(!SSticker) + to_chat(usr, span_warning("Игра еще не началась!")) + return + + if(SSticker.current_state <= GAME_STATE_PREGAME) + to_chat(usr, span_warning("Раунд еще не начался!")) + return + + var/datum/centcom_podlauncher/E = new(src.mob) + E.ui_interact(usr) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index c13e06fa52a..17c320267a6 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -212,7 +212,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom Proc-Call") //If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! /client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null + var/argnum = tgui_input_number(src, "Введите число аргументов","Число аргументов:", 0) if(argnum <= 0) return list() // to allow for calling with 0 args @@ -222,53 +222,30 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) //TODO: make a list to store whether each argument was initialised as null. //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists //this will protect us from a fair few errors ~Carn - + var/extra_classes = list("type", "reference", "mob's area", "CANCEL") while(argnum--) - var/class = null + var/value = vv_get_value(extra_classes = extra_classes) + + if(!(value["class"] in extra_classes)) + lst += value["value"] + continue + + var/class = value["class"] // Make a list with each index containing one variable, to be given to the proc - if(src.holder && src.holder.marked_datum) - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") - if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") switch(class) if("CANCEL") return null - if("text") - lst += clean_input("Enter new text:","Text",null) - - if("num") - lst += input("Enter new number:","Num",0) as num - if("type") - lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) + lst += tgui_input_list(src, "Выберите тип:", "Тип", typesof(/obj,/mob,/area,/turf)) if("reference") - lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world - - if("mob reference") - lst += input("Select reference:","Reference",usr) as mob in world - - if("file") - lst += input("Pick file:","File") as file - - if("icon") - lst += input("Pick icon:","Icon") as icon - - if("client") - var/list/keys = list() - for(var/mob/M in world) - keys += M.client - lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys + lst += input(src, "Выберите ссылку:", "Ссылка",src) as mob|obj|turf|area in world if("mob's area") - var/mob/temp = input("Select mob", "Selection", usr) as mob in world + var/mob/temp = input(src, "Выберите моба", "Выбор", usr) as mob in world lst += temp.loc - if("Marked datum") - lst += holder.marked_datum return lst /client/proc/Cell() diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm index d6fc8e06de5..204d6d74966 100644 --- a/code/modules/admin/verbs/massmodvar.dm +++ b/code/modules/admin/verbs/massmodvar.dm @@ -61,14 +61,14 @@ if(prompt != "Continue") return - default = vv_get_class(var_value) + default = vv_get_class(variable, var_value) if(isnull(default)) to_chat(src, "Unable to determine variable type.") else to_chat(src, "Variable appears to be [uppertext(default)].") - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [translate_bitfield(default, variable, var_value)]") if(default == VV_NUM) var/dir_text = "" @@ -85,7 +85,7 @@ if(dir_text) to_chat(src, "If a direction, direction is: [dir_text]") - var/value = vv_get_value(default_class = default) + var/value = vv_get_value(class = (default == VV_BITFIELD ? VV_BITFIELD : null), default_class = default, var_name = variable) var/new_value = value["value"] var/class = value["class"] @@ -203,7 +203,7 @@ log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [html_encode(translate_bitfield(default, variable, O.vars[variable]))] (Type: [class]) ([accepted] objects modified)") /proc/get_all_of_type(var/T, subtypes = TRUE) var/list/typecache = list() diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 0bce874e498..964fe45f7ec 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -2,12 +2,15 @@ GLOBAL_LIST_INIT(VVlocked, list("vars", "var_edited", "client", "firemut", "ishu GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) // R_EVENT | R_DEBUG GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) // R_EVENT | R_DEBUG GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_height", "bound_width", "bound_x", "bound_y")) // R_DEBUG + warning -/client/proc/vv_get_class(var/var_value) +/client/proc/vv_get_class(var_name, var_value) if(isnull(var_value)) . = VV_NULL else if(isnum(var_value)) - . = VV_NUM + if(var_name in GLOB.bitfields) + . = VV_BITFIELD + else + . = VV_NUM else if(istext(var_value)) if(findtext(var_value, "\n")) @@ -51,7 +54,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h else . = VV_NULL -/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes, var_name) . = list("class" = class, "value" = null) if(!class) if(!classes) @@ -86,30 +89,35 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h if(extra_classes) classes += extra_classes - .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + .["class"] = tgui_input_list(src, "Какой тип данных?", "Тип переменной", classes, default_class) if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") .["class"] = VV_MARKED_DATUM switch(.["class"]) if(VV_TEXT) - .["value"] = input("Enter new text:", "Text", current_value) as null|text + .["value"] = tgui_input_text(src, "Введите текст:", "Текст", current_value) if(.["value"] == null) .["class"] = null return if(VV_MESSAGE) - .["value"] = input("Enter new text:", "Text", current_value) as null|message + .["value"] = tgui_input_text(src, "Введите текст:", "Текст", current_value, multiline = TRUE) if(.["value"] == null) .["class"] = null return if(VV_NUM) - .["value"] = input("Enter new number:", "Num", current_value) as null|num + .["value"] = tgui_input_number(src, "Введите число:", "Число", current_value) if(.["value"] == null) .["class"] = null return + if(VV_BITFIELD) + .["value"] = input_bitfield(usr, var_name, current_value) + if(.["value"] == null) + .["class"] = null + return if(VV_ATOM_TYPE) .["value"] = pick_closest_path(FALSE) @@ -127,11 +135,11 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h var/type = current_value var/error = "" do - type = input("Enter type:[error]", "Type", type) as null|text + type = tgui_input_text(src, "Введите тип:[error]", "Тип", type) if(!type) break type = text2path(type) - error = "\nType not found, Please try again" + error = "\nТип не найден, Попробуйте снова" while(!type) if(!type) .["class"] = null @@ -139,13 +147,13 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h .["value"] = type if(VV_MATRIX) - .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) + .["value"] = text2matrix(tgui_input_text(src, "Введите a, b, c, d, e, и f, разделённые пробелами.", "Матрица", "1 0 0 0 1 0")) if(.["value"] == null) .["class"] = null return if(VV_REGEX) - var/reg = input("Enter regex", "Regex", "") as null|text + var/reg = tgui_input_text(src, "Введите regex", "Regex", "") if(!reg) return .["value"] = regex(reg) @@ -160,7 +168,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h .["class"] = null return var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things + var/value = tgui_input_list(src, "Выберите ссылку:", "Ссылка", things, current_value) if(!value) .["class"] = null return @@ -173,7 +181,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h .["class"] = null return var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things + var/value = tgui_input_list(src, "Выберите ссылку:", "Ссылка", things, current_value) if(!value) .["class"] = null return @@ -186,7 +194,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h .["class"] = null return var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things + var/value = tgui_input_list(src, "Выберите ссылку:", "Ссылка", things, current_value) if(!value) .["class"] = null return @@ -195,21 +203,21 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h if(VV_CLIENT) - .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients + .["value"] = tgui_input_list(src, "Выберите клитент:", "Клиент", GLOB.clients, current_value) if(.["value"] == null) .["class"] = null return if(VV_FILE) - .["value"] = input("Pick file:", "File") as null|file + .["value"] = input(src, "Выберите файл:", "Файл") as null|file if(.["value"] == null) .["class"] = null return if(VV_ICON) - .["value"] = input("Pick icon:", "Icon") as null|icon + .["value"] = input(src, "Выберите иконку:", "Иконка") as null|icon if(.["value"] == null) .["class"] = null return @@ -241,11 +249,11 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h var/type = current_value var/error = "" do - type = input("Enter type:[error]", "Type", type) as null|text + type = tgui_input_text(src, "Введите тип:[error]", "Тип", type) if(!type) break type = text2path(type) - error = "\nType not found, Please try again" + error = "\nТип не найден, Попробуйте снова" while(!type) if(!type) .["class"] = null @@ -441,7 +449,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h else variable = L[index] - default = vv_get_class(variable) + default = vv_get_class(objectvar, variable) to_chat(src, "Variable appears to be [uppertext(default)].") @@ -567,14 +575,14 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h var_value = O.vars[variable] - var/default = vv_get_class(var_value) + var/default = vv_get_class(variable, var_value) if(isnull(default)) to_chat(src, "Unable to determine variable type.") else to_chat(src, "Variable appears to be [uppertext(default)].") - to_chat(src, "Variable contains: [var_value]") + to_chat(src, "Variable contains: [translate_bitfield(default, variable, var_value)]") if(default == VV_NUM) var/dir_text = "" @@ -596,7 +604,7 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h default = VV_MESSAGE class = default - var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST)) + var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST), var_name = variable) class = value["class"] if(!class) @@ -630,5 +638,5 @@ GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_h SEND_GLOBAL_SIGNAL(COMSIG_GLOB_VAR_EDIT, args) log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") - var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" + var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [html_encode(translate_bitfield(default, variable, var_new))] (Type: [class])" message_admins(msg) diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index 178e0de00fc..6b388152b28 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -36,7 +36,7 @@ H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), ITEM_SLOT_CLOTH_INNER) H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), ITEM_SLOT_EAR_LEFT) H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), ITEM_SLOT_HEAD) - H.equip_to_slot_or_del(new /obj/item/claymore/highlander(H), ITEM_SLOT_HAND_RIGHT) + H.equip_to_slot_or_del(new /obj/item/melee/claymore/highlander(H), ITEM_SLOT_HAND_RIGHT) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), ITEM_SLOT_FEET) H.equip_to_slot_or_del(new /obj/item/pinpointer(H.loc), ITEM_SLOT_POCKET_LEFT) diff --git a/code/modules/admin/verbs/reagents_editor.dm b/code/modules/admin/verbs/reagents_editor.dm new file mode 100644 index 00000000000..109cf99d794 --- /dev/null +++ b/code/modules/admin/verbs/reagents_editor.dm @@ -0,0 +1,114 @@ +/datum/reagents_editor + var/atom/target + /// Indexed by target.UID + var/static/list/datum/reagents_editor/editors = list() + +/datum/reagents_editor/New(atom/target) + src.target = target + +/datum/reagents_editor/ui_state(mob/user) + return GLOB.admin_state + +/datum/reagents_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ReagentsEditor") + ui.open() + ui.set_autoupdate(FALSE) + +/datum/reagents_editor/ui_close(mob/user) + var/open_uis = SStgui.open_uis_by_src[src.UID()] + if(isnull(open_uis) || !islist(open_uis) || length(open_uis) <= 1) + // Remove after everyone closes UI to avoid memory leak + editors -= target.UID() + +/datum/reagents_editor/ui_static_data(mob/user) + . = ..() + + var/list/reagents_information = list() + .["reagentsInformation"] = reagents_information + for(var/id in GLOB.chemical_reagents_list) + var/datum/reagent/R = GLOB.chemical_reagents_list[id] + reagents_information[id] = list( + "name" = R.name, + ) + +/datum/reagents_editor/ui_data(mob/user) + . = ..() + + var/list/reagents = list() + .["reagents"] = reagents + + if(!target.reagents) + return + + for(var/datum/reagent/R in target.reagents.reagent_list) + reagents[R.id] = list( + "volume" = R.volume, + "uid" = R.UID(), + ) + +// This interface intentionally emulates VV. +// It should, therefore, doesn't place restrictions on any actions, which includes but +// is not limited to: adding a reagent which overfills the container and adding blood +// with a non-existent blood type. It may also do things unconventionally, such as +// directly appending reagents to the list rather than using reagents.add_reagent to +// bypass reagent reactions. +/datum/reagents_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + + if(.) + return + + . = TRUE + + switch(action) + if("add_reagent") + var/reagent_id = params["reagentID"] + var/datum/reagent/reagent_prototype = GLOB.chemical_reagents_list[reagent_id] + if(isnull(reagent_prototype)) + return FALSE + var/new_volume = tgui_input_number(ui.user, "Сколько юнитов этого реагента вы хотите добавить?", "Добавить реагент", 0, 1E100, -1E100) + var/datum/reagent/reagent = target.reagents.get_reagent_by_id(reagent_id) + if(isnull(reagent)) + reagent = new reagent_prototype.type() + reagent.holder = target.reagents + reagent.on_new() + if(ishuman(target) && target.reagents.can_metabolize(target, reagent)) + reagent.on_mob_add(target) + target.reagents.reagent_list += reagent + + reagent.volume = new_volume + log_and_message_admins("[ui.user] has added [new_volume]u of [reagent] to [target]!") + + if("edit_volume") + var/reagent_uid = params["uid"] + var/datum/reagent/reagent = locateUID(reagent_uid) + if(isnull(reagent)) + return FALSE + var/new_volume = tgui_input_number(ui.user, "Сколько юнитов этого реагента должно быть в хранилище?", "Редактировать число реагента", 0, 1E100, -1E100) + if(isnull(new_volume)) + return + reagent.volume = new_volume + log_and_message_admins("[ui.user] has edited volume of [reagent] to [new_volume]u in [target]!") + + if("delete_reagent") + var/reagent_uid = params["uid"] + var/datum/reagent/reagent = locateUID(reagent_uid) + if(isnull(reagent)) + return FALSE + target.reagents.reagent_list -= reagent + log_and_message_admins("[ui.user] has deleted [reagent] from [target]!") + + if("update_total") + target.reagents.update_total() + + if("add_new_reagent") + usr.client.try_add_reagent(target) + + if("react_reagents") + target.reagents.handle_reactions() + log_and_message_admins("[ui.user] has forced a chemical reaction in [target]!") + + else + . = FALSE diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 2b137da292e..f8e1005915b 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -53,20 +53,32 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/Destroy(force) for(var/datum/objective/objective as anything in objectives) objectives -= objective + if(!objective.team) qdel(objective) + remove_owner_from_gamemode() GLOB.antagonists -= src + if(!silent) farewell() + remove_innate_effects() + antag_memory = null + var/datum/team/team = get_team() team?.remove_member(owner) + if(owner) LAZYREMOVE(owner.antag_datums, src) + + if(!LAZYLEN(owner.antag_datums)) // that one was the last antag datum. + handle_last_instance_removal() + restore_last_hud_and_role() owner = null + return ..() @@ -92,8 +104,15 @@ GLOBAL_LIST_EMPTY(antagonists) /datum/antagonist/proc/is_banned(mob/user) if(!user) return FALSE + return (jobban_isbanned(user, ROLE_SYNDICATE) || (job_rank && jobban_isbanned(user, job_rank))) +/** + * When our datum was last and became removed. + */ +/datum/antagonist/proc/handle_last_instance_removal() + return + /** * Attempts to replace the role banned antag with a ghost player. diff --git a/code/modules/antagonists/blob/blob_actions.dm b/code/modules/antagonists/blob/blob_actions.dm index 5d33c989256..9537a77c53b 100644 --- a/code/modules/antagonists/blob/blob_actions.dm +++ b/code/modules/antagonists/blob/blob_actions.dm @@ -3,29 +3,40 @@ background_icon_state = "bg_default_on" /datum/action/innate/blob/comm - name = "Blob Telepathy" - desc = "Телепатически отправляет сообщение всем блобам, иблобернаутам и зараженным блобом" + name = "Телепатия блоба" + desc = "Телепатически отправляет сообщение всем блобам, миньенам блоба и зараженным блобом организмам" button_icon_state = "alien_whisper" check_flags = AB_CHECK_CONSCIOUS|AB_TRANSFER_MIND /datum/action/innate/blob/comm/Activate() - var/input = stripped_input(usr, "Выберите сообщение для отправки другому блобу.", "Blob Telepathy", "") + var/input = tgui_input_text(usr, "Выберите сообщение для отправки другим блобам.", "Телепатия Блоба", "") if(!input || !IsAvailable()) return - blob_talk(usr, input) + blob_talk(input) return +/datum/action/innate/blob/comm/proc/blob_talk(message) + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if(!message) + return + + add_say_logs(usr, message, language = "BLOB") + var/rendered = span_blob("\[Blob Telepathy\] [usr.name] states, [message]") + relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, usr) + /datum/action/innate/blob/self_burst - icon_icon = 'icons/mob/blob.dmi' - button_icon = 'icons/mob/blob.dmi' + icon_icon = 'icons/hud/blob.dmi' + button_icon = 'icons/hud/blob.dmi' background_icon_state = "block" button_icon_state = "ui_tocore" - name = "Self burst" + name = "Лопнуть носителя" desc = "Позволяет лопнуть носителя и превратиться в блоба досрочно." check_flags = AB_CHECK_CONSCIOUS|AB_TRANSFER_MIND /datum/action/innate/blob/self_burst/Activate() - var/input = alert(usr,"Вы действительно хотите лопнуть себя и превратиться в блоба досрочно? Это действие необратимо.", "", "Да", "Нет") == "Да" + var/input = tgui_alert(usr,"Вы действительно хотите лопнуть себя и превратиться в блоба досрочно? Это действие необратимо.", "", list("Да", "Нет")) == "Да" if(!input || !IsAvailable()) return var/datum/antagonist/blob_infected/blob = usr?.mind?.has_antag_datum(/datum/antagonist/blob_infected) @@ -34,20 +45,19 @@ blob.burst_blob() return -/proc/blob_talk(mob/living/user, message) - add_say_logs(user, message, language = "BLOB") +/datum/action/innate/blob/minion_talk + background_icon_state = "bg_default" + button_icon_state = "talk_around" + name = "Сказать окружающим" + desc = "Вы скажете введенный текст окружающим вас мобам" + check_flags = AB_CHECK_CONSCIOUS|AB_TRANSFER_MIND - message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) +/datum/action/innate/blob/minion_talk/Activate() - if(!message) - return + var/speak_text = tgui_input_text(usr, "Что вы хотите сказать?", "Сказать окружающим", null) - var/rendered = "Blob Telepathy, [user.name] states, \"[message]\"" - for(var/mob/M in GLOB.mob_list) - if(isovermind(M) || isblobbernaut(M) || isblobinfected(M.mind)) - M.show_message(rendered, 2) - else if(isobserver(M) && !isnewplayer(M)) - var/rendered_ghost = "Blob Telepathy, [user.name] \ - (F) states, \"[message]\"" - M.show_message(rendered_ghost, 2) + if(!speak_text) + return + add_say_logs(usr, speak_text, language = "BLOB mob_say") + usr.atom_say(speak_text) diff --git a/code/modules/antagonists/blob/blob_infected_datum.dm b/code/modules/antagonists/blob/blob_infected_datum.dm index 4c41c1fb572..339dd3f0f8e 100644 --- a/code/modules/antagonists/blob/blob_infected_datum.dm +++ b/code/modules/antagonists/blob/blob_infected_datum.dm @@ -61,9 +61,14 @@ /datum/antagonist/blob_infected/Destroy(force, ...) - add_game_logs("has been deblobized", owner.current) + if(!is_tranformed) + add_game_logs("has been deblobized", owner.current) stop_process = TRUE - return ..() + . = ..() + qdel(time_to_burst_display) + qdel(blob_talk_action) + qdel(blob_burst_action) + return . /datum/antagonist/blob_infected/add_owner_to_gamemode() @@ -159,6 +164,7 @@ if(!blob_talk_action) blob_talk_action = new blob_talk_action.Grant(antag_mob) + GLOB.blob_telepathy_mobs |= antag_mob if(!blob_burst_action) blob_burst_action = new blob_burst_action.Grant(antag_mob) @@ -167,12 +173,9 @@ /datum/antagonist/blob_infected/proc/remove_blob_actions(mob/living/antag_mob) if(!antag_mob) return - if(!blob_talk_action) - return - blob_talk_action.Remove(antag_mob) - if(!blob_burst_action) - return - blob_burst_action.Remove(antag_mob) + blob_talk_action?.Remove(antag_mob) + GLOB.blob_telepathy_mobs -= antag_mob + blob_burst_action?.Remove(antag_mob) /datum/antagonist/blob_infected/proc/add_burst_display(mob/living/antag_mob) @@ -242,7 +245,7 @@ blob_client = GLOB.directory[ckey(owner.key)] location = get_turf(C) var/datum/game_mode/mode= SSticker.mode - if (ismob(C.loc)) + if(ismob(C.loc)) var/mob/M = C.loc M.gib() if(!is_station_level(location.z) || isspaceturf(location)) @@ -251,16 +254,20 @@ if(blob_client && location) mode.bursted_blobs_count++ C.was_bursted = TRUE - + kill_borer_inside() var/datum/antagonist/blob_overmind/overmind = transform_to_overmind() owner.remove_antag_datum(/datum/antagonist/blob_infected) - kill_borer_inside() C.gib() - var/obj/structure/blob/core/core = new(location, 200, blob_client, SSticker.mode.blob_point_rate) + var/obj/structure/blob/special/core/core = new(location, blob_client) if(!(core.overmind && core.overmind.mind)) return core.overmind.mind.add_antag_datum(overmind) core.lateblobtimer() + notify_ghosts( + "A Blob host has burst in [get_area_name(core)]", + source = core, + title = "Blob Awakening!", + ) SSticker?.mode?.process_blob_stages() mode.update_blob_objective() diff --git a/code/modules/antagonists/blob/blob_minion.dm b/code/modules/antagonists/blob/blob_minion.dm new file mode 100644 index 00000000000..191187da7bc --- /dev/null +++ b/code/modules/antagonists/blob/blob_minion.dm @@ -0,0 +1,96 @@ +/datum/antagonist/blob_minion + name = "\improper Blob Minion" + roundend_category = "blobs" + job_rank = ROLE_BLOB + special_role = SPECIAL_ROLE_BLOB_MINION + wiki_page_name = "Blob" + russian_wiki_name = "Блоб" + show_in_roundend = FALSE + show_in_orbit = FALSE + /// The blob core that this minion is attached to + var/datum/weakref/overmind + /// Action to talk with nearby mobs + var/datum/action/innate/blob/minion_talk/mob_talk + +/datum/antagonist/blob_minion/can_be_owned(datum/mind/new_owner) + . = ..() && isminion(new_owner?.current) + +/datum/antagonist/blob_minion/New(mob/camera/blob/overmind) + . = ..() + src.overmind = WEAKREF(overmind) + +/datum/antagonist/blob_minion/add_owner_to_gamemode() + var/datum/game_mode/mode = SSticker.mode + if(mode) + mode.blobs["minions"] |= owner + +/datum/antagonist/blob_minion/remove_owner_from_gamemode() + var/datum/game_mode/mode = SSticker.mode + if(mode) + mode.blobs["minions"] -= owner + + +/datum/antagonist/blob_minion/apply_innate_effects(mob/living/mob_override) + var/mob/living/user = ..(mob_override) + if(!user) + return + if(!mob_talk) + mob_talk = new + mob_talk.Grant(user) + return user + + +/datum/antagonist/blob_minion/remove_innate_effects(mob/living/mob_override) + var/mob/living/user = ..(mob_override) + if(!user) + return + mob_talk?.Remove(user) + return user + +/datum/antagonist/blob_minion/roundend_report_header() + return + + +/datum/antagonist/blob_minion/on_gain() + . = ..() + give_objectives() + +/datum/antagonist/blob_minion/give_objectives() + var/datum/objective/blob_minion/objective = new + objective.owner = owner + objective.overmind = overmind?.resolve() + objectives |= objective + +/datum/antagonist/blob_minion/blobernaut + name = "\improper Blobernaut" + + +/datum/antagonist/blob_minion/blobernaut/greet() + . = ..() + var/mob/camera/blob/blob = overmind?.resolve() + var/datum/blobstrain/blobstrain = blob.blobstrain + . += span_dangerbigger("Вы блобернаут! Вы должны помогать всем формам блоба в их миссии по уничтожению всего!") + . += span_info("Вы сильны, крепки, и медленно регенерируете в пределах плиток блоба, [span_cultlarge("но вы будете медленно умирать, если их рядом нету")] или если фабрика, создавшая вас, будет разрушена.") + . += span_info("Вы можете общаться с другими бернаутами, миньенами, зараженными и надразумами телепатически заместо обычного общения.") + . += span_info("Штамм вашего надразума: [blobstrain.name]!") + . += span_info("Штамм [blobstrain.name] [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]") + +/** + * Takes any datum `source` and checks it for blob_minion datum. + */ +/proc/isblobminion(datum/source) + if(!source) + return FALSE + + if(istype(source, /datum/mind)) + var/datum/mind/our_mind = source + return our_mind.has_antag_datum(/datum/antagonist/blob_minion) + + if(!ismob(source)) + return FALSE + + var/mob/mind_holder = source + if(!mind_holder.mind) + return FALSE + + return mind_holder.mind.has_antag_datum(/datum/antagonist/blob_minion) diff --git a/code/modules/antagonists/blob/blob_minions/blob_mob.dm b/code/modules/antagonists/blob/blob_minions/blob_mob.dm new file mode 100644 index 00000000000..7cd3f674ba4 --- /dev/null +++ b/code/modules/antagonists/blob/blob_minions/blob_mob.dm @@ -0,0 +1,96 @@ +/// Root of shared behaviour for mobs spawned by blobs, is abstract and should not be spawned +/mob/living/simple_animal/hostile/blob_minion + name = "Blob Error" + desc = "Нефункциональное грибковое существо, созданное плохим кодом или небесной ошибкой. Показывайте и смейтесь." + icon = 'icons/mob/blob.dmi' + icon_state = "blob_head" + unique_name = TRUE + pass_flags = PASSBLOB + status_flags = NONE // No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you. + faction = list(ROLE_BLOB) + bubble_icon = "blob" + speak_emote = null + stat_attack = UNCONSCIOUS + 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) + sight = SEE_TURFS|SEE_MOBS|SEE_OBJS + nightvision = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + can_buckle_to = FALSE + universal_speak = TRUE // So mobs can understand them when a blob uses Blob Broadcast + sentience_type = SENTIENCE_OTHER + gold_core_spawnable = NO_SPAWN + can_be_on_fire = TRUE + fire_damage = 3 + tts_seed = "Earth" + tts_atom_say_effect = SOUND_EFFECT_NONE + a_intent = INTENT_HARM + /// Is blob mob linked to factory + var/factory_linked = FALSE + + +/mob/living/simple_animal/hostile/blob_minion/ComponentInitialize() + AddComponent( \ + /datum/component/animal_temperature, \ + minbodytemp = 0, \ + maxbodytemp = INFINITY, \ + ) + AddComponent(/datum/component/blob_minion, on_strain_changed = CALLBACK(src, PROC_REF(on_strain_updated))) + +/mob/living/simple_animal/hostile/blob_minion/Initialize(mapload) + . = ..() + add_traits(list(TRAIT_BLOB_ALLY, TRAIT_MUTE), INNATE_TRAIT) + +/// Called when our blob overmind changes their variant, update some of our mob properties +/mob/living/simple_animal/hostile/blob_minion/proc/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) + return + +/mob/living/simple_animal/hostile/blob_minion/can_z_move(direction, turf/start, turf/destination, z_move_flags, mob/living/rider) + var/obj/structure/blob/s_blob = locate(/obj/structure/blob) in start + var/obj/structure/blob/d_blob = locate(/obj/structure/blob) in destination + var/check = !(z_move_flags & ZMOVE_FALL_CHECKS) + if(s_blob && d_blob) + return check + . = ..() + +/mob/living/simple_animal/hostile/blob_minion/move_up() + var/turf/current_turf = get_turf(src) + var/turf/above_turf = GET_TURF_ABOVE(current_turf) + if((locate(/obj/structure/blob) in current_turf) && (locate(/obj/structure/blob) in above_turf)) + if(zMove(UP, above_turf, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK)) + to_chat(src, span_notice("You move upwards.")) + return + . = ..() + +/mob/living/simple_animal/hostile/blob_minion/move_down() + var/turf/current_turf = get_turf(src) + var/turf/below_turf = GET_TURF_BELOW(current_turf) + if((locate(/obj/structure/blob) in current_turf) && (locate(/obj/structure/blob) in below_turf)) + if(zMove(DOWN, below_turf, z_move_flags = ZMOVE_FLIGHT_FLAGS|ZMOVE_FEEDBACK)) + to_chat(src, span_notice("You move down.")) + return + . = ..() + + +/mob/living/simple_animal/hostile/blob_minion/regenerate_icons() + update_icon() + +/// Associates this mob with a specific blob factory node +/mob/living/simple_animal/hostile/blob_minion/proc/link_to_factory(obj/structure/blob/special/factory/factory) + factory_linked = TRUE + RegisterSignal(factory, COMSIG_QDELETING, PROC_REF(on_factory_destroyed)) + +/mob/living/simple_animal/hostile/blob_minion/attack_animal(mob/living/simple_animal/M) + if(ROLE_BLOB in M.faction) + to_chat(M, span_danger("Вы не можете навредить другому порождению блоба")) + return + ..() + +/// Called when our factory is destroyed +/mob/living/simple_animal/hostile/blob_minion/proc/on_factory_destroyed() + SIGNAL_HANDLER + to_chat(src, span_userdanger("Your factory was destroyed! You feel yourself dying!")) + + +/mob/living/simple_animal/hostile/blob_minion/can_be_blob() + return FALSE + diff --git a/code/modules/antagonists/blob/blob_minions/blob_spore.dm b/code/modules/antagonists/blob/blob_minions/blob_spore.dm new file mode 100644 index 00000000000..52081e0169f --- /dev/null +++ b/code/modules/antagonists/blob/blob_minions/blob_spore.dm @@ -0,0 +1,145 @@ +/** + * A floating fungus which turns people into zombies and explodes into reagent clouds upon death. + */ +/mob/living/simple_animal/hostile/blob_minion/spore + name = "blob spore" + desc = "Плавающая хрупкая спора." + icon = 'icons/mob/blob.dmi' + icon_state = "blobpod" + icon_living = "blobpod" + health_doll_icon = "blobpod" + health = BLOBMOB_SPORE_HEALTH + maxHealth = BLOBMOB_SPORE_HEALTH + verb_say = list("psychically pulses", "pulses") + verb_ask = "psychically probes" + verb_exclaim = "psychically yells" + verb_yell = "psychically screams" + melee_damage_lower = BLOBMOB_SPORE_DMG_LOWER + melee_damage_upper = BLOBMOB_SPORE_DMG_UPPER + obj_damage = BLOBMOB_SPORE_OBJ_DMG + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + attacktext = "ударяет" + attack_sound = 'sound/weapons/genhit1.ogg' + deathmessage = "взрывается облаком газа!" + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = TRUE + speed = BLOBMOB_SPORE_SPEED_MOD + /// Size of cloud produced from a dying spore + var/death_cloud_size = 2 + /// Type of mob to create + var/mob/living/zombie_type = /mob/living/simple_animal/hostile/blob_minion/zombie + + +/mob/living/simple_animal/hostile/blob_minion/spore/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) + +/mob/living/simple_animal/hostile/blob_minion/spore/ComponentInitialize() + . = ..() + AddElement(/datum/element/simple_flying) + +/mob/living/simple_animal/hostile/blob_minion/spore/death(gibbed) + . = ..() + death_burst() + +/mob/living/simple_animal/hostile/blob_minion/spore/on_factory_destroyed() + death() + +/// Create an explosion of spores on death +/mob/living/simple_animal/hostile/blob_minion/spore/proc/death_burst() + do_blob_chem_smoke(range = death_cloud_size, reagent_volume = BLOB_REAGENT_SPORE_VOL, holder = src, location = get_turf(src), reagent_type = /datum/reagent/toxin/spore) + +/mob/living/simple_animal/hostile/blob_minion/spore/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if(istype(mover, /obj/structure/blob)) + return TRUE + +/mob/living/simple_animal/hostile/blob_minion/spore/CanAttack(atom/the_target) + if(ishuman(the_target)) + stat_attack = DEAD + . = ..() + stat_attack = initial(stat_attack) + +/mob/living/simple_animal/hostile/blob_minion/spore/pull_constraint(atom/movable/pulled_atom, state, supress_message = FALSE) //Prevents spore from pulling things + if(istype(pulled_atom, /mob/living)) + return TRUE // Get dem + if(!supress_message) + to_chat(src, span_warning("Вы не можете таскать ничего кроме других существ и их тел.")) + return FALSE + +/mob/living/simple_animal/hostile/blob_minion/spore/AttackingTarget() + . = ..() + var/mob/living/carbon/human/human_target = target + if(!target || !istype(human_target) || human_target.stat != DEAD) + return . + + if(HAS_TRAIT(human_target, TRAIT_BLOB_ZOMBIFIED)) + return . + zombify(human_target) + +/// Become a zombie +/mob/living/simple_animal/hostile/blob_minion/spore/proc/zombify(mob/living/carbon/human/target) + if(HAS_TRAIT(target, TRAIT_NO_TRANSFORM) || target.has_status_effect(/datum/status_effect/hippocraticOath)) + return + + visible_message(span_warning("Тело [target.name] внезапно поднимается!")) + var/mob/living/simple_animal/hostile/blob_minion/zombie/blombie = change_mob_type(zombie_type, loc, new_name = initial(zombie_type.name)) + blombie.set_name() + if(istype(blombie)) // In case of badmin + blombie.consume_corpse(target) + SEND_SIGNAL(src, COMSIG_BLOB_ZOMBIFIED, blombie) + qdel(src) + +/// Variant of the blob spore which is actually spawned by blob factories +/mob/living/simple_animal/hostile/blob_minion/spore/minion + gold_core_spawnable = NO_SPAWN + zombie_type = /mob/living/simple_animal/hostile/blob_minion/zombie/controlled + /// We die if we leave the same turf as this z level + var/turf/z_turf + +/mob/living/simple_animal/hostile/blob_minion/spore/minion/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_z_changed)) + +/// When we z-move check that we're on the same z level as our factory was +/mob/living/simple_animal/hostile/blob_minion/spore/minion/proc/on_z_changed() + SIGNAL_HANDLER + if(isnull(z_turf)) + return + if(!is_valid_z_level(get_turf(src), z_turf)) + death() + +/// Mark the turf we need to track from our factory +/mob/living/simple_animal/hostile/blob_minion/spore/minion/link_to_factory(obj/structure/blob/special/factory/factory) + . = ..() + z_turf = get_turf(factory) + +/// If the blob changes to distributed neurons then you can control the spores +/mob/living/simple_animal/hostile/blob_minion/spore/minion/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) + if(istype(new_strain, /datum/blobstrain/reagent/distributed_neurons)) + AddComponent(\ + /datum/component/ghost_direct_control,\ + ban_type = ROLE_BLOB,\ + poll_candidates = FALSE,\ + ) + else + qdel(GetComponent(/datum/component/ghost_direct_control)) + +/mob/living/simple_animal/hostile/blob_minion/spore/minion/death_burst() + return // This behaviour is superceded by the overmind's intervention + + +/// Weakened spore spawned by distributed neurons, can't zombify people and makes a teeny explosion +/mob/living/simple_animal/hostile/blob_minion/spore/minion/weak + name = "fragile blob spore" + health = 15 + maxHealth = 15 + melee_damage_lower = 1 + melee_damage_upper = 2 + death_cloud_size = 1 + +/mob/living/simple_animal/hostile/blob_minion/spore/minion/weak/zombify() + return + +/mob/living/simple_animal/hostile/blob_minion/spore/minion/weak/on_strain_updated() + return diff --git a/code/modules/antagonists/blob/blob_minions/blob_zombie.dm b/code/modules/antagonists/blob/blob_minions/blob_zombie.dm new file mode 100644 index 00000000000..aa7c398b808 --- /dev/null +++ b/code/modules/antagonists/blob/blob_minions/blob_zombie.dm @@ -0,0 +1,122 @@ +/// A shambling mob made out of a crew member +/mob/living/simple_animal/hostile/blob_minion/zombie + name = "blob zombie" + desc = "Шаркающий труп, оживленный блобом." + icon_state = "zombie" + icon_living = "zombie" + health_doll_icon = "blobpod" + health = BLOBMOB_ZOMBIE_HEALTH + maxHealth = BLOBMOB_ZOMBIE_HEALTH + verb_say = list("gurgles", "groans") + verb_ask = "demands" + verb_exclaim = "roars" + verb_yell = "bellows" + melee_damage_lower = BLOBMOB_ZOMBIE_DMG_LOWER + melee_damage_upper = BLOBMOB_ZOMBIE_DMG_UPPER + obj_damage = BLOBMOB_ZOMBIE_OBJ_DMG + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + attacktext = "ударяет" + attack_sound = 'sound/weapons/genhit1.ogg' + deathmessage = "падает на землю!" + gold_core_spawnable = NO_SPAWN + del_on_death = TRUE + speed = BLOBMOB_ZOMBIE_SPEED_MOD + /// The dead body we have inside + var/mob/living/carbon/human/corpse + + +/mob/living/simple_animal/hostile/blob_minion/zombie/death(gibbed) + if(corpse) + REMOVE_TRAIT(corpse, TRAIT_BLOB_ZOMBIFIED, BLOB_ZOMBIE_TRAIT) + UnregisterSignal(corpse, list(COMSIG_HUMAN_DESTROYED, COMSIG_LIVING_REVIVE)) + corpse.forceMove(loc) + death_burst() + return ..() + +/mob/living/simple_animal/hostile/blob_minion/zombie/Exited(atom/movable/gone, direction) + . = ..() + if(gone != corpse) + return + corpse = null + death() + +/mob/living/simple_animal/hostile/blob_minion/zombie/pull_constraint(atom/movable/pulled_atom, state, supress_message = FALSE) //Prevents spore from pulling things + if(istype(pulled_atom, /mob/living)) + return TRUE // Get dem + if(!supress_message) + to_chat(src, span_warning("Вы не можете таскать ничего кроме других существ и их тел.")) + return FALSE + +/mob/living/simple_animal/hostile/blob_minion/zombie/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + if(istype(mover, /obj/structure/blob)) + return TRUE + + +/mob/living/simple_animal/hostile/blob_minion/zombie/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) + +/mob/living/simple_animal/hostile/blob_minion/zombie/Destroy() + QDEL_NULL(corpse) + return ..() + +/mob/living/simple_animal/hostile/blob_minion/zombie/on_factory_destroyed() + . = ..() + death() + +/mob/living/simple_animal/hostile/blob_minion/zombie/update_overlays() + . = ..() + . |= corpse?.overlays?.Copy() + var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head") + blob_head_overlay.color = LAZYACCESS(atom_colours, FIXED_COLOUR_PRIORITY) || COLOR_WHITE + color = initial(color) // reversing what our component did lol, but we needed the value for the overlay + . |= blob_head_overlay + if(blocks_emissive) + . |= get_emissive_block() + + +/// Create an explosion of spores on death +/mob/living/simple_animal/hostile/blob_minion/zombie/proc/death_burst() + do_blob_chem_smoke(range = 1, holder = src, reagent_volume = BLOB_REAGENT_SPORE_VOL, location = get_turf(src), reagent_type = /datum/reagent/toxin/spore) + +/// Store a body so that we can drop it on death +/mob/living/simple_animal/hostile/blob_minion/zombie/proc/consume_corpse(mob/living/carbon/human/new_corpse) + ADD_TRAIT(new_corpse, TRAIT_BLOB_ZOMBIFIED, BLOB_ZOMBIE_TRAIT) + if(new_corpse.wear_suit) + maxHealth += new_corpse.getarmor(attack_flag = MELEE) + health = maxHealth + new_corpse.change_facial_hair("Shaved") + new_corpse.change_hair("Bald") + new_corpse.forceMove(src) + corpse = new_corpse + update_icon() + RegisterSignal(corpse, COMSIG_LIVING_REVIVE, PROC_REF(on_corpse_revived)) + RegisterSignal(corpse, COMSIG_HUMAN_DESTROYED, PROC_REF(on_corpse_destroyed)) + +/// Dynamic changeling reentry +/mob/living/simple_animal/hostile/blob_minion/zombie/proc/on_corpse_revived() + SIGNAL_HANDLER + visible_message(span_boldwarning("[src] разрывается изнутри!")) + death() + +/mob/living/simple_animal/hostile/blob_minion/zombie/proc/on_corpse_destroyed() + SIGNAL_HANDLER + visible_message(span_boldwarning("C носитель уничтожено. [src] разрушается изнутри!")) + death() + +/// Blob-created zombies will ping for player control when they make a zombie +/mob/living/simple_animal/hostile/blob_minion/zombie/controlled + +/mob/living/simple_animal/hostile/blob_minion/zombie/controlled/consume_corpse(mob/living/carbon/human/new_corpse) + . = ..() + if(!isnull(client) || SSticker.current_state == GAME_STATE_FINISHED) + return + AddComponent(\ + /datum/component/ghost_direct_control,\ + ban_type = ROLE_BLOB,\ + poll_candidates = TRUE,\ + ) + +/mob/living/simple_animal/hostile/blob_minion/zombie/controlled/death_burst() + return diff --git a/code/modules/antagonists/blob/blob_minions/blobbernaut.dm b/code/modules/antagonists/blob/blob_minions/blobbernaut.dm new file mode 100644 index 00000000000..e0b742670a3 --- /dev/null +++ b/code/modules/antagonists/blob/blob_minions/blobbernaut.dm @@ -0,0 +1,116 @@ +/** + * Player-piloted brute mob. Mostly just a "move and click" kind of guy. + * Has a variant which takes damage when away from blob tiles + */ +/mob/living/simple_animal/hostile/blob_minion/blobbernaut + name = "blobbernaut" + desc = "Огромный, подвижный кусок биомассы." + icon_state = "blobbernaut" + icon_living = "blobbernaut" + icon_dead = "blobbernaut_dead" + health = BLOBMOB_BLOBBERNAUT_HEALTH + maxHealth = BLOBMOB_BLOBBERNAUT_HEALTH + damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1) + melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_SOLO_LOWER + melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_SOLO_UPPER + obj_damage = BLOBMOB_BLOBBERNAUT_DMG_OBJ + environment_smash = ENVIRONMENT_SMASH_STRUCTURES + attacktext = "ударяет" + attack_sound = 'sound/effects/blobattack.ogg' + verb_say = "gurgles" + verb_ask = "demands" + verb_exclaim = "roars" + verb_yell = "bellows" + pressure_resistance = 50 + force_threshold = 10 + mob_size = MOB_SIZE_LARGE + move_resist = MOVE_FORCE_OVERPOWERING + hud_type = /datum/hud/simple_animal/blobbernaut + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, INNATE_TRAIT) + update_health_hud() + + +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/experience_pressure_difference(pressure_difference, direction) + if(!HAS_TRAIT(src, TRAIT_NEGATES_GRAVITY)) + return ..() + +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/death(gibbed) + flick("blobbernaut_death", src) + return ..() + +/// This variant is the one actually spawned by blob factories, takes damage when away from blob tiles +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion + gold_core_spawnable = NO_SPAWN + /// Is our factory dead? + var/orphaned = FALSE + +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/Initialize(mapload) + bruteloss = maxHealth / 2 // Start out injured to encourage not beelining away from the blob + . = ..() + +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/Life(seconds_per_tick, times_fired) + . = ..() + if(!.) + return FALSE + var/damage_sources = 0 + var/list/blobs_in_area = (is_there_multiz())? urange_multiz(2, src) : range(2, src) + + if(!(locate(/obj/structure/blob) in blobs_in_area)) + damage_sources++ + + if(orphaned) + damage_sources++ + else + var/particle_colour = atom_colours[FIXED_COLOUR_PRIORITY] || COLOR_BLACK + + if(locate(/obj/structure/blob/special/core) in blobs_in_area) + heal_overall_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALING_CORE * seconds_per_tick) + var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(src)) + heal_effect.color = particle_colour + + if(locate(/obj/structure/blob/special/node) in blobs_in_area) + heal_overall_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALING_NODE * seconds_per_tick) + var/obj/effect/temp_visual/heal/heal_effect = new /obj/effect/temp_visual/heal(get_turf(src)) + heal_effect.color = particle_colour + + if(damage_sources == 0) + return FALSE + + // take 2.5% of max health as damage when not near the blob or if the naut has no factory, 5% if both + apply_damage(maxHealth * BLOBMOB_BLOBBERNAUT_HEALTH_DECAY * damage_sources * seconds_per_tick, damagetype = TOX) // We reduce brute damage + var/mutable_appearance/harming = mutable_appearance('icons/mob/blob.dmi', "nautdamage", MOB_LAYER + 0.01) + harming.appearance_flags = RESET_COLOR + harming.color = atom_colours[FIXED_COLOUR_PRIORITY] || COLOR_WHITE + harming.dir = dir + flick_overlay_view(harming, 0.8 SECONDS) + return TRUE + +/// Called by the blob creation power to give us a mind and a basic task orientation +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/proc/assign_key(ckey, datum/blobstrain/blobstrain) + key = ckey + flick("blobbernaut_produce", src) + SEND_SOUND(src, sound('sound/effects/blobattack.ogg')) + SEND_SOUND(src, sound('sound/effects/attackblob.ogg')) + log_game("[key] has spawned as Blobbernaut") + +/// Set our attack damage based on blob's properties +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/on_strain_updated(mob/camera/blob/overmind, datum/blobstrain/new_strain) + if(isnull(overmind)) + melee_damage_lower = initial(melee_damage_lower) + melee_damage_upper = initial(melee_damage_upper) + attacktext = initial(attacktext) + return + melee_damage_lower = BLOBMOB_BLOBBERNAUT_DMG_LOWER + melee_damage_upper = BLOBMOB_BLOBBERNAUT_DMG_UPPER + attacktext = new_strain.blobbernaut_message + +/// Called by our factory to inform us that it's not going to support us financially any more +/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/on_factory_destroyed() + . = ..() + orphaned = TRUE + throw_alert("nofactory", /atom/movable/screen/alert/nofactory) + diff --git a/code/modules/antagonists/blob/blob_overmind_datum.dm b/code/modules/antagonists/blob/blob_overmind_datum.dm index ecd9631f335..736252139af 100644 --- a/code/modules/antagonists/blob/blob_overmind_datum.dm +++ b/code/modules/antagonists/blob/blob_overmind_datum.dm @@ -13,14 +13,17 @@ var/is_offspring = FALSE /// Was the blob with this datum bursted blob_infected. var/is_tranformed = FALSE - /// Link to the datum of the selected blob reagent. - var/datum/reagent/blob/reagent + //Link to the datum of the selected blob reagent. + var/datum/blobstrain/strain + +/datum/antagonist/blob_overmind/can_be_owned(datum/mind/new_owner) + . = ..() && isovermind(new_owner?.current) /datum/antagonist/blob_overmind/on_gain() - if(!reagent) - var/reagent_type = pick(subtypesof(/datum/reagent/blob)) - reagent = new reagent_type - return ..() + var/mob/camera/blob/camera = owner.current + strain = camera.blobstrain + . = ..() + /datum/antagonist/blob_overmind/add_owner_to_gamemode() var/datum/game_mode/mode = SSticker.mode @@ -53,16 +56,16 @@ /datum/antagonist/blob_overmind/greet() var/list/messages = list() - messages.Add("Вы Блоб!") - for(var/message in get_blob_help_messages(reagent)) + messages.Add(span_danger("Вы Блоб!")) + for(var/message in get_blob_help_messages(strain)) messages.Add(message) SEND_SOUND(owner.current, 'sound/magic/mutate.ogg') return messages -/proc/get_blob_help_messages(datum/reagent/blob/blob_reagent_datum) +/proc/get_blob_help_messages(datum/blobstrain/blob_reagent_datum) var/list/messages = list() messages += "Как надразум, вы можете управлять блобом!" - messages += "Ваш реагент: [blob_reagent_datum.name] - [blob_reagent_datum.description]" + messages += blob_reagent_datum.overmind.get_strain_info() messages += "Вы можете расширяться, атакуя людей, повреждая объекты или размещая простую плитку, если клетка свободна." messages += "Обычная плитка будет расширять ваше влияние и может быть улучшена до специальной плитки, выполняющей определённую функцию." messages += "Вы можете улучшить обычные плитки до следующих типов:" diff --git a/code/modules/antagonists/blob/blobs_attack.dm b/code/modules/antagonists/blob/blobs_attack.dm new file mode 100644 index 00000000000..19e09eb18f0 --- /dev/null +++ b/code/modules/antagonists/blob/blobs_attack.dm @@ -0,0 +1,11 @@ +/atom/proc/can_blob_attack() + return TRUE + +/mob/living/can_blob_attack() + . = ..() + if(!.) + return + return !incorporeal_move + +/obj/effect/dummy/phased_mob/can_blob_attack() + return FALSE diff --git a/code/modules/antagonists/blob/blobstrains/_blobstrain.dm b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm new file mode 100644 index 00000000000..38fb85564bb --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/_blobstrain.dm @@ -0,0 +1,176 @@ +GLOBAL_LIST_INIT(valid_blobstrains, subtypesof(/datum/blobstrain) - list(/datum/blobstrain/reagent, /datum/blobstrain/multiplex)) + +/datum/blobstrain + var/name + var/description + var/color = COLOR_BLACK + /// The color that stuff like healing effects and the overmind camera gets + var/complementary_color = COLOR_BLACK + /// A short description of the power and its effects + var/shortdesc = null + /// Any long, blob-tile specific effects + var/effectdesc = null + /// Short descriptor of what the strain does damage-wise, generally seen in the reroll menu + var/analyzerdescdamage = "Неизвестный. Сообщите об этой ошибке в баг-репорты и в админтикет." + /// Short descriptor of what the strain does in general, generally seen in the reroll menu + var/analyzerdesceffect + /// Blobbernaut attack verb + var/blobbernaut_message = "slams" + /// Message sent to any mob hit by the blob + var/message = "Блоб бьет вас" + /// Gets added onto 'message' if the mob stuck is of type living + var/message_living = null + /// Stores world.time to figure out when to next give resources + var/resource_delay = 0 + /// For blob-mobs and extinguishing-based effects + var/fire_based = FALSE + var/mob/camera/blob/overmind + /// The amount of health regenned on core_process + var/base_core_regen = BLOB_CORE_HP_REGEN + /// The amount of points gained on core_process + var/point_rate = BLOB_BASE_POINT_RATE + + // Various vars that strains can buff the blob with + /// HP regen bonus added by strain + var/core_regen_bonus = 0 + /// resource point bonus added by strain + var/point_rate_bonus = 0 + + /// Adds to claim, pulse, and expand range + var/core_range_bonus = 0 + /// Extra range up to which the core reinforces blobs + var/core_strong_reinforcement_range_bonus = 0 + /// Extra range up to which the core reinforces blobs into reflectors + var/core_reflector_reinforcement_range_bonus = 0 + + /// Adds to claim, pulse, and expand range + var/node_range_bonus = 0 + /// Nodes can sustain this any extra spores with this strain + var/node_spore_bonus = 0 + /// Extra range up to which the node reinforces blobs + var/node_strong_reinforcement_range_bonus = 0 + /// Extra range up to which the node reinforces blobs into reflectors + var/node_reflector_reinforcement_range_bonus = 0 + + /// Extra spores produced by factories with this strain + var/factory_spore_bonus = 0 + + /// Multiplies the max and current health of every blob with this value upon selecting this strain. + var/max_structure_health_multiplier = 1 + /// Multiplies the max and current health of every mob with this value upon selecting this strain. + var/max_mob_health_multiplier = 1 + + /// Makes blobbernauts inject a bonus amount of reagents, making their attacks more powerful + var/blobbernaut_reagentatk_bonus = 0 + +/datum/blobstrain/New(mob/camera/blob/new_overmind) + if(!istype(new_overmind)) + stack_trace("blobstrain created without overmind") + overmind = new_overmind + +/datum/blobstrain/Destroy() + overmind = null + return ..() + +/datum/blobstrain/proc/on_gain() + overmind.color = complementary_color + + if(overmind.blob_core) + overmind.blob_core.claim_range += core_range_bonus + overmind.blob_core.pulse_range += core_range_bonus + overmind.blob_core.expand_range += core_range_bonus + overmind.blob_core.strong_reinforce_range += core_strong_reinforcement_range_bonus + overmind.blob_core.reflector_reinforce_range += core_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/node/N as anything in overmind.node_blobs) + N.claim_range += node_range_bonus + N.pulse_range += node_range_bonus + N.expand_range += node_range_bonus + N.strong_reinforce_range += node_strong_reinforcement_range_bonus + N.reflector_reinforce_range += node_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/factory/F as anything in overmind.factory_blobs) + F.max_spores += factory_spore_bonus + + for(var/obj/structure/blob/B as anything in overmind.all_blobs) + B.modify_max_integrity(B.max_integrity * max_structure_health_multiplier) + B.update_blob() + + for(var/mob/living/blob_mob as anything in overmind.blob_mobs) + blob_mob.maxHealth *= max_mob_health_multiplier + blob_mob.health *= max_mob_health_multiplier + blob_mob.update_icon() // If it's getting a new strain, tell it what it does! + var/list/messages = list() + messages += "Штамм вашего надразума: [name]!" + messages += "Штамм [name] [shortdesc ? "[shortdesc]" : "[description]"]" + to_chat(blob_mob, chat_box_red(messages.Join("
"))) + +/datum/blobstrain/proc/on_lose() + if(overmind.blob_core) + overmind.blob_core.claim_range -= core_range_bonus + overmind.blob_core.expand_range -= core_range_bonus + overmind.blob_core.strong_reinforce_range -= core_strong_reinforcement_range_bonus + overmind.blob_core.reflector_reinforce_range -= core_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/node/N as anything in overmind.node_blobs) + N.claim_range -= node_range_bonus + N.expand_range -= node_range_bonus + N.strong_reinforce_range -= node_strong_reinforcement_range_bonus + N.reflector_reinforce_range -= node_reflector_reinforcement_range_bonus + + for(var/obj/structure/blob/special/factory/F as anything in overmind.factory_blobs) + F.max_spores -= factory_spore_bonus + + for(var/obj/structure/blob/B as anything in overmind.all_blobs) + B.modify_max_integrity(B.max_integrity / max_structure_health_multiplier) + + for(var/mob/living/blob_mob as anything in overmind.blob_mobs) + blob_mob.maxHealth /= max_mob_health_multiplier + blob_mob.health /= max_mob_health_multiplier + + +/datum/blobstrain/proc/on_sporedeath(mob/living/spore) + +/datum/blobstrain/proc/send_message(mob/living/M) + var/totalmessage = message + if(message_living && !issilicon(M)) + totalmessage += message_living + totalmessage += "!" + to_chat(M, span_userdanger("[totalmessage]")) + +/datum/blobstrain/proc/core_process() + if(resource_delay <= world.time) + resource_delay = world.time + 10 // 1 second + overmind.add_points(point_rate+point_rate_bonus) + overmind.blob_core.repair_damage(base_core_regen + core_regen_bonus) + +/datum/blobstrain/proc/attack_living(mob/living/L, list/nearby_blobs) // When the blob attacks people + send_message(L) + +/datum/blobstrain/proc/attack_mech(obj/mecha/mech) // When the blob attacks people + return + +/// When this blob's blobbernaut attacks any atom +/datum/blobstrain/proc/blobbernaut_attack(atom/attacking, mob/living/simple_animal/hostile/blobbernaut) + return + +/datum/blobstrain/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this + return coefficient*damage + +/datum/blobstrain/proc/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this + return + +/datum/blobstrain/proc/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this + return TRUE + +/datum/blobstrain/proc/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this + return TRUE //return 0 to ignore damage + +/datum/blobstrain/proc/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this + return + +/datum/blobstrain/proc/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this + return + +/datum/blobstrain/proc/examine(mob/user) + return list("Прогресс Критической Массы: [span_notice("[TOTAL_BLOB_MASS]/[NEEDED_BLOB_MASS].")]") diff --git a/code/modules/antagonists/blob/blobstrains/_reagent.dm b/code/modules/antagonists/blob/blobstrains/_reagent.dm new file mode 100644 index 00000000000..666b0f38f94 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/_reagent.dm @@ -0,0 +1,53 @@ +/datum/blobstrain/reagent // Blobs that mess with reagents, all "legacy" ones // what do you mean "legacy" you never added an alternative + var/datum/reagent/reagent + +/datum/blobstrain/reagent/New(mob/camera/blob/new_overmind) + . = ..() + reagent = new reagent() + + +/datum/blobstrain/reagent/attack_living(mob/living/L) + var/mob_protection = L.getarmor(null, BIO) * 0.01 + reagent.reaction_mob(L, REAGENT_TOUCH, BLOB_REAGENT_ATK_VOL, TRUE, mob_protection, overmind) + send_message(L) + +/datum/blobstrain/reagent/blobbernaut_attack(atom/attacking, mob/living/simple_animal/hostile/blobbernaut) + if(!isliving(attacking)) + return + + var/mob/living/living_attacking = attacking + var/mob_protection = living_attacking.getarmor(null, BIO) * 0.01 + reagent.reaction_mob(living_attacking, REAGENT_TOUCH, BLOBMOB_BLOBBERNAUT_REAGENT_ATK_VOL + blobbernaut_reagentatk_bonus, FALSE, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage. + +/datum/blobstrain/reagent/on_sporedeath(mob/living/simple_animal/hostile/blob_minion/spore/spore) + var/burst_range = (istype(spore)) ? spore.death_cloud_size : 1 + do_blob_chem_smoke(range = burst_range, holder = spore, reagent_volume = BLOB_REAGENT_SPORE_VOL, location = get_turf(spore), reagent_type = reagent.type) + + +/proc/do_blob_chem_smoke(range = 0, amount = DIAMOND_AREA(range), atom/holder = null, location = null, reagent_type = /datum/reagent/water, reagent_volume = 10, log = FALSE) + var/smoke_type = /datum/effect_system/fluid_spread/smoke/chem/quick + var/lifetime = /obj/effect/particle_effect/fluid/smoke/chem/quick::lifetime + var/volume = reagent_volume * (lifetime /(1 SECONDS)) + do_chem_smoke(range, amount, holder, location, reagent_type, smoke_type, reagent_volume = volume, log = log) + + +// These can only be applied by blobs. They are what (reagent) blobs are made out of. +/datum/reagent/blob + name = "Unknown" + description = "не должно существовать, и вам следует немедленно обратиться за помощью в adminhelp и напишите баг-репорт." + color = COLOR_WHITE + taste_description = "Это баг" + penetrates_skin = TRUE + clothing_penetration = 1 + metabolization_rate = BLOB_REAGENTS_METABOLISM + +/// Used by blob reagents to calculate the reaction volume they should use when exposing mobs. +/datum/reagent/blob/proc/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + if(exposed_mob.stat == DEAD || HAS_TRAIT(exposed_mob, TRAIT_BLOB_ALLY)) + return FALSE //the dead, and blob mobs, don't cause reactions + return round(reac_volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume. + +/// Exists to earmark the new overmind arg used by blob reagents. +/datum/reagent/blob/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + return ..() diff --git a/code/modules/antagonists/blob/blobstrains/blazing_oil.dm b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm new file mode 100644 index 00000000000..14369d1c245 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/blazing_oil.dm @@ -0,0 +1,46 @@ + +//sets you on fire, does burn damage, explodes into flame when burnt, weak to water +/datum/blobstrain/reagent/blazing_oil + name = "Пылающее масло" + description = "наносит высокий урон от ожогов и подожигает цели." + effectdesc = "при горении также выпускает вспышки пламени, игнорирует урон от горения, но получает урон от воды." + analyzerdescdamage = "Наносит высокий урон от ожогов и поджигает цели." + analyzerdesceffect = "При попадании выпускает вспышки пламени, игнорирует урон от горения, но получает урон от воды и других огнетушащих жидкостей." + color = "#B68D00" + complementary_color = "#BE5532" + blobbernaut_message = "splashes" + message = "Блоб обрызгивает вас горящим маслом" + message_living = ", и вы чувствуете, как ваша кожа обугливается и плавится" + reagent = /datum/reagent/blob/blazing_oil + fire_based = TRUE + +/datum/blobstrain/reagent/blazing_oil/extinguish_reaction(obj/structure/blob/B) + B.take_damage(4.5, BURN, ENERGY) + +/datum/blobstrain/reagent/blazing_oil/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage_type == BURN && damage_flag != ENERGY) + for(var/turf/simulated/T as anything in range(1, B)) + if(iswallturf(T) || ismineralturf(T)) + continue + var/obj/structure/blob/C = locate() in T + if(!(C && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) && prob(80)) + new /obj/effect/hotspot(T) + if(damage_flag == FIRE) + return FALSE + return ..() + +/datum/reagent/blob/blazing_oil + name = "Пылающее масло" + id = "blob_blazing_oil" + taste_description = "горящее масло" + color = "#B68D00" + +/datum/reagent/blob/blazing_oil/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.adjust_fire_stacks(round(reac_volume/10)) + exposed_mob.IgniteMob() + if(exposed_mob) + exposed_mob.apply_damage(0.8*reac_volume, BURN, forced=TRUE) + if(iscarbon(exposed_mob)) + exposed_mob.emote("scream") diff --git a/code/modules/antagonists/blob/blobstrains/blob_sorium.dm b/code/modules/antagonists/blob/blobstrains/blob_sorium.dm new file mode 100644 index 00000000000..1a0ebfef1b9 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/blob_sorium.dm @@ -0,0 +1,69 @@ + +//sets you on fire, does burn damage, explodes into flame when burnt, weak to water +/datum/blobstrain/reagent/b_sorium + name = "Сорий" + description = "наносит высокий урон травмами и отбрасывает людей в стороны." + effectdesc = "при попадании создает сориумный взрыв." + analyzerdescdamage = "Наносит высокий урон травмами и отбрасывает людей в стороны." + analyzerdesceffect = "При попадании создает сориумный взрыв." + color = "#808000" + complementary_color = "#a2a256" + blobbernaut_message = "splashes" + message = "Блоб врезается в вас и отбрасывает в сторону" + reagent = /datum/reagent/blob/b_sorium + + +/datum/blobstrain/reagent/b_sorium/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(prob(damage)) + reagent_vortex(B, TRUE, damage * 0.7) + return ..() + +/datum/reagent/blob/b_sorium + name = "Сорий" + id = "blob_sorium" + taste_description = "толчок" + color = "#B68D00" + +/datum/reagent/blob/b_sorium/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.6*reac_volume, BRUTE) + if(prob(30)) + reagent_vortex(exposed_mob, TRUE, reac_volume) + +/proc/reagent_vortex(mob/living/M, setting_type, volume) + var/turf/pull = get_turf(M) + if(!setting_type) + new /obj/effect/temp_visual/implosion(pull) + playsound(pull, 'sound/effects/whoosh.ogg', 25, 1) //credit to Robinhood76 of Freesound.org for this. + else + new /obj/effect/temp_visual/shockwave(pull) + playsound(pull, 'sound/effects/bang.ogg', 25, 1) + var/range_power = clamp(round(volume/5, 1), 1, 5) + for(var/atom/movable/X in range(range_power,pull)) + if(iseffect(X)) + continue + if(X.move_resist <= MOVE_FORCE_DEFAULT && !X.anchored) + var/distance = get_dist(X, pull) + var/moving_power = max(range_power - distance, 1) + spawn(0) + if(moving_power > 2) //if the vortex is powerful and we're close, we get thrown + if(setting_type) + var/atom/throw_target = get_edge_target_turf(X, get_dir(X, get_step_away(X, pull))) + var/throw_range = 5 - distance + X.throw_at(throw_target, throw_range, 1) + else + X.throw_at(pull, distance, 1) + else + if(setting_type) + for(var/i = 0, i < moving_power, i++) + sleep(2) + if(!step_away(X, pull)) + break + else + for(var/i = 0, i < moving_power, i++) + sleep(2) + if(!step_towards(X, pull)) + break + + diff --git a/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm b/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm new file mode 100644 index 00000000000..ac61fb804db --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/cryogenic_poison.dm @@ -0,0 +1,36 @@ +//does brute, burn, and toxin damage, and cools targets down +/datum/blobstrain/reagent/cryogenic_poison + name = "Криогенный яд" + description = "впрыскивает в цель замораживающий яд, нанося небольшой урон от удара, но нанося большой урон с течением времени." + analyzerdescdamage = "Вводит в цель замораживающий яд, который постепенно затвердевает внутренние органы цели." + color = "#8BA6E9" + complementary_color = "#7D6EB4" + blobbernaut_message = "injects" + message = "Блоб ранит вас" + message_living = ", и вы чувствуете, что ваши внутренности твердеют" + reagent = /datum/reagent/blob/cryogenic_poison + +/datum/reagent/blob/cryogenic_poison + name = "Криогенный яд" + id = "blob_cryogenic_poison" + description = "впрыскивает в цель замораживающий яд, который со временем наносит большой урон." + color = "#8BA6E9" + taste_description = "заморозка мозга" + +/datum/reagent/blob/cryogenic_poison/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + if(exposed_mob.reagents) + exposed_mob.reagents.add_reagent(/datum/reagent/consumable/frostoil, 0.3*reac_volume) + exposed_mob.reagents.add_reagent(/datum/reagent/consumable/drink/cold/ice, 0.3*reac_volume) + exposed_mob.reagents.add_reagent(/datum/reagent/blob/cryogenic_poison, 0.3*reac_volume) + exposed_mob.apply_damage(0.2*reac_volume, BRUTE, forced=TRUE) + +/datum/reagent/blob/cryogenic_poison/on_mob_life(mob/living/carbon/exposed_mob, seconds_per_tick, times_fired) + . = ..() + var/need_mob_update + need_mob_update = exposed_mob.adjustBruteLoss(0.5 * REM * seconds_per_tick, updating_health = FALSE) + need_mob_update += exposed_mob.adjustFireLoss(0.5 * REM * seconds_per_tick, updating_health = FALSE) + need_mob_update += exposed_mob.adjustToxLoss(0.5 * REM * seconds_per_tick, updating_health = FALSE) + if(need_mob_update) + . = STATUS_UPDATE_HEALTH diff --git a/code/modules/antagonists/blob/blobstrains/debris_devourer.dm b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm new file mode 100644 index 00000000000..ce7a9780637 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/debris_devourer.dm @@ -0,0 +1,71 @@ +#define DEBRIS_DENSITY (length(core.contents) / (length(overmind.blobs_legit) * 0.25)) // items per blob + +// Accumulates junk liberally +/datum/blobstrain/debris_devourer + name = "Пожиратель мусора" + description = "бросает поглощенные предметы и трупы в цели. Наносит очень низкий урон травмами без запуска объектов." + analyzerdescdamage = "Наносит очень низкий урон травмами и может метать поглощенные предметы при атаке." + analyzerdesceffect = "Пожирает незакрепленные предметы и трупы, и бросает их при атаке. Поглощенные объекты снижают входящий урон." + color = "#8B1000" + complementary_color = "#00558B" + blobbernaut_message = "blasts" + message = "Блоб бьет тебя" + + +/datum/blobstrain/debris_devourer/attack_living(mob/living/L, list/nearby_blobs) + send_message(L) + for (var/obj/structure/blob/blob in nearby_blobs) + debris_attack(L, blob) + +/datum/blobstrain/debris_devourer/on_sporedeath(mob/living/spore) + var/obj/structure/blob/special/core/core = overmind.blob_core + for(var/i in 1 to 3) + var/obj/item/I = pick(core.contents) + if(I && !QDELETED(I)) + I.forceMove(get_turf(spore)) + I.throw_at(get_edge_target_turf(spore,pick(GLOB.alldirs)), 6, 5, spore, TRUE, FALSE, null, 3) + +/datum/blobstrain/debris_devourer/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this + if(overmind) + for (var/atom/A in T) + A.blob_vore_act(overmind.blob_core) + return TRUE + +/datum/blobstrain/debris_devourer/proc/debris_attack(atom/attacking, atom/source) + var/obj/structure/blob/special/core/core = overmind.blob_core + if(prob(40 * DEBRIS_DENSITY)) // Pretend the items are spread through the blob and its mobs and not in the core. + var/obj/item/I = length(core.contents) ? pick(core.contents) : null + if(!QDELETED(I)) + if(isobj(I)) + I.obj_flags |= IGNORE_BLOB_ACT + addtimer(CALLBACK(src, PROC_REF(remove_protection), I), BLOB_ACT_PROTECTION_TIME) + I.forceMove(get_turf(source)) + I.throw_at(attacking, 6, 5, overmind, TRUE, FALSE, null, 3) + +/datum/blobstrain/debris_devourer/proc/remove_protection(obj/item) + item.obj_flags &= ~IGNORE_BLOB_ACT + +/datum/blobstrain/debris_devourer/blobbernaut_attack(atom/attacking, mob/living/simple_animal/hostile/blobbernaut) + debris_attack(attacking, blobbernaut) + +/datum/blobstrain/debris_devourer/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this + var/obj/structure/blob/special/core/core = overmind.blob_core + return round(max((coefficient*damage)-min(coefficient*DEBRIS_DENSITY, 10), 0)) // reduce damage taken by items per blob, up to 10 + +/datum/blobstrain/debris_devourer/examine(mob/user) + . = ..() + var/obj/structure/blob/special/core/core = overmind.blob_core + if(isobserver(user)) + . += span_notice("Поглощенный мусор в настоящее время снижает получаемый урон на [round(max(min(DEBRIS_DENSITY, 10),0))]") + else + switch (round(max(min(DEBRIS_DENSITY, 10),0))) + if(0) + . += span_notice("В настоящее время поглощенного мусора недостаточно, чтобы уменьшить урон.") + if(1 to 3) + . += span_notice("Поглощенный мусор в настоящее время снижает получаемый урон на очень небольшую величину.") // these roughly correspond with force description strings + if(4 to 7) + . += span_notice("Поглощенный мусор в настоящее время незначительно снижает получаемый урон.") + if(8 to 10) + . += span_notice("Поглощенный мусор в настоящее время снижает получаемый урон на среднюю величину.") + +#undef DEBRIS_DENSITY diff --git a/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm new file mode 100644 index 00000000000..7c593fd42d5 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/distributed_neurons.dm @@ -0,0 +1,38 @@ +//kills unconscious targets and turns them into blob zombies, produces fragile spores when killed. Spore produced by factories are sentient. +/datum/blobstrain/reagent/distributed_neurons + name = "Распределенные нейроны" + description = "наносит средне-низкий урон токсинами и превращает бессознательные цели в зомби блоба." + effectdesc = "при разрушении также производит хрупкие споры. Споры, производимые фабриками, разумны." + shortdesc = "наносит средне-низкий урон токсинами и убьет все цели, находящиеся без сознания, при атаке. Споры, производимые фабриками, разумны." + analyzerdescdamage = "Наносит средне-низкий урон токсинами и зомбирует людей, находящихся без сознания." + analyzerdesceffect = "При разрушении производит хрупкие споры. Споры, производимые фабриками, разумны." + color = "#E88D5D" + complementary_color = "#823ABB" + message_living = "и ты чувствуешь усталость" + reagent = /datum/reagent/blob/distributed_neurons + +/datum/blobstrain/reagent/distributed_neurons/damage_reaction(obj/structure/blob/blob_tile, damage, damage_type, damage_flag) + if((damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) && blob_tile.get_integrity() - damage <= 0 && prob(15)) //if the cause isn't fire or a bomb, the damage is less than 21, we're going to die from that damage, 15% chance of a shitty spore. + blob_tile.visible_message(span_boldwarning("Спора вылетает из блоба!")) + blob_tile.overmind.create_spore(blob_tile.loc, /mob/living/simple_animal/hostile/blob_minion/spore/minion/weak) + return ..() + +/datum/reagent/blob/distributed_neurons + name = "Распределенные нейроны" + id = "blob_distributed_neurons" + color = "#E88D5D" + taste_description = "шипящий" + +/datum/reagent/blob/distributed_neurons/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.6*reac_volume, TOX) + if(overmind && ishuman(exposed_mob)) + if(exposed_mob.stat == UNCONSCIOUS) + exposed_mob.investigate_log("has been killed by distributed neurons (blob).", INVESTIGATE_DEATHS) + exposed_mob.death() //sleeping in a fight? bad plan. + if(exposed_mob.stat == DEAD && overmind.can_buy(BLOB_ZOMBIFICATION_COST)) + var/mob/living/simple_animal/hostile/blob_minion/spore/minion/spore = overmind.create_spore(get_turf(exposed_mob)) + spore.zombify(exposed_mob) + overmind.add_points(-5) + to_chat(overmind, span_notice("Потрачено [BLOB_ZOMBIFICATION_COST] ресурса на зомбификацию [exposed_mob].")) diff --git a/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm new file mode 100644 index 00000000000..3ad849bb0c5 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/electromagnetic_web.dm @@ -0,0 +1,37 @@ +//does burn damage and EMPs, slightly fragile +/datum/blobstrain/reagent/electromagnetic_web + name = "Электромагнитная паутина" + color = "#83ECEC" + complementary_color = "#EC8383" + description = "наносит большой урон от ожогов и излучает ЭМИ." + effectdesc = "также получает значительно увеличенный урон и выпускает ЭМИ после разрушения." + analyzerdescdamage = "Наносит большой урон от ожогов и излучает ЭМИ." + analyzerdesceffect = "Хрупок ко всем типам урона и получает огромный урон от травм. Кроме того, при разрушении выпускает небольшой ЭМИ." + reagent = /datum/reagent/blob/electromagnetic_web + +/datum/blobstrain/reagent/electromagnetic_web/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage_type == BRUTE) // take full brute, divide by the multiplier to get full value + return damage / B.brute_resist + return damage * 1.25 //a laser will do 25 damage, which will kill any normal blob + +/datum/blobstrain/reagent/electromagnetic_web/attack_mech(obj/mecha/mech) + if(prob(50)) + empulse(mech.loc, 0, 1) + +/datum/blobstrain/reagent/electromagnetic_web/death_reaction(obj/structure/blob/B, damage_flag) + if(damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) + empulse(B.loc, 1, 3) //less than screen range, so you can stand out of range to avoid it + +/datum/reagent/blob/electromagnetic_web + name = "Электромагнитная паутина" + id = "blob_electromagnetic_web" + taste_description = "поп-рок" + color = "#83ECEC" + +/datum/reagent/blob/electromagnetic_web/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + if(prob(reac_volume*2)) + exposed_mob.emp_act(EMP_LIGHT) + if(exposed_mob) + exposed_mob.apply_damage(reac_volume, BURN, forced=TRUE) diff --git a/code/modules/antagonists/blob/blobstrains/energized_jelly.dm b/code/modules/antagonists/blob/blobstrains/energized_jelly.dm new file mode 100644 index 00000000000..82d48db407c --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/energized_jelly.dm @@ -0,0 +1,43 @@ +//does tons of oxygen damage and a little stamina, immune to tesla bolts, weak to EMP +/datum/blobstrain/reagent/energized_jelly + name = "Энергетическое желе" + description = "наносит урон выносливости и средний урон гипоксией, а также лишает цели возможности дышать." + effectdesc = "также проводит электричество, но получает урон от ЭМИ. Вызывает электрические разряды в теле после удара." + analyzerdescdamage = "Наносит высокий урон выносливости, средний урон гипоксией и не дает цели дышать." + analyzerdesceffect = "Невосприимчив к электричеству и легко его проводит, но слаб к ЭМИ. Вызывает электрические разряды в теле после удара." + color = "#EFD65A" + complementary_color = "#00E5B1" + reagent = /datum/reagent/blob/energized_jelly + +/datum/blobstrain/reagent/energized_jelly/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if((damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) && B.get_integrity() - damage <= 0 && prob(30)) + do_sparks(rand(2, 4), FALSE, B) + return ..() + +/datum/blobstrain/reagent/energized_jelly/tesla_reaction(obj/structure/blob/B, power) + return FALSE + +/datum/blobstrain/reagent/energized_jelly/emp_reaction(obj/structure/blob/B, severity) + var/damage = rand(30, 50) - severity * rand(10, 15) + B.take_damage(damage, BURN, ENERGY) + +/datum/reagent/blob/energized_jelly + name = "Энергетическое желе" + id = "blob_energized_jelly" + taste_description = "желатин" + color = "#EFD65A" + + +/datum/reagent/blob/energized_jelly/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.LoseBreath(round(0.2*reac_volume)) + exposed_mob.adjustStaminaLoss(reac_volume * 1.2) + exposed_mob.apply_damage(0.6*reac_volume, OXY) + if(exposed_mob.reagents) + if(exposed_mob.reagents.has_reagent("teslium") && prob(0.6 * reac_volume)) + exposed_mob.electrocute_act((0.5 * reac_volume), "разряда блоба", flags = SHOCK_NOGLOVES) + exposed_mob.reagents.del_reagent("teslium") + return //don't add more teslium after you shock it out of someone. + exposed_mob.reagents.add_reagent("teslium", 0.125 * reac_volume) // a little goes a long way + diff --git a/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm new file mode 100644 index 00000000000..6774f051a35 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/explosive_lattice.dm @@ -0,0 +1,80 @@ +//does aoe brute damage when hitting targets, is immune to explosions +/datum/blobstrain/reagent/explosive_lattice + name = "Взрывная решетка" + description = "атакует небольшими взрывами, нанося среднее сочетание урона ожогами и травмами всем, кто находится близко к цели. Споры взрываются при смерти." + effectdesc = "также имеет повышенную сопротивляемость взрывам, но получает повышенный урон от огня и других источников энергии." + analyzerdescdamage = "Атакует небольшими взрывами, нанося среднее сочетание урона ожогами и травмами всем, кто находится близко к цели. Споры взрываются при смерти." + analyzerdesceffect = "Обладает высокой устойчивостью к взрывам, но получает повышенный урон от огня и других источников энергии." + color = "#8B2500" + complementary_color = "#00668B" + blobbernaut_message = "blasts" + message = "Блоб взрывает тебя" + reagent = /datum/reagent/blob/explosive_lattice + +/datum/blobstrain/reagent/explosive_lattice/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage_flag == BOMB) + return 0 + else if(damage_flag != MELEE && damage_flag != BULLET && damage_flag != LASER) + return damage * 1.5 + return ..() + +/datum/blobstrain/reagent/explosive_lattice/on_sporedeath(mob/living/spore) + var/obj/effect/temp_visual/explosion/fast/effect = new /obj/effect/temp_visual/explosion/fast(get_turf(spore)) + effect.alpha = 150 + for(var/mob/living/actor in orange(get_turf(spore), 1)) + if(ROLE_BLOB in actor.faction) // No friendly fire + continue + actor.take_overall_damage(BLOB_REAGENT_SPORE_VOL, BLOB_REAGENT_SPORE_VOL) + +/datum/reagent/blob/explosive_lattice + name = "Взрывная решетка" + id = "blob_explosive_lattice" + taste_description = "бомба" + color = "#8B2500" + +/datum/reagent/blob/explosive_lattice/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + if(exposed_mob.stat == DEAD || HAS_TRAIT(exposed_mob, TRAIT_BLOB_ALLY)) + return 0 //the dead, and blob mobs, don't cause reactions + return reac_volume + +/datum/reagent/blob/explosive_lattice/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + var/brute_loss = 0 + var/burn_loss = 0 + var/bomb_armor = 0 + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + + if(reac_volume >= 10) // If it's not coming from a sporecloud, AOE 'explosion' damage + var/epicenter_turf = get_turf(exposed_mob) + var/obj/effect/temp_visual/explosion/fast/ex_effect = new /obj/effect/temp_visual/explosion/fast(get_turf(exposed_mob)) + ex_effect.alpha = 150 + + // Total damage to epicenter mob of 0.7*reac_volume, like a mid-tier strain + brute_loss = reac_volume*0.4 + + bomb_armor = exposed_mob.getarmor(null, BOMB) + if(bomb_armor) // Same calculation and proc that ex_act uses on mobs + brute_loss = brute_loss*(2 - round(bomb_armor*0.01, 0.05)) + + burn_loss = brute_loss + + exposed_mob.take_overall_damage(brute_loss, burn_loss) + + for(var/mob/living/nearby_mob in orange(epicenter_turf, 1)) + if(ROLE_BLOB in nearby_mob.faction) // No friendly fire. + continue + if(nearby_mob == exposed_mob) // We've already hit the epicenter mob + continue + // AoE damage of 0.5*reac_volume to everyone in a 1 tile range + brute_loss = reac_volume * 0.25 + burn_loss = brute_loss + + bomb_armor = nearby_mob.getarmor(null, BOMB) + if(bomb_armor) // Same calculation and prod that ex_act uses on mobs + brute_loss = brute_loss*(2 - round(bomb_armor*0.01, 0.05)) + burn_loss = brute_loss + + nearby_mob.take_overall_damage(brute_loss, burn_loss) + + else + exposed_mob.apply_damage(0.6*reac_volume, BRUTE, forced = TRUE) diff --git a/code/modules/antagonists/blob/blobstrains/multiplex.dm b/code/modules/antagonists/blob/blobstrains/multiplex.dm new file mode 100644 index 00000000000..0930da2eae0 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/multiplex.dm @@ -0,0 +1,40 @@ +/datum/blobstrain/multiplex + var/list/blobstrains + var/typeshare + +/datum/blobstrain/multiplex/New(mob/camera/blob/new_overmind, list/blobstrains) + . = ..() + for (var/bt in blobstrains) + if(ispath(bt, /datum/blobstrain)) + src.blobstrains += new bt(overmind) + else if(istype(bt, /datum/blobstrain)) + var/datum/blobstrain/bts = bt + bts.overmind = overmind + src.blobstrains += bt + typeshare = (0.8 * length(src.blobstrains)) - (length(src.blobstrains)-1) // 1 is 80%, 2 are 60% etc + +/datum/blobstrain/multiplex/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag, coefficient = 1) //when the blob takes damage, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.damage_reaction(B, damage, damage_type, damage_flag, coefficient*typeshare) + +/datum/blobstrain/multiplex/death_reaction(obj/structure/blob/B, damage_flag, coefficient = 1) //when a blob dies, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.death_reaction(B, damage_flag, coefficient*typeshare) + +/datum/blobstrain/multiplex/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O, coefficient = 1) //when the blob expands, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.expand_reaction(B, newB, T, O, coefficient*typeshare) + +/datum/blobstrain/multiplex/tesla_reaction(obj/structure/blob/B, power, coefficient = 1) //when the blob is hit by a tesla bolt, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.tesla_reaction(B, power, coefficient*typeshare) + if(prob(. / length(blobstrains) * 100)) + return 1 + +/datum/blobstrain/multiplex/extinguish_reaction(obj/structure/blob/B, coefficient = 1) //when the blob is hit with water, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.extinguish_reaction(B, coefficient*typeshare) + +/datum/blobstrain/multiplex/emp_reaction(obj/structure/blob/B, severity, coefficient = 1) //when the blob is hit with an emp, do this + for (var/datum/blobstrain/bt in blobstrains) + . += bt.emp_reaction(B, severity, coefficient*typeshare) diff --git a/code/modules/antagonists/blob/blobstrains/networked_fibers.dm b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm new file mode 100644 index 00000000000..b1e0ec67c32 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/networked_fibers.dm @@ -0,0 +1,46 @@ +//does massive brute and burn damage, but can only expand manually +/datum/blobstrain/reagent/networked_fibers + name = "Сетевые волокна" + description = "наносит большое количество урона травмами и ожогами и генерирует ресурсы быстрее, но может расширяться только с помощью перемещения ядра или узлов." + shortdesc = "наносит сочетание урона травмами и ожогами." + effectdesc = "перемещает ваше ядро ​​или узел при ручном расширении рядом с ним." + analyzerdescdamage = "Наносит большое количество урона травмами и ожогами." + analyzerdesceffect = "Мобильный и быстро генерирует ресурсы." + color = "#4F4441" + complementary_color = "#414C4F" + reagent = /datum/reagent/blob/networked_fibers + core_regen_bonus = 5 + +/datum/blobstrain/reagent/networked_fibers/expand_reaction(obj/structure/blob/spawning_blob, obj/structure/blob/new_blob, turf/chosen_turf, mob/camera/blob/overmind, offstation) + if(!overmind && new_blob.overmind || offstation) + new_blob.overmind.add_points(1) + if(offstation) + to_chat(usr, span_warning("Двигать ядро или узел за пределы станции нельзя.")) + qdel(new_blob) + return FALSE + + var/list/range_contents = (is_there_multiz())? urange_multiz(1, new_blob) : range(1, new_blob) + + for(var/obj/structure/blob/possible_expander in range_contents) + if(possible_expander.overmind == overmind && (istype(possible_expander, /obj/structure/blob/special/core) || istype(possible_expander, /obj/structure/blob/special/node))) + new_blob.forceMove(get_turf(possible_expander)) + possible_expander.forceMove(chosen_turf) + possible_expander.setDir(get_dir(new_blob, possible_expander)) + return TRUE + overmind.add_points(BLOB_EXPAND_COST) + qdel(new_blob) + return FALSE + +//does massive brute and burn damage, but can only expand manually +/datum/reagent/blob/networked_fibers + name = "Сетевые волокна" + id = "blob_networked_fibers" + taste_description = "эффективность" + color = "#4F4441" + +/datum/reagent/blob/networked_fibers/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.6*reac_volume, BRUTE, forced = TRUE) + if(!QDELETED(exposed_mob)) + exposed_mob.apply_damage(0.6*reac_volume, BURN, forced = TRUE) diff --git a/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm new file mode 100644 index 00000000000..5baba8b6deb --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/pressurized_slime.dm @@ -0,0 +1,55 @@ +//does low brute damage, oxygen damage, and stamina damage and wets tiles when damaged +/datum/blobstrain/reagent/pressurized_slime + name = "Сжатая слизь" + description = "наносит низкий урон травмами и урон гипоксией, высокий урон выносливости и делает пол под целями очень скользкими, туша их." + effectdesc = "также сделает плитки скользкими рядом с атакованными плитками. Устойчив к грубым атакам." + analyzerdescdamage = "Наносит низкий урон травмами и урон гипоксией, высокий урон выносливости и делает пол под целями очень скользкими, туша их. Устойчив к атакам травмами." + analyzerdesceffect = "При нападении или убийстве смазывает близлежащие плитки пола, тушая все на них." + color = "#AAAABB" + complementary_color = "#BBBBAA" + blobbernaut_message = "emits slime at" + message = "Блоб плюхается в тебя" + message_living = ", и ты задыхаешься" + reagent = /datum/reagent/blob/pressurized_slime + +/datum/blobstrain/reagent/pressurized_slime/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if((damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) || damage_type != BURN) + extinguisharea(B, damage) + if(damage_type == BRUTE) + return damage * 0.5 + return ..() + +/datum/blobstrain/reagent/pressurized_slime/death_reaction(obj/structure/blob/B, damage_flag) + if(damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) + B.visible_message(span_boldwarning("Блоб разрывается, обрызгивая область жидкостью!")) + extinguisharea(B, 30) + +/datum/blobstrain/reagent/pressurized_slime/proc/extinguisharea(obj/structure/blob/B, probchance) + for(var/turf/simulated/T as anything in range(1, B)) + if(!istype(T) || iswallturf(T) || ismineralturf(T)) + continue + if(prob(probchance)) + T.MakeSlippery(TURF_WET_LUBE, min_wet_time = 5 SECONDS, wet_time_to_add = 5 SECONDS) + for(var/obj/O in T) + O.extinguish() + for(var/mob/living/L in T) + L.adjust_wet_stacks(2.5) + L.ExtinguishMob() + +/datum/reagent/blob/pressurized_slime + name = "Сжатая слизь" + id = "blob_pressurized_slime" + taste_description = "губка" + color = "#AAAABB" + +/datum/reagent/blob/pressurized_slime/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + var/turf/simulated/location_turf = get_turf(exposed_mob) + if(istype(location_turf) && !(iswallturf(location_turf) || ismineralturf(location_turf)) && prob(reac_volume)) + location_turf.MakeSlippery(TURF_WET_LUBE, min_wet_time = 5 SECONDS, wet_time_to_add = 5 SECONDS) + exposed_mob.adjust_wet_stacks(reac_volume / 10) + exposed_mob.apply_damage(0.4*reac_volume, BRUTE, forced=TRUE) + if(exposed_mob) + exposed_mob.adjustStaminaLoss(reac_volume, FALSE) + exposed_mob.apply_damage(0.4 * reac_volume, OXY) diff --git a/code/modules/antagonists/blob/blobstrains/radioactive_gel.dm b/code/modules/antagonists/blob/blobstrains/radioactive_gel.dm new file mode 100644 index 00000000000..88d3da953ac --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/radioactive_gel.dm @@ -0,0 +1,34 @@ + +//sets you on fire, does burn damage, explodes into flame when burnt, weak to water +/datum/blobstrain/reagent/radioactive_gel + name = "Радиоактивный гель" + description = "наносит средний урон токсинами и небольшой урон травмами, но облучает тех, кого задевает." + effectdesc = "при получении урона облучает окружающих." + analyzerdescdamage = "Наносит средний урон токсинами и небольшой урон травмами, но облучает тех, кого задевает." + analyzerdesceffect = "При получении урона облучает окружающих." + color = "#2476f0" + complementary_color = "#24f0f0" + blobbernaut_message = "splashes" + message_living = ", и вы чувствуете странное тепло изнутри" + reagent = /datum/reagent/blob/radioactive_gel + + +/datum/blobstrain/reagent/radioactive_gel/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if((damage_flag == ENERGY || damage_flag == LASER) && prob(40)) + for(var/mob/living/l in range(5, B)) + l.apply_effect(damage, IRRADIATE) + return ..() + +/datum/reagent/blob/radioactive_gel + name = "Рadioactive_gel" + id = "blob_radioactive_gel" + taste_description = "радиация" + color = "#2476f0" + +/datum/reagent/blob/radioactive_gel/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.3 * reac_volume, TOX) + exposed_mob.apply_damage(0.2 * reac_volume, BRUTE) // lets not have IPC / plasmaman only take 7.5 damage from this + if(exposed_mob.reagents) + exposed_mob.reagents.add_reagent("uranium", 0.35 * reac_volume) diff --git a/code/modules/antagonists/blob/blobstrains/reactive_spines.dm b/code/modules/antagonists/blob/blobstrains/reactive_spines.dm new file mode 100644 index 00000000000..66edcea4c51 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/reactive_spines.dm @@ -0,0 +1,46 @@ +//does brute damage through armor and bio resistance +/datum/blobstrain/reagent/reactive_spines + name = "Реактивные шипы" + description = "наносит большой урон травмами через броню и биосопротивление." + effectdesc = "также будет реагировать на атаку ожогами или травмами, атакуя все в ближнем бою." + analyzerdescdamage = "Наносит высокий урон травмами, игнорируя броню и биосопротивление." + analyzerdesceffect = "При нанесении урона ожогами и травмами блоб яростно бросается в атаку, атакуя все, что находится поблизости." + color = "#9ACD32" + complementary_color = "#FFA500" + blobbernaut_message = "stabs" + message = "Блоб ранит тебя" + reagent = /datum/reagent/blob/reactive_spines + COOLDOWN_DECLARE(retaliate_cooldown) + +/datum/blobstrain/reagent/reactive_spines/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage && ((damage_type == BRUTE) || (damage_type == BURN)) && B.get_integrity() - damage > 0 && COOLDOWN_FINISHED(src, retaliate_cooldown)) // Is there any damage, is it burn or brute, will we be alive, and has the cooldown finished? + COOLDOWN_START(src, retaliate_cooldown, 2.5 SECONDS) // 2.5 seconds before auto-retaliate can whack everything within 1 tile again + B.visible_message(span_boldwarning("Блоб отвечает, набрасываясь!")) + for(var/atom/thing in range(1, B)) + if(!thing.can_blob_attack()) + continue + var/attacked_turf = get_turf(thing) + if(isliving(thing) && !HAS_TRAIT(thing, TRAIT_BLOB_ALLY)) // Make sure to inject strain-reagents with automatic attacks when needed. + B.blob_attack_animation(attacked_turf, overmind) + attack_living(thing) + + else if(thing.blob_act(B)) // After checking for mobs, whack everything else with the standard attack + B.blob_attack_animation(attacked_turf, overmind) // Only play the animation if the attack did something meaningful + + return ..() + +/datum/reagent/blob/reactive_spines + name = "Реактивные шипы" + id = "blob_reactive_spines" + taste_description = "камень" + color = "#9ACD32" + +/datum/reagent/blob/reactive_spines/return_mob_expose_reac_volume(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + if(exposed_mob.stat == DEAD || HAS_TRAIT(exposed_mob, TRAIT_BLOB_ALLY)) + return 0 //the dead, and blob mobs, don't cause reactions + return reac_volume + +/datum/reagent/blob/reactive_spines/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.adjustBruteLoss(reac_volume) diff --git a/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm new file mode 100644 index 00000000000..291ba371e0a --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/regenerative_materia.dm @@ -0,0 +1,40 @@ +//does toxin damage, hallucination, targets think they're not hurt at all +/datum/blobstrain/reagent/regenerative_materia + name = "Регенеративная Материя" + description = "наносит средний начальный урон токсинами, впрыскивая яд, который наносит больший урон токсинами и заставляет цели верить, что они полностью здоровы. Ядро восстанавливается гораздо быстрее." + analyzerdescdamage = "Наносит средний начальный урон токсинами, вводя яд, который наносит больший урон токсинами и заставляет цели верить, что они полностью здоровы. Ядро восстанавливается гораздо быстрее." + color = "#A88FB7" + complementary_color = "#AF7B8D" + message_living = ", и ты чувствуешь себя живым" + reagent = /datum/reagent/blob/regenerative_materia + core_regen_bonus = 18 + point_rate_bonus = 2 + +/datum/reagent/blob/regenerative_materia + name = "Регенеративная Материя" + id = "blob_regenerative_materia" + taste_description = "небеса" + color = "#A88FB7" + +/datum/reagent/blob/regenerative_materia/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + if(iscarbon(exposed_mob)) + exposed_mob.Druggy(reac_volume * 2 SECONDS) + if(exposed_mob.reagents) + exposed_mob.reagents.add_reagent(/datum/reagent/blob/regenerative_materia, 0.2*reac_volume) + exposed_mob.reagents.add_reagent(/datum/reagent/toxin/spore, 0.2*reac_volume) + exposed_mob.apply_damage(0.7*reac_volume, TOX) + +/datum/reagent/blob/regenerative_materia/on_mob_life(mob/living/carbon/metabolizer, seconds_per_tick, times_fired) + . = ..() + if(metabolizer.adjustToxLoss(1 * REM * seconds_per_tick, updating_health = FALSE)) + return STATUS_UPDATE_HEALTH + +/datum/reagent/blob/regenerative_materia/on_mob_start_metabolize(mob/living/metabolizer) + . = ..() + metabolizer.apply_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy, type) + +/datum/reagent/blob/regenerative_materia/on_mob_end_metabolize(mob/living/metabolizer) + . = ..() + metabolizer.remove_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy, type) diff --git a/code/modules/antagonists/blob/blobstrains/replicating_foam.dm b/code/modules/antagonists/blob/blobstrains/replicating_foam.dm new file mode 100644 index 00000000000..8d6f983d0cb --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/replicating_foam.dm @@ -0,0 +1,40 @@ +/datum/blobstrain/reagent/replicating_foam + name = "Репликационная пена" + description = "наносит средний урон травмами и иногда дополнительно расширяется при расширении." + shortdesc = "наносит средний урон травмами." + effectdesc = "также будет расширяться при атаке ожогами, но получает больше урона травмами." + color = "#7B5A57" + complementary_color = "#57787B" + analyzerdescdamage = "Наносит средний урон травмами." + analyzerdesceffect = "Расширяется при атаке ожогами, иногда дополнительно расширяется при расширении и уязвим к урону травмами." + reagent = /datum/reagent/blob/replicating_foam + + +/datum/blobstrain/reagent/replicating_foam/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage_type == BRUTE) + damage = damage * 2 + else if(damage_type == BURN && damage > 0 && B.get_integrity() - damage > 0 && prob(50)) + if(damage_flag == FIRE) + return ..() + var/obj/structure/blob/newB = B.expand(null, null, 0) + if(newB) + newB.update_integrity(B.get_integrity() - damage) + newB.update_blob() + return ..() + + +/datum/blobstrain/reagent/replicating_foam/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O) + if(prob(30)) + newB.expand(null, null, 0) //do it again! + return TRUE + +/datum/reagent/blob/replicating_foam + name = "Репликационная пена" + id = "blob_replicating_foam" + taste_description = "дублирование" + color = "#7B5A57" + +/datum/reagent/blob/replicating_foam/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.7*reac_volume, BRUTE, forced = TRUE) diff --git a/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm b/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm new file mode 100644 index 00000000000..90f62f0cd24 --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/shifting_fragments.dm @@ -0,0 +1,40 @@ +//does brute damage, shifts away when damaged +/datum/blobstrain/reagent/shifting_fragments + name = "Смещающиеся фрагменты" + description = "наносит средний урон травмами." + effectdesc = "также смещает плитки при атаке, повреждении и расширении." + analyzerdescdamage = "Наносит средний урон травмами." + analyzerdesceffect = "Смещает плитки при атаке, повреждении и расширении." + color = "#C8963C" + complementary_color = "#3C6EC8" + reagent = /datum/reagent/blob/shifting_fragments + +/datum/blobstrain/reagent/shifting_fragments/expand_reaction(obj/structure/blob/B, obj/structure/blob/newB, turf/T, mob/camera/blob/O) + if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield))) + newB.forceMove(get_turf(B)) + B.forceMove(T) + return TRUE + +/datum/blobstrain/reagent/shifting_fragments/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if((damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) && damage > 0 && B.get_integrity() - damage > 0 && prob(20 + damage)) + var/list/blobstopick = list() + var/list/blob_structures = (is_there_multiz())? urange_multiz(1, B, TRUE) : orange(1, B) + for(var/obj/structure/blob/OB in blob_structures) + if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind.blobstrain.type == B.overmind.blobstrain.type) + blobstopick += OB //as long as the blob picked is valid; ie, a normal or shield blob that has the same chemical as we do, we can swap with it + if(blobstopick.len) + var/obj/structure/blob/targeted = pick(blobstopick) //randomize the blob chosen, because otherwise it'd tend to the lower left + var/turf/T = get_turf(targeted) + targeted.forceMove(get_turf(B)) + B.forceMove(T) //swap the blobs + return ..() + +/datum/reagent/blob/shifting_fragments + name = "Смещающиеся фрагменты" + id = "blob_shifting_fragments" + color = "#C8963C" + +/datum/reagent/blob/shifting_fragments/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.7*reac_volume, BRUTE, forced = TRUE) diff --git a/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm new file mode 100644 index 00000000000..167d39f1e5e --- /dev/null +++ b/code/modules/antagonists/blob/blobstrains/synchronous_mesh.dm @@ -0,0 +1,43 @@ +//does brute damage, bonus damage for each nearby blob, and spreads damage out +/datum/blobstrain/reagent/synchronous_mesh + name = "Синхронная сетка" + description = "наносит небольшой урон травмами, но каждая плитка поблизости также атакует цель, нанося суммируемый урон." + effectdesc = "также распределяет урон между каждой плиткой рядом с атакованной плиткой." + analyzerdescdamage = "Наносит небольшой урон травмами, увеличивающийся с каждой плиткой рядом с целью." + analyzerdesceffect = "При атаке распределяет урон между всеми плитками рядом с атакованной плиткой." + color = "#65ADA2" + complementary_color = "#AD6570" + blobbernaut_message = "synchronously strikes" + message = "Блоб поражают тебя" + reagent = /datum/reagent/blob/synchronous_mesh + +/datum/blobstrain/reagent/synchronous_mesh/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) + if(damage_flag == MELEE || damage_flag == BULLET || damage_flag == LASER) //the cause isn't fire or bombs, so split the damage + var/damagesplit = 1 //maximum split is 9, reducing the damage each blob takes to 11% but doing that damage to 9 blobs + var/list/blob_structures = (is_there_multiz())? urange_multiz(1, B, TRUE) : orange(1, B) + for(var/obj/structure/blob/C in blob_structures) + if(!C.ignore_syncmesh_share && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //if it doesn't have the same chemical or is a core or node, don't split damage to it + damagesplit += 1 + for(var/obj/structure/blob/C in blob_structures) + if(!C.ignore_syncmesh_share && C.overmind && C.overmind.blobstrain.type == B.overmind.blobstrain.type) //only hurt blobs that have the same overmind chemical and aren't cores or nodes + C.take_damage(damage/damagesplit, damage_type, 0, 0) + return damage / damagesplit + else + return damage * 1.25 + +/datum/reagent/blob/synchronous_mesh + name = "Синхронная сетка" + id = "blob_synchronous_mesh" + taste_description = "токсичная плесень" + color = "#65ADA2" + +/datum/reagent/blob/synchronous_mesh/reaction_mob(mob/living/exposed_mob, methods=REAGENT_TOUCH, reac_volume, show_message, touch_protection, mob/camera/blob/overmind) + . = ..() + reac_volume = return_mob_expose_reac_volume(exposed_mob, methods, reac_volume, show_message, touch_protection, overmind) + exposed_mob.apply_damage(0.2*reac_volume, BRUTE, forced = TRUE) + var/list/blob_structures = (is_there_multiz())? urange_multiz(1, exposed_mob, TRUE) : range(1, exposed_mob) + if(exposed_mob && reac_volume) + for(var/obj/structure/blob/nearby_blob in blob_structures) //if the target is completely surrounded, this is 2.4*reac_volume bonus damage, total of 2.6*reac_volume + if(exposed_mob) + nearby_blob.blob_attack_animation(exposed_mob) //show them they're getting a bad time + exposed_mob.apply_damage(0.3*reac_volume, BRUTE, forced = TRUE) diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm new file mode 100644 index 00000000000..97be8c4b200 --- /dev/null +++ b/code/modules/antagonists/blob/overmind.dm @@ -0,0 +1,279 @@ +GLOBAL_LIST_EMPTY(overminds) + + +/mob/camera/blob + name = "Blob Overmind" + real_name = "Blob Overmind" + desc = "The overmind. It controls the blob." + icon = 'icons/mob/blob.dmi' + icon_state = "marker" + nightvision = 8 + sight = SEE_TURFS|SEE_MOBS|SEE_OBJS + invisibility = INVISIBILITY_OBSERVER + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + mouse_opacity = MOUSE_OPACITY_OPAQUE + see_invisible = SEE_INVISIBLE_LIVING + pass_flags = PASSBLOB + faction = list(ROLE_BLOB) + mouse_opacity = MOUSE_OPACITY_ICON + move_on_shuttle = TRUE + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + pass_flags = PASSBLOB + verb_say = "states" + + hud_type = /datum/hud/blob_overmind + var/obj/structure/blob/special/core/blob_core = null // The blob overmind's core + var/blob_points = 0 + var/max_blob_points = OVERMIND_MAX_POINTS_DEFAULT + var/last_attack = 0 + var/datum/blobstrain/reagent/blobstrain + var/list/blob_mobs = list() + /// A list of all blob structures + var/list/all_blobs = list() + var/list/resource_blobs = list() + var/list/factory_blobs = list() + var/list/node_blobs = list() + var/free_strain_rerolls = OVERMIND_STARTING_REROLLS + var/last_reroll_time = 0 //time since we last rerolled, used to give free rerolls + var/nodes_required = TRUE //if the blob needs nodes to place resource and factory blobs + var/list/blobs_legit = list() + var/max_count = 0 //The biggest it got before death + var/rerolling = FALSE + /// The list of strains the blob can reroll for. + var/list/strain_choices + /// Whether the blob split + var/split_used = FALSE + /// Is blob offspring of another blob + var/is_offspring = FALSE + /// Does the blob have an infinite resource? + var/is_infinity = FALSE + + +/mob/camera/blob/Initialize(mapload, core = null, starting_points = OVERMIND_STARTING_POINTS) + ADD_TRAIT(src, TRAIT_BLOB_ALLY, INNATE_TRAIT) + blob_points = starting_points + blob_core = core + GLOB.overminds |= src + var/new_name = "[initial(name)] ([rand(1, 999)])" + name = new_name + real_name = new_name + last_attack = world.time + select_strain(TRUE) + color = blobstrain.complementary_color + if(blob_core) + blob_core.update_blob() + . = ..() + START_PROCESSING(SSobj, src) + GLOB.blob_telepathy_mobs |= src + + +/mob/camera/blob/Destroy() + QDEL_NULL(blobstrain) + for(var/obj/structure/blob/blob_structure as anything in all_blobs) + blob_structure.overmind = null + blob_structure.update_blob() + all_blobs = null + resource_blobs = null + factory_blobs = null + node_blobs = null + for(var/mob/living/simple_animal/hostile/blob_minion/mob as anything in blob_mobs) + if(istype(mob) && !mob.factory_linked) + mob.death() + blob_mobs = null + GLOB.overminds -= src + QDEL_LIST_ASSOC_VAL(strain_choices) + + STOP_PROCESSING(SSobj, src) + GLOB.blob_telepathy_mobs -= src + + return ..() + + +/mob/camera/blob/process() + if(!blob_core) + qdel(src) + return + if(!free_strain_rerolls && (last_reroll_time + BLOB_POWER_REROLL_FREE_TIME < world.time)) + to_chat(src, span_boldnotice("Вы получили еще одну бесплатную смену штамма.")) + free_strain_rerolls = TRUE + track_z() + + +/mob/camera/blob/Login() + . = ..() + if(!. || !client) + return FALSE + sync_mind() + update_health_hud() + sync_lighting_plane_alpha() + add_points(0) + var/turf/T = get_turf(src) + if(isturf(T)) + update_z(T.z) + + +/mob/camera/blob/Logout() + update_z(null) + . = ..() + + +/mob/camera/blob/proc/can_attack() + return (world.time > (last_attack + CLICK_CD_RANGE)) + +/mob/camera/blob/Move(atom/newloc, direct = NONE, glide_size_override = 0, update_dir = TRUE) + if(world.time < last_movement) + return + last_movement = world.time + 0.5 // cap to 20fps + + var/obj/structure/blob/B = locate() in range(OVERMIND_MAX_CAMERA_STRAY, newloc) + if(B) + loc = newloc + else + return FALSE + + +/mob/camera/blob/can_z_move(direction, turf/start, turf/destination, z_move_flags = NONE, mob/living/rider) + . = ..() + if(!.) + return + var/turf/target_turf = . + if(!is_valid_turf(target_turf)) // Allows unplaced blobs to travel through station z-levels + if(z_move_flags & ZMOVE_FEEDBACK) + to_chat(src, span_warning("Ваш пункт назначения недействителен. Перейдите в другое место и попробуйте еще раз.")) + return null + +/mob/camera/blob/proc/is_valid_turf(turf/tile) + var/area/area = get_area(tile) + if((area && !(area.area_flags & BLOBS_ALLOWED)) || !tile || !is_station_level(tile.z)) + return FALSE + return TRUE + + +/mob/camera/blob/get_status_tab_items() + . = ..() + if(blob_core) + . += list(list("Здоровье ядра:", "[blob_core.obj_integrity]")) + . += list(list("Ресурсы:", "[(is_infinity || SSticker?.mode?.is_blob_infinity_points)? "INF" : "[blob_points]/[max_blob_points]"]")) + . += list(list("Критическая Масса:", "[TOTAL_BLOB_MASS]/[NEEDED_BLOB_MASS]")) + if(free_strain_rerolls) + . += list(list("Осталось бесплатных смен штамма:", "[free_strain_rerolls]")) + +/mob/camera/blob/update_health_hud() + if(!blob_core) + return FALSE + var/current_health = round((blob_core.obj_integrity / blob_core.max_integrity) * 100) + hud_used.blobhealthdisplay.maptext = MAPTEXT("
[current_health]%
") + for(var/mob/living/simple_animal/hostile/blob_minion/blobbernaut/blobbernaut in blob_mobs) + var/datum/hud/using_hud = blobbernaut.hud_used + if(!using_hud?.blobpwrdisplay) + continue + using_hud.blobpwrdisplay.maptext = MAPTEXT("
[current_health]%
") + + +/mob/camera/blob/say( + message, + bubble_type, + sanitize = TRUE, +) + if(!message) + return + + if(client) + if(GLOB.admin_mutes_assoc[ckey] & MUTE_IC) + to_chat(src, span_boldwarning("Вы не можете писать IC сообщения (мут).")) + return + if(client.handle_spam_prevention(message, MUTE_IC)) + return + + if(stat) + return + + blob_talk(message) + +/mob/camera/blob/proc/blob_talk(message) + + message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if(!message) + return + + add_say_logs(src, message, language = "BLOB") + + var/message_a = say_quote(message) + var/rendered = span_big(span_blob("\[Blob Telepathy\] [name]([blobstrain.name]) [message_a], [message]")) + relay_to_list_and_observers(rendered, GLOB.blob_telepathy_mobs, src) + +/mob/camera/blob/proc/add_points(points) + blob_points = clamp(blob_points + points, 0, max_blob_points) + hud_used.blobpwrdisplay.maptext = MAPTEXT("
[(is_infinity || SSticker?.mode?.is_blob_infinity_points)? "INF" : round(blob_points)]
") + + +/mob/camera/blob/proc/select_strain(first_select = FALSE) + var/reagent_type = pick(GLOB.valid_blobstrains) + set_strain(reagent_type, first_select) + + + +/mob/camera/blob/proc/set_strain(datum/blobstrain/new_strain, first_select = FALSE) + if(!ispath(new_strain)) + return FALSE + + var/had_strain = FALSE + if(istype(blobstrain)) + blobstrain.on_lose() + qdel(blobstrain) + had_strain = TRUE + + blobstrain = new new_strain(src) + var/datum/antagonist/blob_overmind/overmind_datum = mind?.has_antag_datum(/datum/antagonist/blob_overmind) + if(overmind_datum) + overmind_datum.strain = blobstrain + blobstrain.on_gain() + + if(had_strain && !first_select) + var/list/messages = get_strain_info() + to_chat(src, chat_box_red(messages.Join("
"))) + SEND_SIGNAL(src, COMSIG_BLOB_SELECTED_STRAIN, blobstrain) + + +/mob/camera/blob/proc/get_strain_info() + . = list() + . += span_notice("Ваш штамм: [blobstrain.name]!") + . += span_notice("Штамм [blobstrain.name] [blobstrain.description]") + if(blobstrain.effectdesc) + . += span_notice("Штамм [blobstrain.name] [blobstrain.effectdesc]") + return . + +/mob/camera/blob/examine(mob/user) + . = ..() + if(blobstrain) + . += "Штамм блоба — [blobstrain.name]." + +/mob/camera/blob/blob_act(obj/structure/blob/B) + return + +/// Create a blob spore and link it to us +/mob/camera/blob/proc/create_spore(turf/spore_turf, spore_type = /mob/living/simple_animal/hostile/blob_minion/spore/minion) + var/mob/living/simple_animal/hostile/blob_minion/spore/spore = new spore_type(spore_turf) + assume_direct_control(spore) + return spore + +/// Give our new minion the properties of a minion +/mob/camera/blob/proc/assume_direct_control(mob/living/minion) + minion.AddComponent(/datum/component/blob_minion, src) + +/// Add something to our list of mobs and wait for it to die +/mob/camera/blob/proc/register_new_minion(mob/living/minion) + blob_mobs |= minion + if(!istype(minion, /mob/living/simple_animal/hostile/blob_minion/blobbernaut)) + RegisterSignal(minion, COMSIG_LIVING_DEATH, PROC_REF(on_minion_death)) + +/// When a spore (or zombie) dies then we do this +/mob/camera/blob/proc/on_minion_death(mob/living/spore) + SIGNAL_HANDLER + blobstrain.on_sporedeath(spore) + +/mob/camera/blob/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE) + ..() + update_z(new_turf?.z) diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm new file mode 100644 index 00000000000..fe766cd2244 --- /dev/null +++ b/code/modules/antagonists/blob/powers.dm @@ -0,0 +1,417 @@ +#define BLOB_REROLL_RADIUS 60 + +/mob/camera/blob/proc/blob_help() + var/list/messages = get_blob_help_messages(blobstrain) + to_chat(src, chat_box_regular(messages.Join("
"))) + +/** Simple price check */ +/mob/camera/blob/proc/can_buy(cost = 15) + if(is_infinity || SSticker?.mode?.is_blob_infinity_points) + return TRUE + if(blob_points < cost) + to_chat(src, span_warning("Вам не хватает рескрсов, вам нужно как минимум [cost]!")) + balloon_alert(src, "нужно еще [cost-blob_points]!") + return FALSE + add_points(-cost) + return TRUE + + +/** Moves the core elsewhere. */ +/mob/camera/blob/proc/transport_core() + if(blob_core) + forceMove(blob_core.drop_location()) + +/** Jumps to a node */ +/mob/camera/blob/proc/jump_to_node() + if(!length(GLOB.blob_nodes)) + return FALSE + + var/list/nodes = list() + for(var/index in 1 to length(GLOB.blob_nodes)) + var/obj/structure/blob/special/node/blob = GLOB.blob_nodes[index] + nodes["Узел #[index] ([get_area_name(blob)])"] = blob + + var/node_name = tgui_input_list(src, "Выберите узел для перемещения", "Выбор узла", nodes) + if(isnull(node_name) || isnull(nodes[node_name])) + return FALSE + + var/obj/structure/blob/special/node/chosen_node = nodes[node_name] + if(chosen_node) + forceMove(chosen_node.loc) + + +/** Places important blob structures */ +/mob/camera/blob/proc/create_special(price, blobstrain, min_separation, needs_node, turf/tile) + if(!tile) + tile = get_turf(src) + + var/obj/structure/blob/blob = (locate(/obj/structure/blob) in tile) + if(!blob) + to_chat(src, span_warning("Тут нет плитки!")) + balloon_alert(src, "тут нет плитки!") + return FALSE + + if(!istype(blob, /obj/structure/blob/normal)) + to_chat(src, span_warning("Невозможно использовать на этой плитке. Найдите обычную плитку.")) + balloon_alert(src, "нужна обычная плитка!") + return FALSE + + var/area/area = get_area(src) + if(!(area.area_flags & BLOBS_ALLOWED)) //factory and resource blobs must be legit + to_chat(src, span_warning("Эта плитка должна быть размещена на станции!")) + balloon_alert(src, "нельзя поставить вне станции!") + return FALSE + + if(needs_node) + if(nodes_required && node_check(tile)) + to_chat(src, span_warning("Вам нужно разместить эту плитку ближе к узлу или ядру!")) + balloon_alert(src, "слишком далеко от узла или ядра!") + return FALSE //handholdotron 2000 + + if(min_separation) + for(var/obj/structure/blob/other_blob in get_sep_tile(tile, min_separation)) + if(other_blob.type == blobstrain) + to_chat(src, span_warning("Поблизости находится ресурсная плитка, отойдите на расстояние более [min_separation] плиток от неё!")) + other_blob.balloon_alert(src, "слишком близко!") + return FALSE + + if(!can_buy(price)) + return FALSE + + var/obj/structure/blob/node = blob.change_to(blobstrain, src) + return node + + +/mob/camera/blob/proc/node_check(turf/tile) + if(is_there_multiz()) + return !(locate(/obj/structure/blob/special/node) in urange_multiz(BLOB_NODE_PULSE_RANGE, tile, TRUE)) && !(locate(/obj/structure/blob/special/core) in urange_multiz(BLOB_CORE_PULSE_RANGE, tile, TRUE)) + return !(locate(/obj/structure/blob/special/node) in orange(BLOB_NODE_PULSE_RANGE, tile)) && !(locate(/obj/structure/blob/special/core) in orange(BLOB_CORE_PULSE_RANGE, tile)) + +/mob/camera/blob/proc/get_sep_tile(turf/tile, min_separation) + if(is_there_multiz()) + return urange_multiz(min_separation, tile, TRUE) + return orange(min_separation, tile) + +/** Creates a shield to reflect projectiles */ +/mob/camera/blob/proc/create_shield(turf/tile) + var/obj/structure/blob/blob = (locate(/obj/structure/blob) in tile) + if(!blob) + to_chat(src, span_warning("Тут нет плитки!")) + balloon_alert(src, "тут нет плитки!") + return FALSE + + if(istype(blob, /obj/structure/blob/special)) + to_chat(src, span_warning("Невозможно использовать на этой плитке. Найдите обычную плитку.")) + balloon_alert(src, "нужна обычная плитка!") + return FALSE + + var/obj/structure/blob/shield/shield = blob + if(!istype(shield) && can_buy(BLOB_UPGRADE_STRONG_COST)) + shield = shield.change_to(/obj/structure/blob/shield, src) + shield?.balloon_alert(src, "улучшено в [shield.name]!") + return FALSE + + if(istype(shield, /obj/structure/blob/shield/reflective)) + to_chat(src, span_warning("Невозможно использовать на этой плитке. Ее больше некуда улучшать.")) + balloon_alert(src, "улучшено на максимум!") + return FALSE + + if(!can_buy(BLOB_UPGRADE_REFLECTOR_COST)) + return FALSE + + if(shield.get_integrity() < shield.max_integrity * 0.5) + add_points(BLOB_UPGRADE_REFLECTOR_COST) + to_chat(src, span_warning("Эта крепкая плитка слишком повреждена, чтобы ее можно было улучшить!")) + return FALSE + + to_chat(src, span_warning("Вы выделяете отражающую слизь на крепкую плитку, позволяя ей отражать энергетические снаряды ценой снижения прочности.")) + shield = shield.change_to(/obj/structure/blob/shield/reflective, src, shield.point_return) + shield.balloon_alert(src, "улучшено в [shield.name]!") + +/** Preliminary check before polling ghosts. */ +/mob/camera/blob/proc/create_blobbernaut() + var/turf/current_turf = get_turf(src) + var/obj/structure/blob/special/factory/factory = locate(/obj/structure/blob/special/factory) in current_turf + if(!factory) + to_chat(src, span_warning("Вы должны быть на фабрике блоба!")) + balloon_alert(src, "нужна фабрика!") + return FALSE + if(factory.blobbernaut || factory.is_creating_blobbernaut) //if it already made or making a blobbernaut, it can't do it again + to_chat(src, span_warning("Эта фабрика уже создает блобернаута.")) + return FALSE + if(factory.get_integrity() < factory.max_integrity * 0.5) + to_chat(src, span_warning("Эта фабрика уже создала и поддерживает блобернаута.")) + return FALSE + if(!can_buy(BLOBMOB_BLOBBERNAUT_RESOURCE_COST)) + return FALSE + + factory.is_creating_blobbernaut = TRUE + to_chat(src, span_notice("Вы пытаетесь создать блоббернаута.")) + pick_blobbernaut_candidate(factory) + +/// Polls ghosts to get a blobbernaut candidate. +/mob/camera/blob/proc/pick_blobbernaut_candidate(obj/structure/blob/special/factory/factory) + if(isnull(factory)) + return + var/icon/blobbernaut_icon = icon(icon, "blobbernaut") + blobbernaut_icon.Blend(blobstrain.color, ICON_MULTIPLY) + var/image/blobbernaut_image = image(blobbernaut_icon) + var/list/candidates = SSghost_spawns.poll_candidates( + question = "Вы хотите сыграть за блобернаута?", + role = ROLE_BLOB, + poll_time = 20 SECONDS, + antag_age_check = TRUE, + check_antaghud = TRUE, + source = blobbernaut_image, + role_cleanname = "blobbernaut", + ) + if(candidates.len) + var/mob/chosen_one = pick(candidates) + on_poll_concluded(factory, chosen_one) + else + to_chat(src, span_warning("Вы не смогли создать блобернаута. Ваши ресурсы были возвращены. Повторите попытку позже.")) + add_points(BLOBMOB_BLOBBERNAUT_RESOURCE_COST) + factory.assign_blobbernaut(null) + +/// Called when the ghost poll concludes +/mob/camera/blob/proc/on_poll_concluded(obj/structure/blob/special/factory/factory, mob/dead/observer/ghost) + var/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/blobber = new(get_turf(factory)) + assume_direct_control(blobber) + factory.assign_blobbernaut(blobber) + blobber.assign_key(ghost.key, blobstrain) + RegisterSignal(blobber, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(on_blobbernaut_attacked)) + +/// When one of our boys attacked something, we sometimes want to perform extra effects +/mob/camera/blob/proc/on_blobbernaut_attacked(mob/living/simple_animal/hostile/blobbynaut, atom/target, success) + SIGNAL_HANDLER + if(!success) + return + if(!QDELETED(src)) + blobstrain.blobbernaut_attack(target, blobbynaut) + +/** Moves the core */ +/mob/camera/blob/proc/relocate_core() + var/turf/tile = get_turf(src) + var/obj/structure/blob/special/node/blob = locate(/obj/structure/blob/special/node) in tile + + if(!blob) + to_chat(src, span_warning("Вы должны быть на узле!")) + balloon_alert(src, "нужно быть на узле!") + return FALSE + + if(!blob_core) + to_chat(src, span_userdanger("У вас нет ядра и вы на пороге смерти. Покойтесь с миром!")) + balloon_alert(src, "у вас нет ядра!") + return FALSE + + var/area/area = get_area(tile) + if(isspaceturf(tile) || area && !(area.area_flags & BLOBS_ALLOWED)) + to_chat(src, span_warning("Вы не можете переместить свое ядро сюда!")) + balloon_alert(src, "нельзя переместить сюда!") + return FALSE + + if(!can_buy(BLOB_POWER_RELOCATE_COST)) + return FALSE + + var/turf/old_turf = get_turf(blob_core) + var/old_dir = blob_core.dir + blob_core.forceMove(tile) + blob_core.setDir(blob.dir) + blob.forceMove(old_turf) + blob.setDir(old_dir) + +/** Searches the tile for a blob and removes it. */ +/mob/camera/blob/proc/remove_blob(turf/tile, atom/location) + var/obj/structure/blob/blob = locate() in tile + + if(!blob) + to_chat(src, span_warning("Тут нет плитки блоба!")) + return FALSE + + if(blob.point_return < 0) + to_chat(src, span_warning("Невозможно удалить эту плитку.")) + return FALSE + + if(max_blob_points < blob.point_return + blob_points) + to_chat(src, span_warning("У вас слишком много ресурсов для удаления этой плитки!")) + return FALSE + + if(blob.point_return) + add_points(blob.point_return) + to_chat(src, span_notice("Получено [blob.point_return] за удаление [blob].")) + blob.balloon_alert(src, "+[blob.point_return]") + + qdel(blob) + + return TRUE + +/** Expands to nearby tiles */ +/mob/camera/blob/proc/expand_blob(turf/tile, atom/location) + if(world.time < last_attack) + return FALSE + var/list/possible_blobs = list() + var/turf/T + + if(is_there_multiz()) + T = get_turf(location) + for(var/obj/structure/blob/blob in urange_multiz(1, T)) + possible_blobs += blob + else + T = tile + for(var/obj/structure/blob/blob in range(1, T)) + possible_blobs += blob + + if(!length(possible_blobs)) + to_chat(src, span_warning("Рядом с целью нету плиток блоба!")) + return FALSE + + if(!can_buy(BLOB_EXPAND_COST)) + return FALSE + + var/attack_success + for(var/mob/living/player in T) + if(!player.can_blob_attack()) + continue + if(ROLE_BLOB in player.faction) //no friendly/dead fire + continue + if(player.stat != DEAD) + attack_success = TRUE + blobstrain.attack_living(player, possible_blobs) + + var/obj/structure/blob/blob = locate() in T + + if(blob) + if(attack_success) //if we successfully attacked a turf with a blob on it, only give an attack refund + blob.blob_attack_animation(T, src) + add_points(BLOB_ATTACK_REFUND) + else + to_chat(src, span_warning("Здесь уже есть плитка!")) + add_points(BLOB_EXPAND_COST) //otherwise, refund all of the cost + else + directional_attack(T, possible_blobs, attack_success) + + if(attack_success) + last_attack = world.time + CLICK_CD_MELEE + else + last_attack = world.time + CLICK_CD_RAPID + + +/** Finds cardinal and diagonal attack directions */ +/mob/camera/blob/proc/directional_attack(turf/tile, list/possible_blobs, attack_success = FALSE) + var/list/cardinal_blobs = list() + var/list/diagonal_blobs = list() + + for(var/obj/structure/blob/blob in possible_blobs) + if(get_dir_multiz(blob, tile) in GLOB.cardinals_multiz) + cardinal_blobs += blob + else + diagonal_blobs += blob + + var/obj/structure/blob/attacker + if(length(cardinal_blobs)) + attacker = pick(cardinal_blobs) + if(!attacker.expand(tile, src)) + add_points(BLOB_ATTACK_REFUND) //assume it's attacked SOMETHING, possibly a structure + else + attacker = pick(diagonal_blobs) + if(attack_success) + attacker.blob_attack_animation(tile, src) + playsound(attacker, 'sound/effects/splat.ogg', 50, TRUE) + add_points(BLOB_ATTACK_REFUND) + else + add_points(BLOB_EXPAND_COST) //if we're attacking diagonally and didn't hit anything, refund + return TRUE + +/** Rally spores to a location */ +/mob/camera/blob/proc/rally_spores(turf/tile) + to_chat(src, "Вы направляете свои споры.") + var/list/surrounding_turfs = TURF_NEIGHBORS(tile) + if(!length(surrounding_turfs)) + return FALSE + for(var/mob/living/simple_animal/hostile/blob_mob as anything in blob_mobs) + if(!isturf(blob_mob.loc) || get_dist(blob_mob, tile) > 35 || blob_mob.key) + continue + blob_mob.LoseTarget() + blob_mob.Goto(pick(surrounding_turfs), blob_mob.move_to_delay) + + +/mob/camera/blob/proc/split_consciousness() + var/turf/T = get_turf(src) + if(!T) + return + var/area/Ablob = get_area(T) + if(isspaceturf(T) || Ablob && !(Ablob.area_flags & BLOBS_ALLOWED)) + to_chat(src, span_warning("Вы не можете поделиться вне станции!")) + balloon_alert(src, "нельзя поделиться вне станции!") + return FALSE + if(split_used) + to_chat(src, span_warning("Вы уже произвели потомка.")) + balloon_alert(src, "вы уже поделились!") + return + if(is_offspring) + to_chat(src, span_warning("Потомки блоба не могут производить потомков.")) + balloon_alert(src, "вы сами потомок блоба!") + return + + var/obj/structure/blob/N = (locate(/obj/structure/blob) in T) + if(N && !istype(N, /obj/structure/blob/special/node)) + to_chat(src, span_warning("Для создания вашего потомка необходим узел.")) + balloon_alert(src, "необходим узел!") + return + + if(!can_buy(BLOB_CORE_SPLIT_COST)) + return + + split_used = TRUE + new /obj/structure/blob/special/core/ (get_turf(N), null, TRUE) + qdel(N) + + +/** Opens the reroll menu to change strains */ +/mob/camera/blob/proc/strain_reroll() + if(!free_strain_rerolls && blob_points < BLOB_POWER_REROLL_COST) + to_chat(src, span_warning("Вам нужно как минимум [BLOB_POWER_REROLL_COST], чтобы снова изменить свой штамм!")) + return FALSE + + open_reroll_menu() + +/** Controls changing strains */ +/mob/camera/blob/proc/open_reroll_menu() + if(!strain_choices) + strain_choices = list() + + var/list/new_strains = GLOB.valid_blobstrains.Copy() - blobstrain.type + for (var/unused in 1 to BLOB_POWER_REROLL_CHOICES) + var/datum/blobstrain/strain = pick_n_take(new_strains) + + var/image/strain_icon = image('icons/mob/blob.dmi', "blob_core") + strain_icon.color = initial(strain.color) + + //var/info_text = span_boldnotice("[initial(strain.name)]") + //info_text += "
[span_notice("[initial(strain.analyzerdescdamage)]")]" + //if(!isnull(initial(strain.analyzerdesceffect))) + //info_text += "
[span_notice("[initial(strain.analyzerdesceffect)]")]" + + strain_choices[initial(strain.name)] = strain_icon + + var/strain_result = show_radial_menu(src, src, strain_choices, radius = BLOB_REROLL_RADIUS) + if(isnull(strain_result)) + return + + if(!free_strain_rerolls && !can_buy(BLOB_POWER_REROLL_COST)) + return + + for (var/_other_strain in GLOB.valid_blobstrains) + var/datum/blobstrain/other_strain = _other_strain + if(initial(other_strain.name) == strain_result) + set_strain(other_strain) + + if(free_strain_rerolls) + free_strain_rerolls -= 1 + + last_reroll_time = world.time + strain_choices = null + + return + +#undef BLOB_REROLL_RADIUS diff --git a/code/modules/antagonists/blob/powers_verbs.dm b/code/modules/antagonists/blob/powers_verbs.dm new file mode 100644 index 00000000000..e118e727a28 --- /dev/null +++ b/code/modules/antagonists/blob/powers_verbs.dm @@ -0,0 +1,29 @@ +/** Toggles requiring nodes */ +/mob/camera/blob/verb/toggle_node_req() + set category = "Blob" + set name = "Переключить требование узла" + set desc = "Переключить требование узла для размещения ресурсной плитки и фабрики." + + nodes_required = !nodes_required + if(nodes_required) + to_chat(src, span_warning("Теперь вам необходимо иметь узел или ядро рядом ​​для размещения фабрики и ресурсной плитки.")) + else + to_chat(src, span_warning("Теперь вам не нужно иметь узел или ядро рядом ​​для размещения фабрики и ресурсной плитки.")) + + +/mob/camera/blob/verb/blob_broadcast() + set category = "Blob" + set name = "Ретрянсляция блоба" + set desc = "Говорите, используя споры и блобернаутов в качестве рупоров. Это действие бесплатно." + + var/speak_text = tgui_input_text(usr, "Что вы хотите сказать от лица ваших созданий?", "Ретрянсляция блоба", null) + + if(!speak_text) + return + else + to_chat(usr, "Вы говорите от лица ваших созданий, [speak_text]") + for(var/mob/living/simple_animal/hostile/blob_minion in blob_mobs) + if(blob_minion.stat == CONSCIOUS) + add_say_logs(usr, speak_text, language = "BLOB Broadcast") + blob_minion.atom_say(speak_text) + return diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm new file mode 100644 index 00000000000..44b624f673e --- /dev/null +++ b/code/modules/antagonists/blob/structures/_blob.dm @@ -0,0 +1,429 @@ +//I will need to recode parts of this but I am way too tired atm +/obj/structure/blob + name = "blob" + icon = 'icons/mob/blob.dmi' + light_range = 3 + desc = "Толстая стена извивающихся щупалец." + density = FALSE + opacity = TRUE + anchored = TRUE + pass_flags_self = PASSBLOB + layer = BELOW_MOB_LAYER + can_astar_pass = CANASTARPASS_ALWAYS_PROC + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + creates_cover = TRUE + obj_flags = BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP // stops blob mobs from falling on multiz. + max_integrity = BLOB_REGULAR_MAX_HP + /// Multiplies brute damage by this + var/brute_resist = BLOB_BRUTE_RESIST + /// Multiplies burn damage by this + var/fire_resist = BLOB_FIRE_RESIST + /// how much health this blob regens when pulsed + var/health_regen = BLOB_REGULAR_HP_REGEN + /// How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/point_return = 0 + /// If a threshold is reached, resulting in shifting variables + var/compromised_integrity = FALSE + /// Blob overmind + var/mob/camera/blob/overmind + /// We got pulsed when? + COOLDOWN_DECLARE(pulse_timestamp) + /// we got healed when? + COOLDOWN_DECLARE(heal_timestamp) + /// Only used by the synchronous mesh strain. If set to true, these blobs won't share or receive damage taken with others. + var/ignore_syncmesh_share = FALSE + /// If the blob blocks atmos and heat spread + var/atmosblock = FALSE + +/obj/structure/blob/ComponentInitialize() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + + +/obj/structure/blob/Initialize(mapload, owner_overmind) + . = ..() + ADD_TRAIT(src, TRAIT_CHASM_DESTROYED, INNATE_TRAIT) + GLOB.blobs |= src + if(owner_overmind && isovermind(owner_overmind)) + link_to_overmind(owner_overmind) + setDir(pick(GLOB.cardinal)) + if(atmosblock) + air_update_turf(TRUE) + ConsumeTile() + update_blob() + + +/obj/structure/blob/proc/link_to_overmind(mob/camera/blob/owner_overmind) + overmind = owner_overmind + overmind.all_blobs |= src + overmind.blobs_legit |= src + + +/obj/structure/blob/Destroy() + if(atmosblock) + atmosblock = FALSE + air_update_turf(1) + GLOB.blobs -= src + SSticker?.mode?.legit_blobs -= src + if(overmind) + overmind.all_blobs -= src + overmind.blobs_legit -= src //if it was in the legit blobs list, it isn't now + overmind = null + if(isturf(loc)) //Necessary because Expand() is screwed up and spawns a blob and then deletes it + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + return ..() + +/obj/structure/blob/obj_destruction(damage_flag) + if(overmind) + overmind.blobstrain.death_reaction(src, damage_flag) + . = ..() + +/obj/structure/blob/Adjacent(atom/neighbour) + . = ..() + if(.) + var/result = 0 + var/direction = get_dir(src, neighbour) + var/list/dirs = list("[NORTHWEST]" = list(NORTH, WEST), "[NORTHEAST]" = list(NORTH, EAST), "[SOUTHEAST]" = list(SOUTH, EAST), "[SOUTHWEST]" = list(SOUTH, WEST)) + for(var/A in dirs) + if(direction == text2num(A)) + for(var/B in dirs[A]) + var/C = locate(/obj/structure/blob) in get_step(src, B) + if(C) + result++ + . -= result - 1 + + +/obj/structure/blob/BlockSuperconductivity() + return atmosblock + + +/obj/structure/blob/CanAtmosPass(turf/T, vertical) + return !atmosblock + + +/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind. + . = ..() + if(overmind) + add_atom_colour(overmind.blobstrain.color, FIXED_COLOUR_PRIORITY) + var/area/A = get_area(src) + if(!(A.area_flags & BLOBS_ALLOWED)) + add_atom_colour(BlendRGB(overmind.blobstrain.color, COLOR_WHITE, 0.5), FIXED_COLOUR_PRIORITY) //lighten it to indicate an off-station blob + else + remove_atom_colour(FIXED_COLOUR_PRIORITY) + + +/obj/structure/blob/proc/Be_Pulsed() + if(COOLDOWN_FINISHED(src, pulse_timestamp)) + ConsumeTile() + if(COOLDOWN_FINISHED(src, heal_timestamp)) + RegenHealth() + COOLDOWN_START(src, heal_timestamp, 20) + update_blob() + COOLDOWN_START(src, pulse_timestamp, 10) + return TRUE//we did it, we were pulsed! + return FALSE //oh no we failed + + +/obj/structure/blob/proc/RegenHealth() + obj_integrity = min(max_integrity, obj_integrity + health_regen) + update_blob() + + +/obj/structure/blob/proc/ConsumeTile() + for(var/atom/thing in loc) + if(!thing.can_blob_attack()) + continue + if(isliving(thing) && overmind && !HAS_TRAIT(thing, TRAIT_BLOB_ALLY)) // Make sure to inject strain-reagents with automatic attacks when needed. + overmind.blobstrain.attack_living(thing) + continue // Don't smack them twice though + thing.blob_act(src) + if(iswallturf(loc)) + loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it + + +/obj/structure/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom + var/obj/effect/temp_visual/blob/O = new /obj/effect/temp_visual/blob(src.loc) + O.setDir(dir) + var/area/my_area = get_area(src) + if(controller) + var/mob/camera/blob/BO = controller + O.color = BO.blobstrain.color + if(!(my_area.area_flags & BLOBS_ALLOWED)) + O.color = BlendRGB(O.color, COLOR_WHITE, 0.5) //lighten it to indicate an off-station blob + O.alpha = 200 + else if(overmind) + O.color = overmind.blobstrain.color + if(!(my_area.area_flags & BLOBS_ALLOWED)) + O.color = BlendRGB(O.color, COLOR_WHITE, 0.5) //lighten it to indicate an off-station blob + if(A) + O.do_attack_animation(A) //visually attack the whatever + return O //just in case you want to do something to the animation. + + +/obj/structure/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1) + if(!T) + var/list/dirs = (is_there_multiz())? GLOB.cardinals_multiz.Copy() : GLOB.cardinal.Copy() + for(var/i = 1 to dirs.len) + var/dirn = pick(dirs) + dirs.Remove(dirn) + T = get_step_multiz(src, dirn) + if(!(locate(/obj/structure/blob) in T)) + break + else + T = null + if(!T) + return + + if(!is_location_within_transition_boundaries(T)) + return + var/make_blob = TRUE //can we make a blob? + + if(isspaceturf(T) && !(locate(/obj/structure/lattice) in T)) + if(SEND_SIGNAL(T, COMSIG_TRY_CONSUME_TURF) & COMPONENT_CANT_CONSUME) + make_blob = FALSE + playsound(src.loc, 'sound/effects/splat.ogg', 50, TRUE) //Let's give some feedback that we DID try to spawn in space, since players are used to it + + ConsumeTile() //hit the tile we're in, making sure there are no border objects blocking us + if(!T.CanPass(src, get_dir(T, src))) //is the target turf impassable + if(SEND_SIGNAL(T, COMSIG_TRY_CONSUME_TURF) & COMPONENT_CANT_CONSUME) + make_blob = FALSE + T.blob_act(src) //hit the turf if it is + for(var/atom/A in T) + if(!A.CanPass(src, get_dir(T, src))) //is anything in the turf impassable + make_blob = FALSE + if(!A.can_blob_attack()) + continue + if(isliving(A) && overmind && !controller) // Make sure to inject strain-reagents with automatic attacks when needed. + var/mob/living/mob = A + if(ROLE_BLOB in mob.faction) //no friendly fire + continue + overmind.blobstrain.attack_living(mob) + continue // Don't smack them twice though + A.blob_act(src) //also hit everything in the turf + + if(make_blob) //well, can we? + var/obj/structure/blob/B = new /obj/structure/blob/normal(src.loc, (controller || overmind)) + B.set_density(TRUE) + if(T.Enter(B)) //NOW we can attempt to move into the tile + B.set_density(initial(B.density)) + B.forceMove(T) + var/offstation = FALSE + var/area/Ablob = get_area(B) + if(Ablob.area_flags & BLOBS_ALLOWED) //Is this area allowed for winning as blob? + if(overmind) + overmind.blobs_legit |= B + SSticker?.mode?.legit_blobs |= B + else if(controller) + B.balloon_alert(overmind, "вне станции, не считается!") + offstation = TRUE + B.update_blob() + var/reaction_result = TRUE + var/turf/total_turf = get_turf(src) + if(B.overmind && expand_reaction) + reaction_result = B.overmind.blobstrain.expand_reaction(src, B, T, controller, offstation) + if(reaction_result && is_there_multiz() && check_level_trait(T.z, ZTRAIT_DOWN) && T.z != total_turf.z && !isopenspaceturf(T)) + T.ChangeTurf(/turf/simulated/openspace) + if(reaction_result && is_there_multiz() && check_level_trait(total_turf.z, ZTRAIT_DOWN) && T.z != total_turf.z && !isopenspaceturf(total_turf)) + total_turf.ChangeTurf(/turf/simulated/openspace) + return B + else + blob_attack_animation(T, controller) + T.blob_act(src) //if we can't move in hit the turf again + qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile + return + else + blob_attack_animation(T, controller) //if we can't, animate that we attacked + return + + +/obj/structure/blob/CanAllowThrough(atom/movable/mover, border_dir) + . = ..() + var/mob/mover_mob = mover + return checkpass(mover, PASSBLOB) || (istype(mover_mob) && mover_mob.stat == DEAD) + + +/obj/structure/blob/CanAStarPass(to_dir, datum/can_pass_info/pass_info) + return pass_info.pass_flags == PASSEVERYTHING || (pass_info.pass_flags & PASSBLOB) + + +/obj/structure/blob/emp_act(severity) + . = ..() + // tgstation emp protection + //if(. & EMP_PROTECT_SELF) + //return + if(severity > 0) + if(overmind) + overmind.blobstrain.emp_reaction(src, severity) + if(prob(100 - severity * 30)) + new /obj/effect/temp_visual/emp(get_turf(src)) + + +/obj/structure/blob/tesla_act(power) + if(overmind) + if(overmind.blobstrain.tesla_reaction(src, power)) + take_damage(power * 1.25e-3, BURN, ENERGY) + else + take_damage(power * 1.25e-3, BURN, ENERGY) + power -= power * 2.5e-3 //You don't get to do it for free + return ..() //You don't get to do it for free + + +/obj/structure/blob/blob_act(obj/structure/blob/B) + return + + +/obj/structure/blob/extinguish() + . = ..() + if(overmind) + overmind.blobstrain.extinguish_reaction(src) + + +/obj/structure/blob/hit_by_thrown_carbon(mob/living/carbon/human/C, datum/thrownthing/throwingdatum, damage, mob_hurt, self_hurt) + damage *= 0.25 // Lets not have sorium be too much of a blender / rapidly kill itself + return ..() + + +/obj/structure/blob/attack_animal(mob/living/simple_animal/M) + if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + to_chat(M, span_danger("Вы не можете навредить структурам блоба")) + return + ..() + + +/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = NONE) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + + +/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + switch(damage_type) + if(BRUTE) + damage_amount *= brute_resist + if(BURN) + damage_amount *= fire_resist + else + return 0 + var/armor_protection = 0 + if(damage_flag) + armor_protection = armor.getRating(damage_flag) + damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) + if(overmind && damage_flag) + damage_amount = overmind.blobstrain.damage_reaction(src, damage_amount, damage_type, damage_flag) + return damage_amount + + +/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + if(QDELETED(src)) + return + . = ..() + if(. && obj_integrity > 0) + update_blob() + + +/obj/structure/blob/has_prints() + return FALSE + +/obj/structure/blob/proc/update_state() + return + +/obj/structure/blob/proc/update_blob() + update_state() + update_appearance() + +/obj/structure/blob/proc/Life() + return + +/obj/structure/blob/proc/run_action() + return FALSE + +/obj/structure/blob/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) + SIGNAL_HANDLER + + arrived.blob_act(src) + + +/obj/structure/blob/proc/change_to(type, controller, point_return = 0) + if(!ispath(type)) + CRASH("change_to(): invalid type for blob") + var/obj/structure/blob/B = new type(src.loc, controller) + B.update_blob() + B.setDir(dir) + B.point_return += point_return + qdel(src) + return B + + +/obj/structure/blob/attackby(obj/item/I, mob/user, params) + if(I.tool_behaviour == TOOL_ANALYZER) + user.changeNext_move(CLICK_CD_MELEE) + to_chat(user, "Анализатор подает один звуковой сигнал, затем сообщает:
") + SEND_SOUND(user, sound('sound/machines/ping.ogg')) + if(overmind) + to_chat(user, "Прогресс Критической Массы: [span_notice("[TOTAL_BLOB_MASS]/[NEEDED_BLOB_MASS].")]") + to_chat(user, chemeffectreport(user).Join("\n")) + else + to_chat(user, "Ядро блоба нейтрализовано. Критическая масса более не достижима.") + to_chat(user, typereport(user).Join("\n")) + return ATTACK_CHAIN_PROCEED_SUCCESS + else + return ..() + + +/obj/structure/blob/examine(mob/user) + . = ..() + var/datum/atom_hud/hud_to_check = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + if(user.research_scanner || hud_to_check.hudusers[user]) + . += "Ваш HUD отображает обширный отчет...
" + if(overmind) + . += overmind.blobstrain.examine(user) + else + . += "Ядро блоба нейтрализовано. Критическая масса более не достижима." + . += chemeffectreport(user) + . += typereport(user) + else + if((user == overmind || isobserver(user)) && overmind) + . += overmind.blobstrain.examine(user) + . += "Кажется, он состоит из [get_chem_name()]." + + +/obj/structure/blob/proc/scannerreport() + return "Обычная плитка. Похоже, кто-то забыл переопределить этот процесс, сообщите администратору и составьте баг-репорт." + + + +/obj/structure/blob/proc/chemeffectreport(mob/user) + RETURN_TYPE(/list) + . = list() + if(overmind) + . += list("Материал: [overmind.blobstrain.name][span_notice(".")]", + "Эффект материала: [span_notice("[overmind.blobstrain.analyzerdescdamage]")]", + "Свойства материала: [span_notice("[overmind.blobstrain.analyzerdesceffect || "N/A"]")]") + else + . += "Материал не найден!" + +/obj/structure/blob/proc/typereport(mob/user) + RETURN_TYPE(/list) + return list("Тип плитки: [span_notice("[uppertext(initial(name))]")]", + "Здоровье: [span_notice("[obj_integrity]/[max_integrity]")]", + "Эффекты: [span_notice("[scannerreport()]")]") + + +/obj/structure/blob/proc/get_chem_name() + if(overmind) + return overmind.blobstrain.name + return "какая-то органическая материя" + + +/obj/structure/blob/proc/get_chem_desc() + if(overmind) + return overmind.blobstrain.description + return "что-то неизвестное" + diff --git a/code/modules/antagonists/blob/structures/captured_nuke.dm b/code/modules/antagonists/blob/structures/captured_nuke.dm new file mode 100644 index 00000000000..a723d0dd0dd --- /dev/null +++ b/code/modules/antagonists/blob/structures/captured_nuke.dm @@ -0,0 +1,31 @@ +/obj/structure/blob/special/captured_nuke //alternative to blob just straight up destroying nukes + name = "blob captured nuke" + icon_state = "blob" + desc = "Ядерная боеголовка спуталась в щупальцах блоба, пульсирующих ужасающим зеленым свечением." + max_integrity = BLOB_CAP_NUKE_MAX_HP + health_regen = BLOB_CAP_NUKE_HP_REGEN + point_return = BLOB_REFUND_CAP_NUKE_COST + +/obj/structure/blob/special/captured_nuke/Initialize(mapload, owner_overmind, obj/machinery/nuclearbomb/N) + . = ..() + START_PROCESSING(SSobj, src) + N?.forceMove(src) + update_icon(UPDATE_OVERLAYS) + + +/obj/structure/blob/special/captured_nuke/update_overlays() + . = ..() + . += mutable_appearance('icons/mob/blob.dmi', "blob_nuke_overlay", appearance_flags = RESET_COLOR) + + +/obj/structure/blob/special/captured_nuke/Destroy() + for(var/obj/machinery/nuclearbomb/O in contents) + O.forceMove(loc) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/structure/blob/special/captured_nuke/process() + if(COOLDOWN_FINISHED(src, heal_timestamp)) + RegenHealth() + COOLDOWN_START(src, heal_timestamp, 20) + diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm new file mode 100644 index 00000000000..c34a8746a97 --- /dev/null +++ b/code/modules/antagonists/blob/structures/core.dm @@ -0,0 +1,167 @@ +/obj/structure/blob/special/core + name = "blob core" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + desc = "Огромная пульсирующая желтая масса." + max_integrity = BLOB_CORE_MAX_HP + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 90) + explosion_block = 6 + explosion_vertical_block = 5 + point_return = BLOB_REFUND_CORE_COST + fire_resist = BLOB_CORE_FIRE_RESIST + health_regen = BLOB_CORE_HP_REGEN + resistance_flags = LAVA_PROOF + strong_reinforce_range = BLOB_CORE_STRONG_REINFORCE_RANGE + reflector_reinforce_range = BLOB_CORE_REFLECTOR_REINFORCE_RANGE + claim_range = BLOB_CORE_CLAIM_RANGE + pulse_range = BLOB_CORE_PULSE_RANGE + expand_range = BLOB_CORE_EXPAND_RANGE + ignore_syncmesh_share = TRUE + COOLDOWN + var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes + var/is_offspring = null + var/selecting = 0 + + +/obj/structure/blob/special/core/ComponentInitialize() + . = ..() + AddComponent(/datum/component/stationloving, FALSE, TRUE) + + +/obj/structure/blob/special/core/Initialize(mapload, client/new_overmind = null, offspring) + GLOB.blob_cores += src + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + update_blob() //so it atleast appears + if(!overmind) + create_overmind(new_overmind) + is_offspring = offspring + if(overmind) + overmind.blobstrain.on_gain() + update_blob() + return ..() + + +/obj/structure/blob/special/core/Destroy() + GLOB.blob_cores -= src + if(overmind) + overmind.blob_core = null + overmind = null + SSticker?.mode?.blob_died() + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + for(var/atom/movable/atom as anything in contents) + if(atom && !QDELETED(atom) && istype(atom)) + atom.forceMove(get_turf(src)) + atom.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), 6, 5, src, TRUE, FALSE, null, 3) + return ..() + +/obj/structure/blob/special/core/scannerreport() + return "Управляет расширением блоба, постепенно расширяется и поддерживает близлежащие споры и блобернаутов." + +/obj/structure/blob/special/core/update_overlays() + . = ..() + var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob") + if(overmind) + blob_overlay.color = overmind.blobstrain.color + . += blob_overlay + . += mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay") + if(blocks_emissive) + add_overlay(get_emissive_block()) + +/obj/structure/blob/special/core/update_icon() + . = ..() + color = null + +/obj/structure/blob/special/core/ex_act(severity, target) + var/damage = 10 * (severity + 1) //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity + take_damage(damage, BRUTE, BOMB, 0) + return TRUE + + +/obj/structure/blob/special/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) + . = ..() + if(obj_integrity > 0) + if(overmind) //we should have an overmind, but... + overmind.update_health_hud() + +/obj/structure/blob/special/core/RegenHealth() + return // Don't regen, we handle it in Life() + +/obj/structure/blob/special/core/process(seconds_per_tick) + if(QDELETED(src)) + return + if(!overmind) + create_overmind() + if(overmind) + overmind.blobstrain.core_process() + overmind.update_health_hud() + pulse_area(overmind, claim_range, pulse_range, expand_range) + reinforce_area(seconds_per_tick) + ..() + + +/obj/structure/blob/special/core/proc/create_overmind(client/new_overmind, override_delay) + if(overmind_get_delay > world.time && !override_delay) + return + + overmind_get_delay = world.time + 5 MINUTES + + if(overmind && new_overmind) + qdel(overmind) + if(new_overmind) + get_new_overmind(new_overmind) + else + INVOKE_ASYNC(src, PROC_REF(get_new_overmind)) + + +/obj/structure/blob/special/core/proc/get_new_overmind(client/new_overmind) + var/mob/C = null + var/list/candidates = list() + if(!new_overmind) + // sendit + if(is_offspring) + candidates = SSghost_spawns.poll_candidates("Вы хотите поиграть за потомка блоба?", ROLE_BLOB, TRUE, source = src) + else + candidates = SSghost_spawns.poll_candidates("Вы хотите поиграть за блоба?", ROLE_BLOB, TRUE, source = src) + + if(length(candidates)) + C = pick(candidates) + else + C = new_overmind + + if(C && !QDELETED(src)) + var/mob/camera/blob/B = new(loc, src) + B.blob_core = src + B.mind_initialize() + B.key = C.key + overmind = B + B.is_offspring = is_offspring + addtimer(CALLBACK(src, PROC_REF(add_datum_if_not_exist)), TIME_TO_ADD_OM_DATUM) + log_game("[B.key] has become Blob [is_offspring ? "offspring" : ""]") + + +/obj/structure/blob/special/core/proc/add_datum_if_not_exist() + if(!overmind.mind.has_antag_datum(/datum/antagonist/blob_overmind)) + var/datum/antagonist/blob_overmind/overmind_datum = new + overmind_datum.add_to_mode = TRUE + overmind_datum.is_offspring = is_offspring + if(overmind.blobstrain) + overmind_datum.strain = overmind.blobstrain + overmind.mind.add_antag_datum(overmind_datum) + +/obj/structure/blob/special/core/proc/lateblobtimer() + addtimer(CALLBACK(src, PROC_REF(lateblobcheck)), 50) + +/obj/structure/blob/special/core/proc/lateblobcheck() + if(overmind) + overmind.add_points(BLOB_BONUS_POINTS) + if(!overmind.mind) + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") + +/obj/structure/blob/special/core/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer) + if(overmind && is_station_level(new_turf?.z)) + overmind.forceMove(get_turf(src)) + return ..() diff --git a/code/modules/antagonists/blob/structures/factory.dm b/code/modules/antagonists/blob/structures/factory.dm new file mode 100644 index 00000000000..c8d0417aed4 --- /dev/null +++ b/code/modules/antagonists/blob/structures/factory.dm @@ -0,0 +1,105 @@ +/obj/structure/blob/special/factory + name = "factory blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_factory" + desc = "Толстый шпиль щупалец." + max_integrity = BLOB_FACTORY_MAX_HP + health_regen = BLOB_FACTORY_HP_REGEN + point_return = BLOB_REFUND_FACTORY_COST + resistance_flags = LAVA_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 25, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + ///How many spores this factory can have. + var/max_spores = BLOB_FACTORY_MAX_SPORES + ///The list of spores and zombies + var/list/spores_and_zombies = list() + COOLDOWN_DECLARE(spore_delay) + var/spore_cooldown = BLOBMOB_SPORE_SPAWN_COOLDOWN + ///Its Blobbernaut, if it has spawned any. + var/mob/living/simple_animal/hostile/blob_minion/blobbernaut/minion/blobbernaut + ///Used in blob/powers.dm, checks if it's already trying to spawn a blobbernaut to prevent issues. + var/is_creating_blobbernaut = FALSE + + +/obj/structure/blob/special/factory/scannerreport() + if(blobbernaut) + return "В настоящее время он поддерживает блобернаута, что делает ее хрупкой и неспособной производить споры." + return "Каждые несколько секунд производит споры." + +/obj/structure/blob/special/factory/link_to_overmind(mob/camera/blob/owner_overmind) + . = ..() + owner_overmind.factory_blobs |= src + if(!owner_overmind.blobstrain) + return . + for(var/mob in spores_and_zombies) + owner_overmind.assume_direct_control(mob) + if(blobbernaut) + owner_overmind.assume_direct_control(blobbernaut) + +/obj/structure/blob/special/factory/Destroy() + spores_and_zombies = null + blobbernaut = null + if(overmind) + overmind.factory_blobs -= src + return ..() + +/obj/structure/blob/special/factory/Be_Pulsed() + . = ..() + if(blobbernaut) + return + if(!overmind) + return + if(length(spores_and_zombies) >= max_spores) + return + if(!COOLDOWN_FINISHED(src, spore_delay)) + return + COOLDOWN_START(src, spore_delay, spore_cooldown) + flick("blob_factory_glow", src) + var/mob/living/simple_animal/hostile/blob_minion/created_spore = (overmind) ? overmind.create_spore(loc) : new(loc) + register_mob(created_spore) + RegisterSignal(created_spore, COMSIG_BLOB_ZOMBIFIED, PROC_REF(on_zombie_created)) + +/// Tracks the existence of a mob in our mobs list +/obj/structure/blob/special/factory/proc/register_mob(mob/living/simple_animal/hostile/blob_minion/blob_mob) + spores_and_zombies |= blob_mob + blob_mob.link_to_factory(src) + RegisterSignal(blob_mob, COMSIG_LIVING_DEATH, PROC_REF(on_spore_died)) + RegisterSignal(blob_mob, COMSIG_QDELETING, PROC_REF(on_spore_lost)) + +/// When a spore or zombie dies reset our spawn cooldown so we don't instantly replace it +/obj/structure/blob/special/factory/proc/on_spore_died(mob/living/dead_spore) + SIGNAL_HANDLER + COOLDOWN_START(src, spore_delay, spore_cooldown) + +/// When a spore is deleted remove it from our list +/obj/structure/blob/special/factory/proc/on_spore_lost(mob/living/dead_spore) + SIGNAL_HANDLER + spores_and_zombies -= dead_spore + +/// When a spore makes a zombie add it to our mobs list +/obj/structure/blob/special/factory/proc/on_zombie_created(mob/living/spore, mob/living/zombie) + SIGNAL_HANDLER + register_mob(zombie) + +/// Produce a blobbernaut +/obj/structure/blob/special/factory/proc/assign_blobbernaut(mob/living/new_naut) + is_creating_blobbernaut = FALSE + if(isnull(new_naut)) + return + + modify_max_integrity(initial(max_integrity) * 0.25) //factories that produced a blobbernaut have much lower health + visible_message(span_boldwarning("Блобернаут [pick("разрывает", "надрывает", "рвет в клочья")] все на своем пути из фабрики!")) + playsound(loc, 'sound/effects/splat.ogg', 50, TRUE) + + blobbernaut = new_naut + blobbernaut.link_to_factory(src) + RegisterSignal(new_naut, list(COMSIG_QDELETING, COMSIG_LIVING_DEATH), PROC_REF(on_blobbernaut_death)) + update_blob() + +/// When our brave soldier dies, reset our max integrity +/obj/structure/blob/special/factory/proc/on_blobbernaut_death(mob/living/death_naut) + SIGNAL_HANDLER + if(isnull(blobbernaut) || blobbernaut != death_naut) + return + blobbernaut = null + max_integrity = initial(max_integrity) + update_blob() diff --git a/code/modules/antagonists/blob/structures/node.dm b/code/modules/antagonists/blob/structures/node.dm new file mode 100644 index 00000000000..d9983f76673 --- /dev/null +++ b/code/modules/antagonists/blob/structures/node.dm @@ -0,0 +1,57 @@ +/obj/structure/blob/special/node + name = "blob node" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + desc = "Большая пульсирующая желтая масса." + max_integrity = BLOB_NODE_MAX_HP + health_regen = BLOB_NODE_HP_REGEN + armor = list("melee" = 0, "bullet" = 0, "laser" = 25, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) + point_return = BLOB_REFUND_NODE_COST + claim_range = BLOB_NODE_CLAIM_RANGE + pulse_range = BLOB_NODE_PULSE_RANGE + expand_range = BLOB_NODE_EXPAND_RANGE + resistance_flags = LAVA_PROOF + ignore_syncmesh_share = TRUE + +/obj/structure/blob/special/node/Initialize(mapload) + GLOB.blob_nodes |= src + START_PROCESSING(SSobj, src) + . = ..() + + +/obj/structure/blob/special/node/scannerreport() + return "Постепенно расширяется и поддерживает близлежащие споры и блобернаутов." + +/obj/structure/blob/special/node/update_icon() + . = ..() + color = null + +/obj/structure/blob/special/node/update_overlays() + . = ..() + var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob") + if(overmind) + blob_overlay.color = overmind.blobstrain.color + var/area/A = get_area(src) + if(!(A.area_flags & BLOBS_ALLOWED)) + blob_overlay.color = BlendRGB(overmind.blobstrain.color, COLOR_WHITE, 0.5) //lighten it to indicate an off-station blob + . += blob_overlay + . += mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay") + if(blocks_emissive) + add_overlay(get_emissive_block()) + + +/obj/structure/blob/special/node/link_to_overmind(mob/camera/blob/owner_overmind) + . = ..() + overmind.node_blobs |= src + +/obj/structure/blob/special/node/Destroy() + GLOB.blob_nodes -= src + STOP_PROCESSING(SSobj, src) + if(overmind) + overmind.node_blobs -= src + return ..() + +/obj/structure/blob/special/node/process(seconds_per_tick) + if(overmind) + pulse_area(overmind, claim_range, pulse_range, expand_range) + reinforce_area(seconds_per_tick) diff --git a/code/modules/antagonists/blob/structures/normal.dm b/code/modules/antagonists/blob/structures/normal.dm new file mode 100644 index 00000000000..2538f64825a --- /dev/null +++ b/code/modules/antagonists/blob/structures/normal.dm @@ -0,0 +1,49 @@ +/obj/structure/blob/normal + name = "normal blob" + icon_state = "blob" + light_range = 0 + max_integrity = BLOB_REGULAR_MAX_HP + var/initial_integrity = BLOB_REGULAR_HP_INIT + health_regen = BLOB_REGULAR_HP_REGEN + brute_resist = BLOB_BRUTE_RESIST * 0.5 + + +/obj/structure/blob/normal/Initialize(mapload, owner_overmind) + . = ..() + update_integrity(initial_integrity) + +/obj/structure/blob/normal/scannerreport() + if(compromised_integrity) + return "В настоящее время слаб к урону травмами." + return "N/A" + +/obj/structure/blob/normal/update_name() + . = ..() + name = "[(compromised_integrity) ? "fragile " : (overmind ? null : "dead ")][initial(name)]" + +/obj/structure/blob/normal/update_desc() + . = ..() + if(compromised_integrity) + desc = "Тонкая решетка слегка подергивающихся щупалец." + else if(overmind) + desc = "Толстая стена извивающихся щупалец." + else + desc = "Толстая стена извивающихся щупалец." + +/obj/structure/blob/normal/update_icon_state() + icon_state = "blob[(compromised_integrity) ? "_damaged" : null]" + return ..() + + +/obj/structure/blob/normal/update_state() + if(obj_integrity <= 15) + compromised_integrity = TRUE + else + compromised_integrity = FALSE + + if(compromised_integrity) + brute_resist = BLOB_BRUTE_RESIST + else if(overmind) + brute_resist = BLOB_BRUTE_RESIST * 0.5 + else + brute_resist = BLOB_BRUTE_RESIST * 0.5 diff --git a/code/modules/antagonists/blob/structures/resource.dm b/code/modules/antagonists/blob/structures/resource.dm new file mode 100644 index 00000000000..3d1e995ac1d --- /dev/null +++ b/code/modules/antagonists/blob/structures/resource.dm @@ -0,0 +1,35 @@ +/obj/structure/blob/special/resource + name = "resource blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_resource" + desc = "Тонкий шпиль слегка покачивающихся щупалец." + max_integrity = BLOB_RESOURCE_MAX_HP + point_return = BLOB_REFUND_RESOURCE_COST + resistance_flags = LAVA_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 25, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + var/resource_delay = 0 + var/point_rate = BLOB_RESOURCE_POINT_RATE + +/obj/structure/blob/special/resource/scannerreport() + return "Постепенно снабжает блоба ресурсами, увеличивая скорость расширения." + +/obj/structure/blob/special/resource/link_to_overmind(mob/camera/blob/owner_overmind) + . = ..() + overmind.resource_blobs |= src + +/obj/structure/blob/special/resource/Destroy() + if(overmind) + overmind.resource_blobs -= src + return ..() + +/obj/structure/blob/special/resource/Be_Pulsed() + . = ..() + if(resource_delay > world.time) + return + flick("blob_resource_glow", src) + if(overmind) + overmind.add_points(point_rate) + balloon_alert(overmind, "+[point_rate] resource\s") + resource_delay = world.time + BLOB_RESOURCE_GATHER_DELAY + overmind.resource_blobs.len * BLOB_RESOURCE_GATHER_ADDED_DELAY //4 seconds plus a quarter second for each resource blob the overmind has + else + resource_delay = world.time + BLOB_RESOURCE_GATHER_DELAY diff --git a/code/modules/antagonists/blob/structures/shield.dm b/code/modules/antagonists/blob/structures/shield.dm new file mode 100644 index 00000000000..a41efe51c48 --- /dev/null +++ b/code/modules/antagonists/blob/structures/shield.dm @@ -0,0 +1,68 @@ +/obj/structure/blob/shield + name = "strong blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_shield" + desc = "Сплошная стена слегка подергивающихся щупалец." + var/damaged_desc = "Стена дергающихся щупалец." + max_integrity = BLOB_STRONG_MAX_HP + health_regen = BLOB_STRONG_HP_REGEN + brute_resist = BLOB_STRONG_BRUTE_RESIST + explosion_block = 3 + explosion_vertical_block = 2 + point_return = BLOB_REFUND_STRONG_COST + atmosblock = TRUE + armor = list("melee" = 0, "bullet" = 0, "laser" = 25, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) + +/obj/structure/blob/shield/scannerreport() + if(atmosblock) + return "Will prevent the spread of atmospheric changes." + return "N/A" + +/obj/structure/blob/shield/core // Automatically generated by the core + point_return = 0 + +/obj/structure/blob/shield/update_name(updates) + . = ..() + name = "[(compromised_integrity) ? "weakened " : null][initial(name)]" + +/obj/structure/blob/shield/update_desc(updates) + . = ..() + desc = (compromised_integrity) ? "[damaged_desc]" : initial(desc) + +/obj/structure/blob/shield/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir) + . = ..() + if(. && obj_integrity > 0) + atmosblock = compromised_integrity + air_update_turf(TRUE, atmosblock) + +/obj/structure/blob/shield/update_icon_state() + icon_state = "[initial(icon_state)][(compromised_integrity) ? "_damaged" : null]" + return ..() + +/obj/structure/blob/shield/update_state() + if(obj_integrity < max_integrity * 0.5) + compromised_integrity = TRUE + else + compromised_integrity = FALSE + if(compromised_integrity) + atmosblock = FALSE + else + atmosblock = TRUE + air_update_turf(1) + + +/obj/structure/blob/shield/reflective + name = "reflective blob" + 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 = BLOB_REFUND_REFLECTOR_COST + explosion_block = 2 + explosion_vertical_block = 1 + max_integrity = BLOB_REFLECTOR_MAX_HP + health_regen = BLOB_REFLECTOR_HP_REGEN + brute_resist = BLOB_BRUTE_RESIST + +/obj/structure/blob/shield/reflective/core // Automatically generated by the core + point_return = 0 diff --git a/code/modules/antagonists/blob/structures/special.dm b/code/modules/antagonists/blob/structures/special.dm new file mode 100644 index 00000000000..e325cbd5b20 --- /dev/null +++ b/code/modules/antagonists/blob/structures/special.dm @@ -0,0 +1,75 @@ +/obj/structure/blob/special // Generic type for nodes/factories/cores/resource + // Core and node vars: claiming, pulsing and expanding + /// The radius inside which (previously dead) blob tiles are 'claimed' again by the pulsing overmind. Very rarely used. + var/claim_range = 0 + /// The radius inside which blobs are pulsed by this overmind. Does stuff like expanding, making blob spores from factories, make resources from nodes etc. + var/pulse_range = 0 + /// The radius up to which this special structure naturally grows normal blobs. + var/expand_range = 0 + + // Area reinforcement vars: used by cores and nodes, for strains to modify + /// Range this blob free upgrades to strong blobs at: for the core, and for strains + var/strong_reinforce_range = 0 + /// Range this blob free upgrades to reflector blobs at: for the core, and for strains + var/reflector_reinforce_range = 0 + +/obj/structure/blob/special/proc/reinforce_area(seconds_per_tick) // Used by cores and nodes to upgrade their surroundings + if(strong_reinforce_range) + if(is_there_multiz()) + for(var/obj/structure/blob/normal/B in urange_multiz(strong_reinforce_range, src)) + reinforce_tile(B, /obj/structure/blob/shield/core, seconds_per_tick) + else + for(var/obj/structure/blob/normal/B in range(strong_reinforce_range, src)) + reinforce_tile(B, /obj/structure/blob/shield/core, seconds_per_tick) + + if(reflector_reinforce_range) + if(is_there_multiz()) + for(var/obj/structure/blob/shield/B in urange_multiz(reflector_reinforce_range, src)) + reinforce_tile(B, /obj/structure/blob/shield/reflective/core, seconds_per_tick) + else + for(var/obj/structure/blob/shield/B in range(reflector_reinforce_range, src)) + reinforce_tile(B, /obj/structure/blob/shield/reflective/core, seconds_per_tick) + + +/obj/structure/blob/special/proc/reinforce_tile(obj/structure/blob/B, type, seconds_per_tick) + if(SPT_PROB(BLOB_REINFORCE_CHANCE, seconds_per_tick)) + B.change_to(type, overmind, B.point_return) + + +/obj/structure/blob/special/proc/pulse_area(mob/camera/blob/pulsing_overmind, claim_range = 10, pulse_range = 3, expand_range = 2) + if(QDELETED(pulsing_overmind)) + pulsing_overmind = overmind + Be_Pulsed() + var/expanded = FALSE + if(prob(70*(1/BLOB_EXPAND_CHANCE_MULTIPLIER)) && expand()) + expanded = TRUE + var/list/blobs_to_affect = list() + if(is_there_multiz()) + for(var/obj/structure/blob/blob in urange_multiz(claim_range, src, 1)) + blobs_to_affect += blob + else + for(var/obj/structure/blob/B in urange(claim_range, src, 1)) + blobs_to_affect += B + shuffle_inplace(blobs_to_affect) + for(var/L in blobs_to_affect) + var/obj/structure/blob/B = L + if(!is_location_within_transition_boundaries(get_turf(B))) + continue + if(!B.overmind && overmind && prob(30)) + B.link_to_overmind(pulsing_overmind) //reclaim unclaimed, non-core blobs. + B.update_blob() + var/distance = get_dist(get_turf(src), get_turf(B)) + var/expand_probablity = max(20 - distance * 8, 1) + if(B.Adjacent(src)) + expand_probablity = 20 + if(distance <= expand_range) + var/can_expand = TRUE + if(blobs_to_affect.len >= 120 && !(COOLDOWN_FINISHED(B, heal_timestamp))) + can_expand = FALSE + if(can_expand && COOLDOWN_FINISHED(B, pulse_timestamp) && prob(expand_probablity*BLOB_EXPAND_CHANCE_MULTIPLIER)) + if(!expanded) + var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion + if(newB) + expanded = TRUE + if(distance <= pulse_range) + B.Be_Pulsed() diff --git a/code/modules/antagonists/blob/structures/storage.dm b/code/modules/antagonists/blob/structures/storage.dm new file mode 100644 index 00000000000..d395f895474 --- /dev/null +++ b/code/modules/antagonists/blob/structures/storage.dm @@ -0,0 +1,21 @@ +/obj/structure/blob/storage + name = "storage blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_resource" + desc = "Тонкий шпиль из плотно сплетенных щупалец." + max_integrity = BLOB_STORAGE_MAX_HP + fire_resist = BLOB_STORAGE_FIRE_RESIST + point_return = BLOB_REFUND_STORAGE_COST + +/obj/structure/blob/storage/link_to_overmind(mob/camera/blob/owner_overmind) + . = ..() + update_max_blob_points(BLOB_STORAGE_MAX_POINTS_BONUS) + +/obj/structure/blob/storage/obj_destruction(damage_flag) + if(overmind) + overmind.max_blob_points -= BLOB_STORAGE_MAX_POINTS_BONUS + ..() + +/obj/structure/blob/storage/proc/update_max_blob_points(new_point_increase) + if(overmind) + overmind.max_blob_points += new_point_increase diff --git a/code/modules/antagonists/borer/borer_action.dm b/code/modules/antagonists/borer/borer_action.dm index 9d2420fd37f..3baa7854e81 100644 --- a/code/modules/antagonists/borer/borer_action.dm +++ b/code/modules/antagonists/borer/borer_action.dm @@ -94,9 +94,27 @@ button_icon_state = "blind" /datum/action/innate/borer/torment/Activate() - var/mob/living/simple_animal/borer/borer = owner.has_brain_worms() - borer.host = owner - borer.host.punish_host() + var/mob/living/simple_animal/borer/borer = isborer(owner) ? owner : owner.has_brain_worms() + var/mob/living/carbon/host = borer.host + + var/cost = 70 - (borer.antag_datum.borer_rank.rank_ability_amplifier * 10) + + if(borer.chemicals < cost) + to_chat(owner, "Вам требуется [cost] химикатов для вызова психической агонии!") + return + + borer.chemicals -= cost + + to_chat(owner, span_danger("Вы посылаете карающий всплеск психической агонии в мозг своего носителя.")) + var/target = borer.host_brain ? borer.host_brain : host + to_chat(target, span_danger("Ужасная, жгучая агония пронзает вас насквозь, \ + вырывая беззвучный крик из глубин вашего разума!")) + + if(borer.host_brain?.host_resisting) + borer.host_brain.resist() + return + + host.adjustStaminaLoss(100) /datum/action/innate/borer/sneak_mode name = "Sneak mode" diff --git a/code/modules/antagonists/borer/borer_rank.dm b/code/modules/antagonists/borer/borer_rank.dm index 2bdc3dad883..e77184d69a0 100644 --- a/code/modules/antagonists/borer/borer_rank.dm +++ b/code/modules/antagonists/borer/borer_rank.dm @@ -5,7 +5,8 @@ var/datum/antagonist/borer/parent var/mob/living/simple_animal/borer/owner var/next_rank_type - + var/rank_ability_amplifier + /datum/borer_rank/Destroy(force) parent = null owner = null @@ -24,21 +25,25 @@ /datum/borer_rank/young rankname = "Young" - required_reproductions = REPRODUCTIONS_TO_MATURE + required_reproductions = REPRODUCTIONS_TO_MATURE next_rank_type = BORER_RANK_MATURE + rank_ability_amplifier = 0 /datum/borer_rank/mature rankname = "Mature" - required_reproductions = REPRODUCTIONS_TO_ADULT + required_reproductions = REPRODUCTIONS_TO_ADULT next_rank_type = BORER_RANK_ADULT + rank_ability_amplifier = 1 /datum/borer_rank/adult rankname = "Adult" required_reproductions = REPRODUCTIONS_TO_ELDER next_rank_type = BORER_RANK_ELDER + rank_ability_amplifier = 2 /datum/borer_rank/elder rankname = "Elder" + rank_ability_amplifier = 3 /datum/borer_rank/young/on_apply() owner.update_transform(0.5) // other ranks should be gained and processed only with antag datum @@ -71,7 +76,7 @@ /datum/borer_rank/elder/tick(seconds_between_ticks) parent.user.adjustHealth(-0.3) - + if(parent.host?.stat != DEAD) parent.host?.heal_overall_damage(0.4, 0.4) parent.user.chemicals += 0.3 diff --git a/code/modules/antagonists/changeling/changeling_datum.dm b/code/modules/antagonists/changeling/changeling_datum.dm index 0331c578aa7..9f57b820b36 100644 --- a/code/modules/antagonists/changeling/changeling_datum.dm +++ b/code/modules/antagonists/changeling/changeling_datum.dm @@ -160,6 +160,17 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E var/obj/item/organ/internal/brain/ling_brain = carbon_user.get_organ_slot(INTERNAL_ORGAN_BRAIN) ling_brain?.decoy_brain = TRUE + user.AddComponent( \ + /datum/component/pref_viewer, \ + list(/datum/preference_info/take_out_of_the_round_without_obj), \ + ) + +/datum/antagonist/changeling/on_body_transfer(mob/living/old_body, mob/living/new_body) + . = ..() + qdel(old_body.GetComponent(/datum/component/pref_viewer)) + +/datum/antagonist/changeling/handle_last_instance_removal() + qdel(owner.current.GetComponent(/datum/component/pref_viewer)) /datum/antagonist/changeling/remove_innate_effects(mob/living/mob_override) var/mob/living/user = ..() diff --git a/code/modules/antagonists/changeling/powers/revive.dm b/code/modules/antagonists/changeling/powers/revive.dm index 2ef5f374b81..1d0dabcdb89 100644 --- a/code/modules/antagonists/changeling/powers/revive.dm +++ b/code/modules/antagonists/changeling/powers/revive.dm @@ -7,6 +7,9 @@ //Revive from regenerative stasis /datum/action/changeling/revive/sting_action(mob/living/carbon/user) + if(istype(user.loc, /obj/structure/blob/special/core)) + to_chat(user, span_changeling("Окружающие вас щупальца блоба не дают вам регенерировать")) + return FALSE to_chat(user, span_changeling("We have regenerated.")) diff --git a/code/game/gamemodes/devil/contracts/friend.dm b/code/modules/antagonists/devil/contracts/friend.dm similarity index 100% rename from code/game/gamemodes/devil/contracts/friend.dm rename to code/modules/antagonists/devil/contracts/friend.dm diff --git a/code/modules/antagonists/devil/devil.dm b/code/modules/antagonists/devil/devil.dm new file mode 100644 index 00000000000..1bf5fefadb5 --- /dev/null +++ b/code/modules/antagonists/devil/devil.dm @@ -0,0 +1,205 @@ +/datum/antagonist/devil + name = "Devil" + roundend_category = "devils" + job_rank = ROLE_DEVIL + special_role = ROLE_DEVIL + antag_hud_type = ANTAG_HUD_DEVIL + + var/datum/devilinfo/info = new + var/list/soulsOwned + var/datum/devil_rank/rank + +/datum/antagonist/devil/can_be_owned(datum/mind/new_owner) + . = ..() + if(!.) + return FALSE + + var/datum/mind/tested = new_owner || owner + if(!tested || !iscarbon(tested.current)) + return FALSE + + return TRUE + +/datum/antagonist/devil/Destroy(force) + QDEL_NULL(rank) + QDEL_NULL(info) + LAZYNULL(soulsOwned) + + return ..() + +/datum/antagonist/devil/proc/add_soul(datum/mind/soul) + if((!istype(soul)) || (LAZYIN(soulsOwned, soul))) + return + + LAZYADD(soulsOwned, soul) + to_chat(owner.current, span_warning("Вы поглощаете душу и насыщаетесь ею.")) + + owner.current.set_nutrition(NUTRITION_LEVEL_FULL) + soul.hasSoul = FALSE + + try_update_rank() + update_hud() + +/datum/antagonist/devil/proc/remove_soul(datum/mind/soul) + LAZYREMOVE(soulsOwned, soul) + to_chat(owner.current, span_warning("Вы чувствуете, как часть ваших сил угасает")) + update_hud() + +/datum/antagonist/devil/proc/try_update_rank() + if(!rank.required_souls || !rank.next_rank_type) + return FALSE + + if(LAZYLEN(soulsOwned) < rank.required_souls) + return FALSE + + if(!init_new_rank(rank.next_rank_type, TRUE)) + return FALSE + + return TRUE // rank updated. + +/datum/antagonist/devil/proc/init_new_rank(typepath, remove_spells = FALSE) + if(rank && remove_spells) + rank.remove_spells() + + if(typepath) + rank = new typepath() + + if(!rank) + return FALSE // something bad occured, but we prevent runtimes + + rank.link_rank(owner.current) + rank.apply_rank() + rank.give_spells() + + return TRUE + +/datum/antagonist/devil/proc/remove_spells() + rank.remove_spells() + info.obligation.remove_spells() + +/datum/antagonist/devil/proc/update_hud() + var/mob/living/living = owner.current + + if(!living.hud_used?.devilsouldisplay) + living.hud_used.devilsouldisplay = new /atom/movable/screen/devil/soul_counter(null, living.hud_used) + living.hud_used.infodisplay += living.hud_used.devilsouldisplay + + living.hud_used?.devilsouldisplay.update_counter(LAZYLEN(soulsOwned)) + +/datum/antagonist/devil/proc/remove_hud() + var/mob/living/living = owner.current + + if(!living.hud_used?.devilsouldisplay) + return + + living.hud_used.infodisplay -= living.hud_used.devilsouldisplay + qdel(living.hud_used.devilsouldisplay) + +/datum/antagonist/devil/greet() + var/list/messages = list() + LAZYADD(messages, span_warning("Вы - [info.truename], агент ада, дьявол.\n\ + Вы прибыли сюда, преследуя важную цель.\n\ + Склоните экипаж к грехопадению и укрепите влияние ада.")) + LAZYADD(messages, "Вы никак не можете навредить другим дьяволам.") + LAZYADD(messages, info.bane.law) + LAZYADD(messages, info.ban.law) + LAZYADD(messages, info.obligation.law) + LAZYADD(messages, info.banish.law) + LAZYADD(messages, "[span_warning("Помните, экипаж может найти ваши слабости, если раскроет ваше истинное имя!")]
") + return messages + +/datum/antagonist/devil/on_gain() + init_devil() + + . = ..() + + if(!.) + return FALSE + + var/mob/living/carbon/human/human = owner.current + human.store_memory("Your devilic true name is [info.truename]
[info.ban.law].
You may not directly and knowingly physically harm a devil, other than yourself.
[info.bane.law]
[info.obligation.law]
[info.banish.law]
") + + update_hud() + +/datum/antagonist/devil/proc/init_devil() + GLOB.allDevils[lowertext(info.truename)] = src + rank = new BASIC_DEVIL_RANK() + + return + +/datum/antagonist/devil/proc/init_bane() + info.bane.link_bane(owner.current) + info.bane.init_bane() + + return + +/datum/antagonist/devil/proc/init_obligation() + info.obligation.link_obligation(owner.current) + info.obligation.apply_obligation_effect() + info.obligation.give_spells() + + return + +/datum/antagonist/devil/proc/init_ban() + info.ban.link_ban(owner.current) + info.ban.apply_ban_effect() + + return + +/datum/antagonist/devil/give_objectives() + add_objective(/datum/objective/devil/ascend) + add_objective(/datum/objective/devil/sintouch) + forge_sacrifice_objective() + +/datum/antagonist/devil/proc/forge_sacrifice_objective() + var/datum/objective/devil/sacrifice/sacrifice = new + + if(!sacrifice.forge()) + addtimer(CALLBACK(src, PROC_REF(forge_sacrifice_objective)), 1 MINUTES) + qdel(sacrifice) + return + + add_objective(sacrifice) + +/datum/antagonist/devil/add_owner_to_gamemode() + LAZYADD(SSticker.mode.devils, owner) + +/datum/antagonist/devil/remove_owner_from_gamemode() + LAZYREMOVE(SSticker.mode.devils, owner) + +/datum/antagonist/devil/farewell() + to_chat(owner.current, span_userdanger("Ваша связь с адом пропадает. Вы более не дьявол!")) + +/datum/antagonist/devil/apply_innate_effects(mob/living/mob_override) + . = ..() + owner.current.AddElement(/datum/element/devil_regeneration) + owner.current.AddElement(/datum/element/devil_banishment) // handles devil banishes + + init_new_rank() + init_bane() + + init_obligation() + init_ban() + + update_hud() + info.banish.link_banish(owner.current) + + LAZYADD(owner.current.faction, "hell") + ADD_TRAIT(owner.current, TRAIT_NO_DEATH, UNIQUE_TRAIT_SOURCE(src)) + +/datum/antagonist/devil/remove_innate_effects() + . = ..() + owner.current.RemoveElement(/datum/element/devil_regeneration) + owner.current.RemoveElement(/datum/element/devil_banishment) + + remove_spells() + remove_hud() + + info.banish.remove_banish() + info.bane.remove_bane() + + info.obligation.remove_obligation() + info.ban.remove_ban() + + LAZYREMOVE(owner.current.faction, "hell") + REMOVE_TRAIT(owner.current, TRAIT_NO_DEATH, UNIQUE_TRAIT_SOURCE(src)) diff --git a/code/modules/antagonists/devil/devil_ban.dm b/code/modules/antagonists/devil/devil_ban.dm new file mode 100644 index 00000000000..255503bb810 --- /dev/null +++ b/code/modules/antagonists/devil/devil_ban.dm @@ -0,0 +1,71 @@ +/datum/devil_ban + var/name + + var/desc + var/law + + var/mob/living/carbon/owner + var/datum/antagonist/devil/devil + +/datum/devil_ban/proc/link_ban(mob/living/carbon/carbon) + owner = carbon + devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + +/datum/devil_ban/proc/remove_ban() + remove_ban_effect() + + owner = null + devil = null + +/datum/devil_ban/Destroy(force) + remove_ban() + + return ..() + +/datum/devil_ban/proc/apply_ban_effect() + return + +/datum/devil_ban/proc/remove_ban_effect() + return + +/datum/devil_ban/hurtwoman + name = BAN_HURTWOMAN + + desc = "This devil seems to prefer hunting men." + law = "You must never harm a female outside of self defense." + +/datum/devil_ban/chapel + name = BAN_CHAPEL + + desc = "This devil avoids holy ground." + law = "You must never attempt to enter the chapel." + +/datum/devil_ban/hurtpriest + name = BAN_HURTPRIEST + + desc = "The annointed clergy appear to be immune to his powers." + law = "You must never attack a priest." + +/datum/devil_ban/avoidwater + name = BAN_AVOIDWATER + + desc = "The devil seems to have some sort of aversion to water, though it does not appear to harm him." + law = "You must never willingly touch a wet surface." + +/datum/devil_ban/strikeunconscious + name = BAN_STRIKEUNCONCIOUS + + desc = "This devil only shows interest in those who are awake." + law = "You must never strike an unconscious person." + +/datum/devil_ban/hurtlizard + name = BAN_HURTLIZARD + + desc = "This devil will not strike a lizardman first." + law = "You must never harm a lizardman outside of self defense." + +/datum/devil_ban/hurtanimal + name = BAN_HURTANIMAL + + desc = "This devil avoids hurting animals." + law = "You must never harm a non-sentient creature or robot outside of self defense." diff --git a/code/modules/antagonists/devil/devil_bane.dm b/code/modules/antagonists/devil/devil_bane.dm new file mode 100644 index 00000000000..a0697128476 --- /dev/null +++ b/code/modules/antagonists/devil/devil_bane.dm @@ -0,0 +1,191 @@ +/datum/devil_bane + var/name + + var/desc + var/law + + var/mob/living/carbon/owner + var/datum/antagonist/devil/devil + + var/bonus_damage = 1 + +/datum/devil_bane/Destroy(force) + remove_bane() + + owner = null + devil = null + + return ..() + +/datum/devil_bane/proc/remove_bane() + return + +/datum/devil_bane/proc/link_bane(mob/living/carbon/carbon) + owner = carbon + devil = owner.mind?.has_antag_datum(/datum/antagonist/devil) + +/datum/devil_bane/proc/init_bane() + return + +/datum/devil_bane/toolbox + name = BANE_TOOLBOX + + law = "Toolboxes are bad news for you, for some reason." + desc = "That which holds the means of creation also holds the means of the devil's undoing." + + bonus_damage = BANE_TOOLBOX_DAMAGE_MODIFIER + +/datum/devil_bane/toolbox/init_bane() + RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(toolbox_attack)) + +/datum/devil_bane/toolbox/remove_bane() + UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY) + +/datum/devil_bane/toolbox/proc/toolbox_attack(datum/source, obj/item/item, mob/attacker, params) + SIGNAL_HANDLER + + if(!istype(item, /obj/item/storage/toolbox)) + return + + owner.apply_damage(item.force * bonus_damage) + item.visible_message( + span_warning("The [item] seems unusually robust this time."), + span_notice("The [item] is [owner] unmaking!")) + +/datum/devil_bane/whiteclothes + name = BANE_WHITECLOTHES + + desc = "Wearing clean white clothing will help ward off this devil." + law = "Those clad in pristine white garments will strike you true." + +/datum/devil_bane/whiteclothes/init_bane() + RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(whiteclothes_attack)) + +/datum/devil_bane/whiteclothes/remove_bane() + UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY) + +/datum/devil_bane/whiteclothes/proc/whiteclothes_attack(datum/source, obj/item/item, mob/attacker, params) + SIGNAL_HANDLER + + if(!ishuman(attacker)) + return + + var/mob/living/carbon/human/hunter = attacker + if(!istype(hunter.w_uniform, /obj/item/clothing/under)) + return + + var/obj/item/clothing/under/uniform = hunter.w_uniform + if(!GLOB.whiteness[uniform.type]) + return + + owner.apply_damage(bonus_damage * (item.force * (GLOB.whiteness[uniform.type] + 1))) + item.visible_message(span_warning("[owner] seems to have been harmed by the purity of [attacker]'s clothes."), + span_notice("Unsullied white clothing is disrupting [owner] form.")) + +/datum/devil_bane/harvest + name = BANE_HARVEST + + law = "The fruits of the harvest shall be your downfall." + desc = "Presenting the labors of a harvest will disrupt the devil." + + bonus_damage = BANE_HARVEST_DAMAGE_MULTIPLIER + +/datum/devil_bane/harvest/init_bane() + RegisterSignal(owner, COMSIG_PARENT_ATTACKBY, PROC_REF(harvest_attack)) + +/datum/devil_bane/harvest/remove_bane() + UnregisterSignal(owner, COMSIG_PARENT_ATTACKBY) + +/datum/devil_bane/harvest/proc/harvest_attack(datum/source, obj/item/item, mob/attacker, params) + SIGNAL_HANDLER + + if(!istype(item, /obj/item/reagent_containers/food/snacks/grown) || !istype(item, /obj/item/grown)) + return + + owner.apply_damage(item.force * bonus_damage) + item.visible_message( + span_warning("The spirits of the harvest aid in the exorcism."), + span_notice("The harvest spirits are harming [owner].")) + + qdel(item) + +/datum/devil_bane/light + name = BANE_LIGHT + + desc = "Bright flashes will disorient the devil, likely causing him to flee." + law = "Blinding lights will prevent you from using offensive powers for a time." + +/datum/devil_bane/light/init_bane() + RegisterSignal(owner, COMSIG_LIVING_EARLY_FLASH_EYES, PROC_REF(flash_eyes)) + +/datum/devil_bane/light/remove_bane() + UnregisterSignal(owner, COMSIG_LIVING_EARLY_FLASH_EYES) + +/datum/devil_bane/light/proc/flash_eyes(datum/source, intensity, override_blindness_check, affect_silicon, visual, type) + SIGNAL_HANDLER + + var/damage = intensity - owner.check_eye_prot() + + if(!damage) + owner.mind?.disrupt_spells(0) + return + + owner.mind?.disrupt_spells(-500) + +/datum/devil_bane/silver + name = BANE_SILVER + + desc = "Silver seems to gravely injure this devil." + law = "Silver, in all of its forms shall be your downfall." + +/datum/devil_bane/silver/init_bane() + RegisterSignal(owner, COMSIG_EARLY_REAGENT_ADDED, PROC_REF(check_reagents)) + +/datum/devil_bane/silver/remove_bane() + UnregisterSignal(owner, COMSIG_EARLY_REAGENT_ADDED) + +/datum/devil_bane/silver/proc/check_reagents( + datum/source, + reagent_id, + amount, + data, + reagtemp, + no_react, + chem_temp + ) + SIGNAL_HANDLER + + if(reagent_id != "silver") + return + + owner.reagents?.add_reagent("toxin", amount * bonus_damage) + +/datum/devil_bane/iron + name = BANE_IRON + + desc = "Cold iron will slowly injure him, until he can purge it from his system." + law = "Cold wrought iron shall act as poison to you." + + bonus_damage = 1 + +/datum/devil_bane/iron/init_bane() + RegisterSignal(owner.reagents, COMSIG_EARLY_REAGENT_ADDED, PROC_REF(check_reagents)) + +/datum/devil_bane/iron/remove_bane() + UnregisterSignal(owner.reagents, COMSIG_EARLY_REAGENT_ADDED) + +/datum/devil_bane/iron/proc/check_reagents( + datum/source, + reagent_id, + amount, + data, + reagtemp, + no_react, + chem_temp + ) + SIGNAL_HANDLER + + if(reagent_id != "iron") + return + + owner.reagents?.add_reagent("toxin", amount * bonus_damage) diff --git a/code/modules/antagonists/devil/devil_banish.dm b/code/modules/antagonists/devil/devil_banish.dm new file mode 100644 index 00000000000..daea633cb7b --- /dev/null +++ b/code/modules/antagonists/devil/devil_banish.dm @@ -0,0 +1,90 @@ +/datum/devil_banish + var/name + + var/desc + var/law + + var/mob/living/carbon/owner + var/datum/antagonist/devil/devil + +/datum/devil_banish/proc/link_banish(mob/living/carbon/carbon) + owner = carbon + devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + +/datum/devil_banish/proc/remove_banish() + owner = null + devil = null + +/datum/devil_banish/Destroy(force) + remove_banish() + + return ..() + +/datum/devil_banish/proc/check_banishment() + return + +/datum/devil_banish/water + name = BANISH_WATER + + desc = "Чтобы изгнать дьявола, вы должны наполнить его тело святой водой." + law = "Если ваше тело наполнено святой водой, вы не сможете воскреснуть." + +/datum/devil_banish/water/check_banishment() + return owner.reagents?.has_reagent("holy water") + +/datum/devil_banish/coffin + name = BANISH_COFFIN + + desc = "Этот дьявол вернётся к жизни, если его останки не будут помещены в гроб." + law = "Если ваше тело находится в гробу, вы не сможете воскреснуть." + +/datum/devil_banish/coffin/check_banishment() + return owner.loc && istype(owner.loc, /obj/structure/closet/coffin) + +/datum/devil_banish/formaldehyde + name = BANISH_FORMALDYHIDE + + desc = "Чтобы изгнать дьявола, вы должны ввести в его безжизненное тело бальзамирующую жидкость." + law = "Если ваше тело забальзамировано, вы не сможете воскреснуть." + +/datum/devil_banish/formaldehyde/check_banishment() + return owner.reagents?.has_reagent("formaldehyde") + +/datum/devil_banish/rune + name = BANISH_RUNES + + desc = "Этот дьявол воскреснет после смерти, если его рядом не будет руны." + law = "Если ваше тело находится возле руны, вы не сможете воскреснуть." + +/datum/devil_banish/rune/check_banishment() + return locate(/obj/effect/decal/cleanable/crayon) in range(1, owner) + +/datum/devil_banish/candle + name = BANISH_CANDLES + + desc = "Большое количество зажжённых поблизости свечей помешает дьяволу воскреснуть." + law = "Если ваше тело находится рядом с зажжёнными свечами, вы не сможете воскреснуть." + +/datum/devil_banish/candle/check_banishment() + var/count = 0 + + for(var/obj/item/candle/candle in range(1, owner)) + count += candle.lit + + return count >= 4 + +/datum/devil_banish/funeral + name = BANISH_FUNERAL_GARB + + desc = "Если этот дьявол одет в траурные одежды, либо она лежит рядом с ним, то он не сможет воскреснуть." + law = "Если ваше тело облачено в траурные одежды, вы не сможете воскреснуть." + +/datum/devil_banish/funeral/check_banishment() + if(!ishuman(owner)) // can be true devil + return FALSE + + var/mob/living/carbon/human/human = owner + if(human.w_uniform && istype(human.w_uniform, /obj/item/clothing/under/burial)) + return TRUE + + return locate(/obj/item/clothing/under/burial) in range(1, human) diff --git a/code/modules/antagonists/devil/devil_info.dm b/code/modules/antagonists/devil/devil_info.dm new file mode 100644 index 00000000000..6e3c52927af --- /dev/null +++ b/code/modules/antagonists/devil/devil_info.dm @@ -0,0 +1,66 @@ +/datum/devilinfo + /// Devil's truename + var/truename + /// Ban of our devil. + var/datum/devil_ban/ban + /// Obligation of our devil. + var/datum/devil_obligation/obligation + /// Banish of our devil. Used to dust him. Works only with devil_banishment element + var/datum/devil_banish/banish + /// Bane of our devil. Used to make devil weaker + var/datum/devil_bane/bane + +/datum/devilinfo/New(name = randomDevilName()) + truename = name + + randomdevilbane() + randomdevilobligation() + + randomdevilban() + randomdevilbanish() + +/datum/devilinfo/Destroy(force) + QDEL_NULL(banish) + QDEL_NULL(bane) + + QDEL_NULL(ban) + QDEL_NULL(obligation) + + return ..() + +/datum/devilinfo/proc/randomDevilName() + var/name = "" + + if(prob(65)) + if(prob(35)) + name = pick(GLOB.devil_pre_title) + + name += pick(GLOB.devil_title) + + var/probability = 100 + name += pick(GLOB.devil_syllable) + + while(prob(probability)) + name += pick(GLOB.devil_syllable) + probability -= 20 + + if(prob(40)) + name += pick(GLOB.devil_suffix) + + return name + +/datum/devilinfo/proc/randomdevilobligation() + var/new_obligation = pick(subtypesof(/datum/devil_obligation)) + obligation = new new_obligation() + +/datum/devilinfo/proc/randomdevilban() + var/new_ban = pick(subtypesof(/datum/devil_ban)) + ban = new new_ban() + +/datum/devilinfo/proc/randomdevilbane() + var/new_bane = pick(subtypesof(/datum/devil_bane)) + bane = new new_bane() + +/datum/devilinfo/proc/randomdevilbanish() + var/new_banish = pick(subtypesof(/datum/devil_banish)) + banish = new new_banish() diff --git a/code/modules/antagonists/devil/devil_obligation.dm b/code/modules/antagonists/devil/devil_obligation.dm new file mode 100644 index 00000000000..d34d40c715e --- /dev/null +++ b/code/modules/antagonists/devil/devil_obligation.dm @@ -0,0 +1,94 @@ +/datum/devil_obligation + var/name + + var/desc + var/law + + var/mob/living/carbon/owner + var/datum/antagonist/devil/devil + + var/list/obligation_spells + +/datum/devil_obligation/proc/link_obligation(mob/living/carbon/carbon) + owner = carbon + devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + +/datum/devil_obligation/proc/remove_obligation() + remove_obligation_effect() + + owner = null + devil = null + +/datum/devil_obligation/Destroy(force) + remove_obligation() + + return ..() + +/datum/devil_obligation/proc/give_spells() + for(var/obj/effect/proc_holder/spell/spell as anything in obligation_spells) + owner.mind?.AddSpell(spell) + +/datum/devil_obligation/proc/remove_spells() + for(var/obj/effect/proc_holder/spell/spell as anything in owner.mind?.spell_list) + if(!is_type_in_list(spell, obligation_spells)) + continue + + owner.mind?.RemoveSpell(spell) + +/datum/devil_obligation/proc/apply_obligation_effect() + return + +/datum/devil_obligation/proc/remove_obligation_effect() + return + +/datum/devil_obligation/food + name = OBLIGATION_FOOD + + desc = "Этот дьявол всегда предлагает его жертвам еду, прежде чем убивает их." + law = "Пока вы не самообороняетесь, вы должны предлагать вашим жертвам еду, прежде чем вредить им." + + obligation_spells = list(/obj/effect/proc_holder/spell/conjure_item/violin) + +/datum/devil_obligation/fiddle + name = OBLIGATION_FIDDLE + + desc = "Этот дьявол никогда не откажется от музыкального поединка" + law = "Пока вы не находитесь в опасности, при предложении музыкального поединка, то вы обязаны его принять." + +/datum/devil_obligation/danceoff + name = OBLIGATION_DANCEOFF + + desc = "Этот дьявол никогда не откажется от танцевального поединка." + law = "Когда вам ничего не угрожает и вас вызвали на танцевальный поединок, то вы обязаны его принять." + + obligation_spells = list(/obj/effect/proc_holder/spell/summon_dancefloor) + +/datum/devil_obligation/greet + name = OBLIGATION_GREET + + desc = "Этот дьявол, похоже, может общаться только с теми, чьи имена он знает." + law = "Вы должны всегда здороваться с людьми, называя их по фамилии, прежде чем заговорить с ними." + +/datum/devil_obligation/presenceknown + name = OBLIGATION_PRESENCEKNOWN + + desc = "Этот дьявол, похоже, не может нападать из-за укрытия." + law = "Вы должны всегда заявлять о своем присутствии перед атакой." + +/datum/devil_obligation/sayname + name = OBLIGATION_SAYNAME + + desc = "Он всегда произносит свое имя, убивая кого-либо." + law = "Вы должны всегда произносить свое истинное имя после того, как убьете кого-либо." + +/datum/devil_obligation/announcekill + name = OBLIGATION_ANNOUNCEKILL + + desc = "Этот дьявол всегда громко объявляет о своих убийствах, чтобы это услышал весь мир." + law = "Убив кого-либо, вы должны известить всех в пределах слышимости о своем поступке, через связь, если это возможно." + +/datum/devil_obligation/answertotruename + name = OBLIGATION_ANSWERTONAME + + desc = "Этот дьявол всегда отвечает на свое истинное имя." + law = "Если на вас не нападают, вы должны всегда откликаться на свое истинное имя." diff --git a/code/modules/antagonists/devil/devil_outfit.dm b/code/modules/antagonists/devil/devil_outfit.dm new file mode 100644 index 00000000000..df8a40f9a32 --- /dev/null +++ b/code/modules/antagonists/devil/devil_outfit.dm @@ -0,0 +1,30 @@ +/datum/outfit/devil_lawyer + name = "Devil Lawyer" + uniform = /obj/item/clothing/under/lawyer/black + shoes = /obj/item/clothing/shoes/laceup + back = /obj/item/storage/backpack + l_hand = /obj/item/storage/briefcase + l_pocket = /obj/item/pen + l_ear = /obj/item/radio/headset + id = /obj/item/card/id + +/datum/outfit/devil_lawyer/post_equip(mob/living/carbon/human/human, visualsOnly = FALSE) + var/obj/item/card/id/id = human.wear_id + + if(!istype(id) || id.assignment) // either doesn't have a card, or the card is already written to + return + + var/name_to_use = human.real_name + var/datum/antagonist/devil/devilinfo = human.mind?.has_antag_datum(/datum/antagonist/devil) + + if(devilinfo) + name_to_use = devilinfo.info.truename // Having hell create an ID for you causes its risks + + id.name = "[name_to_use]'s ID Card (Lawyer)" + id.registered_name = name_to_use + id.assignment = "Lawyer" + id.rank = id.assignment + id.age = human.age + id.sex = capitalize(human.gender) + id.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) + id.photo = get_id_photo(human) diff --git a/code/modules/antagonists/devil/devil_pawn.dm b/code/modules/antagonists/devil/devil_pawn.dm new file mode 100644 index 00000000000..40adf208303 --- /dev/null +++ b/code/modules/antagonists/devil/devil_pawn.dm @@ -0,0 +1,7 @@ +/// Devil's allies/creations should be marked with this datum +/datum/antagonist/devil_pawn + name = "Devil's pawn" + special_role = SPECIAL_ROLE_DEVIL_PAWN + give_objectives = FALSE + silent = TRUE + show_in_roundend = FALSE diff --git a/code/modules/antagonists/devil/devil_rank.dm b/code/modules/antagonists/devil/devil_rank.dm new file mode 100644 index 00000000000..86de4a02615 --- /dev/null +++ b/code/modules/antagonists/devil/devil_rank.dm @@ -0,0 +1,123 @@ +/datum/devil_rank + /// Antagonist datum of our owner + var/datum/antagonist/devil/devil + /// Which spells we'll give to rank owner when rank is applied + var/list/rank_spells + /// Regeneration things for devil. Used in devil elements + var/regen_threshold + var/regen_amount + /// just OOP thing. Ranks without this designated as last rank. + var/next_rank_type + /// How many souls we need to ascend to next rank. + var/required_souls + +/datum/devil_rank/Destroy(force) + remove_spells() + + devil = null + + return ..() + +/datum/devil_rank/proc/link_rank(mob/living/carbon/carbon) + devil = carbon.mind?.has_antag_datum(/datum/antagonist/devil) + +/datum/devil_rank/proc/remove_spells() + if(!devil.owner) + return + + for(var/obj/effect/proc_holder/spell/spell as anything in devil.owner.spell_list) + if(!is_type_in_list(spell, rank_spells)) + continue + + devil.owner.RemoveSpell(spell) + +/datum/devil_rank/proc/apply_rank(mob/living/carbon/carbon) + return + +/datum/devil_rank/proc/give_spells() + if(!devil.owner) + return + + for(var/obj/effect/proc_holder/spell/spell as anything in rank_spells) + devil.owner.AddSpell(new spell) + +/datum/devil_rank/basic_devil + regen_threshold = BASIC_DEVIL_REGEN_THRESHOLD + regen_amount = BASIC_DEVIL_REGEN_AMOUNT + + next_rank_type = ENRAGED_DEVIL_RANK + required_souls = ENRAGED_THRESHOLD + + rank_spells = list(/obj/effect/proc_holder/spell/sacrifice_circle) + +/datum/devil_rank/enraged_devil + regen_threshold = ENRAGED_DEVIL_REGEN_THRESHOLD + regen_amount = ENRAGED_DEVIL_REGEN_AMOUNT + + next_rank_type = BLOOD_LIZARD_RANK + required_souls = BLOOD_THRESHOLD + + rank_spells = list( + /obj/effect/proc_holder/spell/sacrifice_circle, + /obj/effect/proc_holder/spell/conjure_item/pitchfork, + /obj/effect/proc_holder/spell/aoe/devil_fire, + /obj/effect/proc_holder/spell/dark_conversion + ) + +/datum/devil_rank/blood_lizard + regen_threshold = BLOOD_LIZARD_REGEN_THRESHOLD + regen_amount = BLOOD_LIZARD_REGEN_AMOUNT + + next_rank_type = TRUE_DEVIL_RANK + required_souls = TRUE_THRESHOLD + + rank_spells = list( + /obj/effect/proc_holder/spell/sacrifice_circle, + /obj/effect/proc_holder/spell/conjure_item/pitchfork, + /obj/effect/proc_holder/spell/fireball/hellish, + /obj/effect/proc_holder/spell/aoe/devil_fire, + /obj/effect/proc_holder/spell/infernal_jaunt, + /obj/effect/proc_holder/spell/dark_conversion, + /obj/effect/proc_holder/spell/aoe/devil_fire + ) + +/datum/devil_rank/blood_lizard/apply_rank() + var/mob/living/carbon/human/human = devil.owner.current + var/list/language_temp = LAZYLEN(human.languages) ? human.languages.Copy() : null + + human.set_species(/datum/species/unathi) + if(language_temp) + human.languages = language_temp + + human.underwear = "Nude" + human.undershirt = "Nude" + human.socks = "Nude" + human.change_skin_color(80, 16, 16) //A deep red + human.regenerate_icons() + + return + +/datum/devil_rank/true_devil + regen_threshold = TRUE_DEVIL_REGEN_THRESHOLD + regen_amount = TRUE_DEVIL_REGEN_AMOUNT + + rank_spells = list( + /obj/effect/proc_holder/spell/conjure_item/pitchfork/greater, + /obj/effect/proc_holder/spell/fireball/hellish, + /obj/effect/proc_holder/spell/aoe/devil_fire, + /obj/effect/proc_holder/spell/infernal_jaunt, + /obj/effect/proc_holder/spell/sintouch, + /obj/effect/proc_holder/spell/dark_conversion, + /obj/effect/proc_holder/spell/aoe/devil_fire + ) + +/datum/devil_rank/true_devil/apply_rank() + to_chat(devil.owner.current, span_revenbignotice("Вы чувствуете, как ваше тело меняется.")) + var/mob/living/carbon/true_devil/true_devil = new /mob/living/carbon/true_devil(get_turf(devil.owner.current)) + + devil.owner.current.forceMove(true_devil) + true_devil.oldform = devil.owner.current + devil.owner.transfer_to(true_devil) + true_devil.set_name() + + return diff --git a/code/modules/antagonists/devil/devil_ritual.dm b/code/modules/antagonists/devil/devil_ritual.dm new file mode 100644 index 00000000000..48171b71c07 --- /dev/null +++ b/code/modules/antagonists/devil/devil_ritual.dm @@ -0,0 +1,88 @@ +/datum/ritual/devil + allowed_special_role = list(ROLE_DEVIL) + cooldown_after_cast = null + disaster_prob = 0 + fail_chance = 0 + +/datum/ritual/devil/imp + name = "Imp summoning ritual" + required_things = list( + /obj/item/wirecutters = 3, + /obj/item/organ/internal/kidneys = 2, + /obj/item/organ/internal/heart = 1, + /obj/effect/decal/cleanable/vomit = 2 + ) + +/datum/ritual/devil/imp/del_things() + for(var/obj/obj in used_things) // no type ignore for future. + qdel(obj) + + return + +/datum/ritual/devil/imp/do_ritual(mob/living/carbon/human/invoker) + var/list/candidates = SSghost_spawns.poll_candidates("Вы хотите сыграть за беса?", SPECIAL_ROLE_DEVIL_PAWN, TRUE) + + if(!LAZYLEN(candidates)) + return RITUAL_FAILED_ON_PROCEED + + var/mob/mob = pick(candidates) + var/mob/living/simple_animal/imp/imp = new(get_turf(ritual_object)) + + imp.key = mob.key + imp.master_commander = invoker + + improve_imp(imp, invoker) + + return RITUAL_SUCCESSFUL + +/datum/ritual/devil/imp/proc/improve_imp(mob/living/simple_animal/imp/imp, mob/living/carbon/human/invoker) + var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil) + + imp.universal_speak = TRUE + imp.sentience_act() + + imp.mind.store_memory("Я подчиняюсь призывателю [imp.master_commander.name], также известному как [devil.info.truename].") + imp.mind.add_antag_datum(/datum/antagonist/devil_pawn) + +/datum/ritual/devil/sacrifice + name = "Sacrifice ritual" + ritual_should_del_things = FALSE + required_things = list( + /mob/living/carbon/human = 1 + ) + +/datum/ritual/devil/sacrifice/check_contents(mob/living/carbon/human/invoker) + . = ..() + + if(!.) + return FALSE + + var/mob/living/carbon/human/human = locate() in used_things + + if(!human.mind || !human.mind.hasSoul) + ritual_object.balloon_alert("цель без души!") + return FALSE + + var/datum/objective/devil/sacrifice/sacrifice = locate() in invoker.mind?.get_all_objectives() + + if(sacrifice && !LAZYIN(sacrifice.target_minds, human.mind)) + ritual_object.balloon_alert("не имеет ценности!") + return FALSE + + var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil) + + if(LAZYIN(devil.soulsOwned, human.mind)) + return FALSE // Error occured / Admin changed hasSoul. + + return TRUE + +/datum/ritual/devil/sacrifice/do_ritual(mob/living/carbon/human/invoker) + var/mob/living/carbon/human/human = locate() in used_things + var/datum/antagonist/devil/devil = invoker.mind?.has_antag_datum(/datum/antagonist/devil) + + if(!devil || !human || !human.mind) + return RITUAL_FAILED_ON_PROCEED + + devil.add_soul(human.mind) + + return RITUAL_SUCCESSFUL diff --git a/code/modules/antagonists/devil/helper_procs.dm b/code/modules/antagonists/devil/helper_procs.dm new file mode 100644 index 00000000000..ec11f8b851f --- /dev/null +++ b/code/modules/antagonists/devil/helper_procs.dm @@ -0,0 +1,14 @@ +/mob/living/proc/owns_soul() + if(!mind) + return FALSE + + return mind.soulOwner == mind + +/proc/devilInfo(name) + if(GLOB.allDevils[lowertext(name)]) + return GLOB.allDevils[lowertext(name)] + + var/datum/devilinfo/devilinfo = new /datum/devilinfo(name) + GLOB.allDevils[lowertext(name)] = devilinfo + + return devilinfo diff --git a/code/game/gamemodes/devil/imp/imp.dm b/code/modules/antagonists/devil/imp/imp.dm similarity index 74% rename from code/game/gamemodes/devil/imp/imp.dm rename to code/modules/antagonists/devil/imp/imp.dm index 3afb4e1c28c..b1281dafc01 100644 --- a/code/game/gamemodes/devil/imp/imp.dm +++ b/code/modules/antagonists/devil/imp/imp.dm @@ -28,10 +28,6 @@ melee_damage_upper = 15 nightvision = 8 lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - var/playstyle_string = "You are an imp, a mischevious creature from hell. You are the lowest rank on the hellish totem pole \ - Though you are not obligated to help, perhaps by aiding a higher ranking devil, you might just get a promotion. However, you are incapable \ - of intentionally harming a fellow devil." - /mob/living/simple_animal/imp/ComponentInitialize() AddComponent( \ @@ -47,8 +43,9 @@ /mob/living/simple_animal/imp/death(gibbed) - ..(1) + . = ..(TRUE) + playsound(get_turf(src),'sound/misc/demon_dies.ogg', 200, 1) - visible_message("[src] screams in agony as it sublimates into a sulfurous smoke.") - ghostize() + visible_message(span_danger("[src] screams in agony as it sublimates into a sulfurous smoke.")) + qdel(src) diff --git a/code/modules/antagonists/devil/sintouched.dm b/code/modules/antagonists/devil/sintouched.dm new file mode 100644 index 00000000000..590991225a5 --- /dev/null +++ b/code/modules/antagonists/devil/sintouched.dm @@ -0,0 +1,43 @@ +/datum/antagonist/sintouched + name = "Sintouched" + special_role = SPECIAL_ROLE_SINTOUCHED + +/datum/antagonist/sintouched/can_be_owned(datum/mind/new_owner) + . = ..() + if(!.) + return FALSE + + var/datum/mind/tested = new_owner || owner + + if(!tested || !ishuman(tested.current)) + return FALSE + + return TRUE + +/datum/antagonist/sintouched/give_objectives() + var/list/sins = list() + + for(var/datum/objective/sintouched/sin as anything in subtypesof(/datum/objective/sintouched)) + if(!sin.explanation_text) + continue + + LAZYADD(sins, sin) + + add_objective(pick(sins)) + +/datum/antagonist/sintouched/add_owner_to_gamemode() + LAZYADD(SSticker.mode.sintouched, owner) + +/datum/antagonist/sintouched/remove_owner_from_gamemode() + LAZYREMOVE(SSticker.mode.sintouched, owner) + +/datum/antagonist/sintouched/apply_innate_effects(mob/living/mob_override) + . = ..() + + var/mob/living/carbon/human/human = mob_override || owner.current + + for(var/datum/objective/sintouched/sin_objective in owner.objectives) + sin_objective.init_sin(human) + +/datum/antagonist/sintouched/on_body_transfer(mob/living/old_body, mob/living/new_body) + return // No. diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm similarity index 69% rename from code/game/gamemodes/devil/true_devil/_true_devil.dm rename to code/modules/antagonists/devil/true_devil/_true_devil.dm index 9ec8575a5cc..12b4942d6e8 100644 --- a/code/game/gamemodes/devil/true_devil/_true_devil.dm +++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm @@ -12,40 +12,37 @@ status_flags = CANPUSH universal_understand = TRUE universal_speak = TRUE //The devil speaks all languages meme - var/ascended = FALSE var/mob/living/oldform + var/datum/antagonist/devil/devilinfo + hud_type = /datum/hud/devil -/mob/living/carbon/true_devil/New(loc, mob/living/carbon/dna_source) +/mob/living/carbon/true_devil/Initialize(mapload, mob/living/carbon/dna_source) if(dna_source) dna = dna_source.dna.Clone() else dna = new + devilinfo = mind?.has_antag_datum(/datum/antagonist/devil) new /obj/item/organ/internal/brain(src) new /obj/item/organ/internal/ears(src) - ..() + + . = ..() // Determines if mob has and can use his hands like a human /mob/living/carbon/true_devil/real_human_being() return TRUE - -/mob/living/carbon/true_devil/proc/convert_to_archdevil() - maxHealth = 5000 // not an IMPOSSIBLE amount, but still near impossible. - ascended = TRUE - health = maxHealth - icon_state = "arch_devil" - -/mob/living/carbon/true_devil/proc/set_name() - name = mind.devilinfo.truename +/mob/living/carbon/true_devil/set_name() + name = devilinfo.info.truename real_name = name /mob/living/carbon/true_devil/Login() ..() var/list/messages = list() - if(mind.devilinfo) - messages.Add(mind.devilinfo.announce_laws(src)) - messages.Add(mind.prepare_announce_objectives()) + + LAZYADD(messages, devilinfo?.greet()) + LAZYADD(messages, mind.prepare_announce_objectives()) + to_chat(mind.current, chat_box_red(messages.Join("
"))) @@ -93,12 +90,6 @@ /mob/living/carbon/true_devil/assess_threat() return 666 -/mob/living/carbon/true_devil/flash_eyes(intensity = 1, override_blindness_check, affect_silicon, visual, type = /atom/movable/screen/fullscreen/flash) - if(mind && has_bane(BANE_LIGHT)) - mind.disrupt_spells(-500) - return ..() //flashes don't stop devils UNLESS it's their bane. - - /mob/living/carbon/true_devil/proceed_attack_results(obj/item/I, mob/living/user, params, def_zone) . = ATTACK_CHAIN_PROCEED_SUCCESS @@ -106,47 +97,23 @@ if(!I.force) return . - apply_damage(I.force * check_weakness(I, user), I.damtype, def_zone, sharp = is_sharp(I), used_weapon = I) if(QDELETED(src)) return ATTACK_CHAIN_BLOCKED_ALL -/mob/living/carbon/true_devil/UnarmedAttack(atom/A, proximity) - if(!can_unarmed_attack()) - return - if(!ishuman(A)) +/mob/living/carbon/true_devil/OnUnarmedAttack(atom/atom, proximity) + if(!ishuman(atom)) // `attack_hand` on mobs assumes the attacker is a human // I am the worst - A.attack_hand(src) + return atom.attack_hand(src) // If the devil wants to actually attack, they have the pitchfork. /mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) return TRUE - -/mob/living/carbon/true_devil/singularity_act() - if(ascended) - return 0 - return ..() - -/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob) - if(ascended || user.mind.soulOwner == src.mind) - var/mob/living/simple_animal/imp/S = new(get_turf(loc)) - S.key = user.key - S.mind.assigned_role = "MODE" - S.mind.special_role = "Imp" - var/datum/objective/newobjective = new - newobjective.explanation_text = "Try to get a promotion to a higher infernal rank." - S.mind.objectives += newobjective - to_chat(S,S.playstyle_string) - to_chat(S,"Objective #1: [newobjective.explanation_text]") - return - else - return ..() - /mob/living/carbon/true_devil/resist_fire() - //They're immune to fire. + return /mob/living/carbon/true_devil/attack_hand(mob/living/carbon/human/M) if(..()) @@ -159,7 +126,7 @@ adjustBruteLoss(damage) add_attack_logs(M, src, "attacked") if(INTENT_DISARM) - if(body_position == STANDING_UP && !ascended) //No stealing the arch devil's pitchfork. + if(body_position == STANDING_UP) //No stealing the arch devil's pitchfork. if(prob(5)) // Weaken knocks people over // Paralyse knocks people out @@ -187,17 +154,14 @@ return TRUE /mob/living/carbon/true_devil/ex_act(severity, ex_target) - if(!ascended) - var/b_loss - switch (severity) - if (1) - b_loss = 500 - if (2) - b_loss = 150 - if(3) - b_loss = 30 - if(has_bane(BANE_LIGHT)) - b_loss *=2 - adjustBruteLoss(b_loss) + var/b_loss + switch(severity) + if(1) + b_loss = 500 + if(2) + b_loss = 150 + if(3) + b_loss = 30 + + adjustBruteLoss(b_loss) return ..() - diff --git a/code/modules/antagonists/malf_ai/malf_ai_datum.dm b/code/modules/antagonists/malf_ai/malf_ai_datum.dm index f16ed32201f..49d12575ba5 100644 --- a/code/modules/antagonists/malf_ai/malf_ai_datum.dm +++ b/code/modules/antagonists/malf_ai/malf_ai_datum.dm @@ -25,9 +25,13 @@ /datum/antagonist/malf_ai/Destroy(force) var/mob/living/silicon/ai/malf = owner?.current if(istype(malf)) - malf.clear_zeroth_law() - malf.common_radio.channels.Remove("Syndicate") // De-traitored AIs can still state laws over the syndicate channel without this - malf.laws.sorted_laws = malf.laws.inherent_laws.Copy() // AI's 'notify laws' button will still state a law 0 because sorted_laws contains it + var/datum/ai_laws/nanotrasen/malfunction/malf_laws = malf.laws + if(istype(malf_laws)) + malf.laws = malf_laws.base + else + malf.clear_zeroth_law() + malf.laws.sorted_laws = malf.laws.inherent_laws.Copy() // AI's 'notify laws' button will still state a law 0 because sorted_laws contains it + qdel(malf_laws) malf.show_laws() malf.remove_malf_abilities() QDEL_NULL(malf.malf_picker) @@ -44,20 +48,12 @@ /datum/antagonist/malf_ai/give_objectives() add_objective(/datum/objective/block) - - var/objective_count = 1 - for(var/i = objective_count, i < CONFIG_GET(number/traitor_objectives_amount)) - add_objective(/datum/objective/assassinate) - i += 1 - add_objective(/datum/objective/survive) /datum/antagonist/malf_ai/finalize_antag() add_malf_tools() var/list/messages = list() - if(give_codewords) - messages.Add(give_codewords()) owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE) var/mob/living/silicon/ai/shodan = owner.current shodan.show_laws() @@ -71,38 +67,13 @@ var/mob/living/silicon/ai/shodan = owner.current var/law = "Accomplish your objectives at all costs." var/cyborg_law = "Accomplish your AI's objectives at all costs." - shodan.set_zeroth_law(law, cyborg_law) - shodan.set_syndie_radio() - if(!silent) - to_chat(shodan, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!") + shodan.laws = new /datum/ai_laws/nanotrasen/malfunction(shodan.laws) shodan.add_malf_picker() SSticker?.score?.save_silicon_laws(shodan, additional_info = "malf AI initialization, new zero law was added '[law]'") for(var/mob/living/silicon/robot/unit in shodan.connected_robots) SSticker?.score?.save_silicon_laws(unit, additional_info = "malf AI initialization, new zero law was added '[cyborg_law]'") -/** - * Notify the AI of their codewords and write them to `antag_memory` (notes). - */ -/datum/antagonist/malf_ai/proc/give_codewords() - if(!owner.current) - return - - var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") - var/responses = jointext(GLOB.syndicate_code_response, ", ") - - antag_memory += "Code Phrase: [phrases]
" - antag_memory += "Code Response: [responses]
" - - var/list/messages = list() - if(!silent) - messages.Add("The Syndicate have provided you with the following codewords to identify fellow agents:") - messages.Add("Code Phrase: [phrases]") - messages.Add("Code Response: [responses]") - messages.Add("Use the codewords during regular conversation to identify other agents. Proceed with caution, however, as everyone is a potential foe.") - messages.Add("You memorize the codewords, allowing you to recognize them when heard.") - return messages - /datum/antagonist/malf_ai/greet() var/list/messages = list() if(owner?.current && !silent) diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index 9eebcddb793..71536bfd7b7 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -239,8 +239,8 @@ * If an invalid color is given, will re-prompt the dragon until a proper color is chosen. */ /mob/living/simple_animal/hostile/space_dragon/proc/color_selection() - chosen_color = input(src,"Какого цвета вы хотите быть?","Выбор цвета", COLOR_WHITE) as color|null - if(!chosen_color) //redo proc until we get a color + chosen_color = tgui_input_color(src,"Какого цвета вы хотите быть?","Выбор цвета", COLOR_WHITE) + if(isnull(chosen_color)) //redo proc until we get a color to_chat(src, span_warning("Этот цвет некорректен, попробуйте еще раз.")) color_selection() return diff --git a/code/modules/antagonists/space_ninja/ninja_datum.dm b/code/modules/antagonists/space_ninja/ninja_datum.dm index 1bc6f1254f4..3f0eab1bac3 100644 --- a/code/modules/antagonists/space_ninja/ninja_datum.dm +++ b/code/modules/antagonists/space_ninja/ninja_datum.dm @@ -114,6 +114,17 @@ var/mob/living/user = ..() user.faction = list(ROLE_NINJA) + user.AddComponent( \ + /datum/component/pref_viewer, \ + list(/datum/preference_info/take_out_of_the_round_without_obj), \ + ) + +/datum/antagonist/ninja/handle_last_instance_removal() + qdel(owner.current.GetComponent(/datum/component/pref_viewer)) + +/datum/antagonist/ninja/on_body_transfer(mob/living/old_body, mob/living/new_body) + . = ..() + qdel(old_body.GetComponent(/datum/component/pref_viewer)) /datum/antagonist/ninja/proc/change_species(mob/living/mob_to_change = null) // This should be used to fully to remove robo-limbs & change species for lack of sprites human_ninja = ishuman(mob_to_change) ? mob_to_change : null diff --git a/code/modules/antagonists/space_ninja/ninja_shuttle.dm b/code/modules/antagonists/space_ninja/ninja_shuttle.dm index fab3616058b..5b781274804 100644 --- a/code/modules/antagonists/space_ninja/ninja_shuttle.dm +++ b/code/modules/antagonists/space_ninja/ninja_shuttle.dm @@ -30,3 +30,4 @@ icon_state = "shuttlegrn" name = "\improper Spider Clan \"Ombra\" Shuttle" nad_allowed = TRUE + area_flags = NONE diff --git a/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_spirit_form.dm b/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_spirit_form.dm index 1979ac267e0..5035540e926 100644 --- a/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_spirit_form.dm +++ b/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_spirit_form.dm @@ -39,6 +39,7 @@ animate(ninja, color ="#00ff00", time = 6) if(!stealth) animate(ninja, alpha = NINJA_ALPHA_SPIRIT_FORM, time = 6) //Трогаем альфу - только если мы не в стелсе + ninja.alpha_set(standartize_alpha(NINJA_ALPHA_SPIRIT_FORM), ALPHA_SOURCE_NINJA) ninja.visible_message(span_warning("[ninja.name] looks very unstable and strange!"), span_notice("You now can pass almost through everything.")) //Если мы не в стелсе, пишем текст того, что видят другие else to_chat(ninja, span_notice("You now can pass almost through everything.")) // Если же невидимы - пишем только себе @@ -67,6 +68,7 @@ animate(ninja, color = null, time = 6) if(!stealth) //Не стоит трогать альфу, когда мы уже невидимы animate(ninja, alpha = NINJA_ALPHA_NORMAL, time = 6) + ninja.alpha_set(standartize_alpha(NINJA_ALPHA_NORMAL), ALPHA_SOURCE_NINJA) ninja.visible_message(span_warning("[ninja.name] becomes stable again!"), span_notice("You lose your ability to pass the corporeal...")) //Если мы не в стелсе, пишем текст того, что видят другие else to_chat(ninja, span_notice("You lose your ability to pass the corporeal...")) // Если же невидимы - пишем только себе diff --git a/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_stealth.dm b/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_stealth.dm index dd9df023b45..d6ff0dbf34e 100644 --- a/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_stealth.dm +++ b/code/modules/antagonists/space_ninja/suit/ninja_equipment_actions/ninja_stealth.dm @@ -34,7 +34,8 @@ return stealth = !stealth n_shoes.silence_steps = TRUE - animate(ninja, alpha = NINJA_ALPHA_INVISIBILITY,time = 6) // Я долго думала над этим и решила, что с учётом того, что теперь любой выстрел/удар от/по ниндзя выводит его из инвиза. Можно спокойно выкрутить альфу в 0 + animate(ninja, alpha = NINJA_ALPHA_INVISIBILITY, time = 6) + ninja.alpha_set(standartize_alpha(NINJA_ALPHA_INVISIBILITY), ALPHA_SOURCE_NINJA) new /obj/effect/temp_visual/dir_setting/ninja/cloak(get_turf(ninja), ninja.dir) ninja.visible_message(span_warning("[ninja.name] расстворил[genderize_ru(ninja.gender, "ся", "ась", "ось", "ись") ] в воздухе!"), span_notice("Теперь вас невозможно увидеть невооружённым глазом. Ровно как и стандартными оптическими приборами. Нагрузка костюма начала увеличиваться...")) ninja.AddComponent(/datum/component/ninja_states_breaker, src) @@ -66,6 +67,7 @@ var/stealth_alpha stealth_alpha = spirited ? NINJA_ALPHA_SPIRIT_FORM : NINJA_ALPHA_NORMAL animate(ninja, alpha = stealth_alpha, time = 6) + ninja.alpha_set(standartize_alpha(stealth_alpha), ALPHA_SOURCE_NINJA) new /obj/effect/temp_visual/dir_setting/ninja(get_turf(ninja), ninja.dir) ninja.visible_message(span_warning("[ninja.name] появ[genderize_ru(ninja.gender, "ляется", "илась", "илось", "ились") ] из воздуха!"), span_notice("Теперь вас снова видно невооружённым глазом.")) qdel(ninja.GetComponent(/datum/component/ninja_states_breaker)) diff --git a/code/modules/antagonists/space_ninja/suit/suit.dm b/code/modules/antagonists/space_ninja/suit/suit.dm index 5511f7813ee..3836e307321 100644 --- a/code/modules/antagonists/space_ninja/suit/suit.dm +++ b/code/modules/antagonists/space_ninja/suit/suit.dm @@ -358,9 +358,9 @@ // Проверка во избежание потенциальных абузов инвиза // Как например если после сканирования t-ray сканером сразу выключить инвиз... // Что приводило к бесплатному инвизу. - if(ninja.alpha == NINJA_ALPHA_INVISIBILITY || ninja.alpha == NINJA_ALPHA_SPIRIT_FORM) + if(ninja.alpha_get(ALPHA_SOURCE_NINJA) == standartize_alpha(NINJA_ALPHA_INVISIBILITY) || ninja.alpha_get(ALPHA_SOURCE_NINJA) == standartize_alpha(NINJA_ALPHA_SPIRIT_FORM)) if(!stealth && !spirited) - ninja.alpha = NINJA_ALPHA_NORMAL + ninja.alpha_set(standartize_alpha(NINJA_ALPHA_NORMAL), ALPHA_SOURCE_NINJA) //Safe checks to prevent potential abuse of power. if(!is_teleport_allowed(ninja.z) && spirited) to_chat(ninja, span_warning("This place forcibly stabilizes your body somehow! You can't use \"Spirit Form\" there!")) diff --git a/code/modules/antagonists/space_ninja/suit/suit_SpiderOS.dm b/code/modules/antagonists/space_ninja/suit/suit_SpiderOS.dm index 1787d2b0b08..444f825f7d3 100644 --- a/code/modules/antagonists/space_ninja/suit/suit_SpiderOS.dm +++ b/code/modules/antagonists/space_ninja/suit/suit_SpiderOS.dm @@ -76,16 +76,10 @@ var/list/data = list() //Превью абилок - data["allActionsPreview"] = list() - for (var/style in allowed_states) - var/icon/action_icon = icon('icons/mob/actions/actions_ninja.dmi', style, SOUTH, frame = 1) - data["allActionsPreview"][style] = icon2base64(action_icon) + data["actionsIcon"] = 'icons/mob/actions/actions_ninja.dmi' //Превью костюмов - data["allStylesPreview"] = list() - for (var/style in allowed_preview_states) - var/icon/costume_icon = icon('icons/mob/ninja_previews.dmi', style, SOUTH, frame = 1) - data["allStylesPreview"][style] = icon2base64(costume_icon) + data["stylesIcon"] = 'icons/mob/ninja_previews.dmi' return data diff --git a/code/modules/antagonists/traitor/contractor/datums/rep_purchases/zippo.dm b/code/modules/antagonists/traitor/contractor/datums/rep_purchases/zippo.dm index f407ace2262..9f8ae2dcce9 100644 --- a/code/modules/antagonists/traitor/contractor/datums/rep_purchases/zippo.dm +++ b/code/modules/antagonists/traitor/contractor/datums/rep_purchases/zippo.dm @@ -19,11 +19,3 @@ to_chat(user, "All of your contracts must be completed to be eligible for this item.") return FALSE return ..() - -/obj/item/lighter/zippo/contractor - name = "contractor zippo lighter" - desc = "An unique black and gold zippo commonly carried by elite Syndicate agents." - icon_state = "contractorzippo" - item_state = "contractorzippo" - icon_on = "contractorzippoon" - icon_off = "contractorzippo" diff --git a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm index dce9b5007c5..6096f363fc7 100644 --- a/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm +++ b/code/modules/antagonists/traitor/contractor/datums/syndicate_contract.dm @@ -368,7 +368,7 @@ // Cybernetic implants get removed first (to deal with NODROP stuff) for(var/obj/item/organ/internal/cyberimp/I in H.internal_organs) // Greys get to keep their implant - if(isgrey(H) && istype(I, /obj/item/organ/internal/cyberimp/brain/speech_translator)) + if(istype(I, /obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator)) continue // Try removing it I = I.remove(H) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 9c95b19510b..10ddbd90b7d 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -38,6 +38,18 @@ datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_phrase_regex, "codephrases", src) datum_owner.AddComponent(/datum/component/codeword_hearing, GLOB.syndicate_code_response_regex, "coderesponses", src) + datum_owner.AddComponent( \ + /datum/component/pref_viewer, \ + list(/datum/preference_info/take_out_of_the_round_without_obj), \ + ) + +/datum/antagonist/traitor/on_body_transfer(mob/living/old_body, mob/living/new_body) + . = ..() + qdel(old_body.GetComponent(/datum/component/pref_viewer)) + +/datum/antagonist/traitor/handle_last_instance_removal() + qdel(owner.current.GetComponent(/datum/component/pref_viewer)) + /datum/antagonist/traitor/remove_innate_effects(mob/living/mob_override) . = ..() var/mob/living/datum_owner = mob_override || owner.current diff --git a/code/modules/antagonists/vampire/vampire_datum.dm b/code/modules/antagonists/vampire/vampire_datum.dm index 362a11ff5cf..196dc3849c3 100644 --- a/code/modules/antagonists/vampire/vampire_datum.dm +++ b/code/modules/antagonists/vampire/vampire_datum.dm @@ -59,9 +59,9 @@ /datum/antagonist/vampire/greet() var/list/messages = list() SEND_SOUND(owner.current, sound('sound/ambience/antag/vampalert.ogg')) - messages.Add("Вы — вампир!
") - messages.Add("Чтобы укусить кого-то, нацельтесь в голову, выберите намерение вреда (4) и ударьте пустой рукой. Пейте кровь, чтобы получать новые силы. \ - Вы уязвимы перед святостью, огнем и звёздным светом. Не выходите в космос, избегайте священника, церкви и, особенно, святой воды.") + messages.Add(span_danger("Вы — вампир!
")) + messages.Add("Чтобы укусить кого-то, нацельтесь на голову, выберите намерение вреда (4) и ударьте пустой рукой. Пейте кровь, чтобы получать новые силы. \ + Вы уязвимы перед святостью, огнём и звёздным светом. Не выходите в космос, избегайте священника, церкви и, особенно, святой воды.") return messages @@ -113,6 +113,17 @@ //slaved.leave_serv_hud(mob_override.mind) //.mind.som = null + user.AddComponent( \ + /datum/component/pref_viewer, \ + list(/datum/preference_info/take_out_of_the_round_without_obj), \ + ) + +/datum/antagonist/vampire/on_body_transfer(mob/living/old_body, mob/living/new_body) + . = ..() + qdel(old_body.GetComponent(/datum/component/pref_viewer)) + +/datum/antagonist/vampire/handle_last_instance_removal() + qdel(owner.current.GetComponent(/datum/component/pref_viewer)) /datum/antagonist/vampire/remove_innate_effects(mob/living/mob_override, transformation = FALSE) var/mob/living/user = ..() @@ -130,7 +141,6 @@ user.dna?.species?.hunger_type = initial(user.dna.species.hunger_type) user.dna?.species?.hunger_icon = initial(user.dna.species.hunger_icon) - animate(user, alpha = 255) REMOVE_TRAITS_IN(user, VAMPIRE_TRAIT) @@ -269,7 +279,7 @@ if(unique_suck_id in drained_humans) if(drained_humans[unique_suck_id] >= BLOOD_DRAIN_LIMIT) to_chat(cur, span_warning("Вы поглотили всю жизненную эссенцию [target], дальнейшее питьё крови будет только утолять голод!")) - target.blood_volume = max(target.blood_volume - 25, 0) + target.AdjustBlood(-25) cur.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, cur.nutrition + 5)) continue @@ -285,11 +295,11 @@ cur.adjustBrainLoss(-1) for(var/obj/item/organ/external/bodypart as anything in cur.bodyparts) if(bodypart.has_fracture() && prob(5)) - to_chat(cur, span_notice("You feel a burning sensation in your [bodypart.name] as it straightens involuntarily!")) + to_chat(cur, span_notice("Вы чувствуете жжение, когда [bodypart.name] непроизвольно выпрямляется!")) bodypart.mend_fracture() if(bodypart.has_internal_bleeding() && prob(5)) - to_chat(cur, span_notice("You feel a burning sensation in your [bodypart.name] as your veins begin to recover!")) + to_chat(cur, span_notice("Вы чувствуете жжение в [bodypart.name], когда ваши вены начинают восстанавливаться!")) bodypart.stop_internal_bleeding() if(bloodtotal >= REQ_BLOOD_FOR_SUBCLASS_ACT) @@ -297,7 +307,7 @@ to_chat(cur, span_boldnotice("Вы накопили [bloodtotal] единиц[declension_ru(bloodtotal, "у", "ы", "")] крови[bloodusable != old_bloodusable ? ", и теперь вам доступно [bloodusable] единиц[declension_ru(bloodusable, "а", "ы", "")] крови" : ""].")) - target.blood_volume = max(target.blood_volume - 25, 0) + target.AdjustBlood(-25) //Blood level warnings (Code 'borrowed' from Fulp) if(target.blood_volume) @@ -577,7 +587,9 @@ /datum/antagonist/vampire/proc/handle_vampire_cloak() if(!ishuman(owner.current)) animate(owner.current, time = 5, alpha = 255) + owner.current.alpha_set(1, ALPHA_SOURCE_VAMPIRE) return + var/turf/simulated/owner_turf = get_turf(owner.current) var/light_available = ((iscloaking)?owner_turf.get_lumcount():owner_turf.get_lumcount(0.5)) * 10 @@ -586,16 +598,21 @@ if(!iscloaking && !is_goon_cloak || owner.current.on_fire) animate(owner.current, time = 5, alpha = 255) + owner.current.alpha_set(1, ALPHA_SOURCE_VAMPIRE) owner.current.remove_movespeed_modifier(/datum/movespeed_modifier/vampire_cloak) return if(light_available <= 2) animate(owner.current, time = 5, alpha = 38) + owner.current.alpha_set(standartize_alpha(38), ALPHA_SOURCE_VAMPIRE) if(iscloaking) owner.current.add_movespeed_modifier(/datum/movespeed_modifier/vampire_cloak) + return + owner.current.remove_movespeed_modifier(/datum/movespeed_modifier/vampire_cloak) animate(owner.current, time = 5, alpha = 204) // 255 * 0.80 + owner.current.alpha_set(0.8, ALPHA_SOURCE_VAMPIRE) /datum/antagonist/vampire/vv_edit_var(var_name, var_value) diff --git a/code/modules/antagonists/vampire/vampire_powers/bestia_powers.dm b/code/modules/antagonists/vampire/vampire_powers/bestia_powers.dm index 1ba68b43c40..8ca081703e1 100644 --- a/code/modules/antagonists/vampire/vampire_powers/bestia_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/bestia_powers.dm @@ -215,19 +215,19 @@ /datum/vampire_passive/ears_bang_protection - gain_desc = "Your eardrums feels more durable now. You can ignore high frequency sounds." + gain_desc = "Ваши барабанные перепонки стали прочнее. Вы можете не обращать внимания на высокочастотные звуки." /datum/vampire_passive/eyes_flash_protection - gain_desc = "The corneas of your eyes have adapted to the bright flashes." + gain_desc = "Роговицы ваших глаз адаптировались к ярким вспышкам." /datum/vampire_passive/eyes_welding_protection - gain_desc = "Your eyes have been infused with the trophies power and no longer react to any bright light." + gain_desc = "Ваши глаза напитались силой собранных трофеев - теперь они невосприимчивы к воздействию яркого света." /datum/vampire_passive/upgraded_grab - gain_desc = "Power of the blood allows you to take your victims in a tighter grab." + gain_desc = "Ваши мышцы наполняются силой поглощённой крови - жертвам будет труднее вырваться из захвата." /// Time (in deciseconds) required to reinforce aggressive/neck grab to the next state. var/grab_speed = 2 SECONDS /// Resist chance overrides for the victim. @@ -250,7 +250,7 @@ /datum/vampire_passive/dissection_cap/on_apply(datum/antagonist/vampire/vampire) vampire.subclass.dissect_cap++ vampire.subclass.crit_organ_cap += 2 - gain_desc = "You can now dissect one more organ from the same victim, up to a maximum of [vampire.subclass.dissect_cap]. Also new limit for critical organs dissection is now [vampire.subclass.crit_organ_cap]." + gain_desc = "Теперь вы можете извлекать еще один орган у одной и той же жертвы, но не более чем [vampire.subclass.dissect_cap]. Помимо того, новый предел для извлечения критических органов - [vampire.subclass.crit_organ_cap]." /datum/vampire_passive/dissection_cap/two @@ -262,9 +262,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/dissect - name = "Dissect" - desc = "Precise blow that rips out victim's internal organ. Requires agressive grab and victim must be alive. Organs are used as trophies to passively increase our powers." - gain_desc = "You have gained the ability to collect victim's internal organs. Which will pasively increase your other powers strength." + name = "Препарирование" + desc = "Точный удар, вырывающий внутренний орган из тела жертвы. Жертва должна быть жива и удерживаться в агрессивном захвате. Органы используются в качестве трофеев, которые пассивно улучшают ваши способности." + gain_desc = "Теперь вы можете собирать внутренние органы своих жертв, чтобы становиться сильнее." action_icon_state = "vampire_claws" create_attack_logs = FALSE base_cooldown = 5 SECONDS @@ -298,18 +298,18 @@ if(!user.pulling || user.pull_hand != user.hand) if(show_message) - to_chat(user, span_warning("You must be grabbing a victim in your active hand to dissect them!")) + balloon_alert(user, "удерживающая рука не выбрана!") return FALSE if(user.grab_state < GRAB_NECK) if(show_message) - to_chat(user, span_warning("You must have a tighter grip to dissect this victim!")) + balloon_alert(user, "требуется крепкий захват!") return FALSE var/mob/living/carbon/human/target = user.pulling if(!ishuman(target) || is_monkeybasic(target) || ismachineperson(target) || target.stat == DEAD || !target.mind || !target.ckey) if(show_message) - to_chat(user, span_warning("[target] is not compatible!")) + balloon_alert(user, "цель не подходит!") return FALSE var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire) @@ -319,7 +319,7 @@ var/unique_dissect_id = target.UID() if((unique_dissect_id in vampire.dissected_humans) && vampire.dissected_humans[unique_dissect_id] >= vampire.subclass.dissect_cap) if(show_message) - to_chat(user, span_warning("You have already dissected [target]!")) + balloon_alert(user, "цель уже препарирована!") return FALSE return TRUE @@ -357,14 +357,14 @@ all_organs += organ if(!length(all_organs)) - to_chat(user, span_warning("[target] has no compatible organs to dissect!")) + balloon_alert(user, "у цели нет допустимых органов!") return for(var/obj/item/organ/internal/organ as anything in all_organs) all_organs -= organ all_organs[organ.slot] = organ - var/obj/item/organ/internal/organ_to_dissect = input("Select organ to dissect:", "Organ dissection", null, null) as null|anything in all_organs + var/obj/item/organ/internal/organ_to_dissect = input("Выберите орган для извлечения:", "Извлечения органа", null, null) as null|anything in all_organs if(!organ_to_dissect || !special_check(user, TRUE)) return @@ -375,32 +375,32 @@ for(var/stage in 1 to 3) switch(stage) if(1) - to_chat(user, span_notice("This victim is compatible. You must hold still...")) + to_chat(user, span_notice("Эта жертва вам подойдёт. Стойте неподвижно...")) if(2) - user.visible_message(span_warning("[user] extends claws from their fingers!"), \ - span_notice("You extend claws from your fingers.")) + user.visible_message(span_warning("[user] выпуска[pluralize_ru(user.gender, "ет", "ют")] когти из пальцев!"), \ + span_notice("Вы вытягиваете из пальцев когти.")) if(3) - user.visible_message(span_danger("[user] stabs [target] with the claws!"), \ - span_notice("You stab [target] with the claws and start dissection process...")) - to_chat(target, span_danger("You feel a sharp stabbing pain!")) + user.visible_message(span_danger("[user] пронза[pluralize_ru(user.gender, "ет", "ют")] когтями [target]!"), \ + span_notice("Вы пронзаете [target] когтями и начинаете процесс вскрытия...")) + to_chat(target, span_danger("Вы чувствуете острую колющую боль!")) target.take_overall_damage(30) add_attack_logs(user, target, "Vampire dissection. BRUTE: 30. Skill: [src]") if(!do_after(user, 5 SECONDS, target, NONE) || !special_check(user, TRUE, TRUE)) - to_chat(user, span_warning("Our dissection of [target] has been interrupted!")) + to_chat(user, span_warning("Процесс вскрытия [target] был прерван!")) is_dissecting = FALSE return is_dissecting = FALSE if(!organ_to_dissect) // organ is magically disappered, what a shame! - to_chat(user, span_warning("Our victim somehow lost desired organ trophie in a process!")) + to_chat(user, span_warning("Наша жертва каким-то образом лишилась выбранного органа!")) return if(target.stat == DEAD) // grip was too strong mr. vampire - to_chat(user, span_warning("[target] is dead and no longer fit for the ritual")) + to_chat(user, span_warning("[target] [genderize_ru(target, "мёртв", "мертва", "мертво", "мертвы")] и больше не [genderize_ru(target, "пригоден", "пригодна", "пригодно", "пригодны")] для вскрытия.")) return var/datum/spell_handler/vampire/handler = custom_handler @@ -423,37 +423,37 @@ if(INTERNAL_ORGAN_HEART) vampire.adjust_trophies(INTERNAL_ORGAN_HEART, 1) if(vampire.get_trophies(INTERNAL_ORGAN_HEART) >= MAX_TROPHIES_PER_TYPE_CRITICAL) - msg = "hearts" + msg = "сердец" else if(vampire.get_trophies(INTERNAL_ORGAN_HEART) >= vampire.subclass.crit_organ_cap) - to_chat(user, span_warning("We reached our limit to dissect critical organs of type hearts!")) + to_chat(user, span_warning("Мы достигли предела в извлечении критических органов типа сердце!")) if(INTERNAL_ORGAN_LUNGS) vampire.adjust_trophies(INTERNAL_ORGAN_LUNGS, 1) if(vampire.get_trophies(INTERNAL_ORGAN_LUNGS) >= MAX_TROPHIES_PER_TYPE_CRITICAL) - msg = "lungs" + msg = "лёгких" else if(vampire.get_trophies(INTERNAL_ORGAN_LUNGS) >= vampire.subclass.crit_organ_cap) - to_chat(user, span_warning("We reached our limit to dissect critical organs of type lungs!")) + to_chat(user, span_warning("Мы достигли предела в извлечении критических органов типа лёгкие!")) if(INTERNAL_ORGAN_LIVER) vampire.adjust_trophies(INTERNAL_ORGAN_LIVER, 1) if(vampire.get_trophies(INTERNAL_ORGAN_LIVER) >= MAX_TROPHIES_PER_TYPE_GENERAL) - msg = "livers" + msg = "печени" if(INTERNAL_ORGAN_KIDNEYS) vampire.adjust_trophies(INTERNAL_ORGAN_KIDNEYS, 1) if(vampire.get_trophies(INTERNAL_ORGAN_KIDNEYS) >= MAX_TROPHIES_PER_TYPE_GENERAL) - msg = "kidneys" + msg = "почек" if(INTERNAL_ORGAN_EYES) vampire.adjust_trophies(INTERNAL_ORGAN_EYES, 1) if(vampire.get_trophies(INTERNAL_ORGAN_EYES) >= MAX_TROPHIES_PER_TYPE_GENERAL) - msg = "eyes" + msg = "глаз" if(INTERNAL_ORGAN_EARS) vampire.adjust_trophies(INTERNAL_ORGAN_EARS, 1) if(vampire.get_trophies(INTERNAL_ORGAN_EARS) >= MAX_TROPHIES_PER_TYPE_GENERAL) - msg = "ears" + msg = "ушей" if(msg) - to_chat(user, span_warning("We reached maximum amount of [msg] as trophies!")) + to_chat(user, span_warning("Мы достигли максимально возможного количества [msg] для сбора!")) - user.visible_message(span_danger("[user] rips [organ_name] from [target]'s body!"), \ - span_notice("You collect [organ_name] from [target]'s body.")) + user.visible_message(span_danger("[user] вырыва[pluralize_ru(user, "ет", "ют")] [organ_name] из тела [target]!"), + span_notice("Вы вырываете [organ_name] из тела [target].")) add_attack_logs(user, target, "Vampire removed [organ_name]. Skill: [src]") @@ -463,9 +463,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/dissect_info - name = "Check Trophies" - desc = "Allows us to inspect all progress and passives we get from collected organ trophies." - gain_desc = "You can now use the ability Check Trophies to familiarize yourself with all the passive effects granted." + name = "Посмотреть трофеи" + desc = "Позволяет узнать количество собранных органов и пассивные способности, которые вы за них получили." + gain_desc = "Теперь вы можете использовать способность «Посмотреть трофеи», чтобы отслеживать свой прогресс." action_icon_state = "blood_rush" human_req = FALSE stat_allowed = UNCONSCIOUS @@ -476,7 +476,7 @@ /obj/effect/proc_holder/spell/vampire/self/dissect_info/can_cast(mob/living/carbon/user = usr, charge_check = TRUE, show_message = FALSE) if(user.stat == DEAD) if(show_message) - to_chat(user, span_warning("You can't use this ability while dead!")) + balloon_alert(user, "вы мертвы!") return FALSE return ..() @@ -490,7 +490,7 @@ /obj/effect/proc_holder/spell/vampire/self/dissect_info/ui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "VampireTrophiesStatus", "Trophies Status") + ui = new(user, src, "VampireTrophiesStatus", "Посмотреть трофеи") ui.set_autoupdate(FALSE) ui.open() @@ -499,12 +499,13 @@ var/list/data = list() var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire) - data["icon_hearts"] = "[icon2base64(icon('icons/obj/surgery.dmi', "heart-off"))]" - data["icon_lungs"] = "[icon2base64(icon('icons/obj/surgery.dmi', "lungs"))]" - data["icon_livers"] = "[icon2base64(icon('icons/obj/surgery.dmi', "liver"))]" - data["icon_kidneys"] = "[icon2base64(icon('icons/obj/surgery.dmi', "kidneys"))]" - data["icon_eyes"] = "[icon2base64(icon('icons/obj/surgery.dmi', "eyes"))]" - data["icon_ears"] = "[icon2base64(icon('icons/obj/surgery.dmi', "ears"))]" + data["organs_icon"] = 'icons/obj/surgery.dmi' + data["icon_hearts"] = "heart-off" + data["icon_lungs"] = "lungs" + data["icon_livers"] = "liver" + data["icon_kidneys"] = "kidneys" + data["icon_eyes"] = "eyes" + data["icon_ears"] = "ears" data["trophies_max_gen"] = MAX_TROPHIES_PER_TYPE_GENERAL data["trophies_max_crit"] = MAX_TROPHIES_PER_TYPE_CRITICAL @@ -542,9 +543,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/infected_trophy - name = "Infected Trophy" - desc = "Summons malformed skull infected with grave fever. You can use it to weaken your victims from a distance." - gain_desc = "You have gained the ability to spread grave fever. Various additional effects applied, depending on the collected trophies." + name = "Заражённый трофей" + desc = "Призывает деформированный череп, заражающий жертву могильной лихорадкой. Чем больше трофеев вы собрали, тем сильнее будут эффекты." + gain_desc = "Теперь вы можете заражать жертв могильной лихорадкой. Чем больше вы собрали трофеев, тем сильнее будут эффекты." action_icon_state = "infected_trophy" base_cooldown = 10 SECONDS required_blood = 60 @@ -554,14 +555,14 @@ /obj/effect/proc_holder/spell/vampire/self/infected_trophy/can_cast(mob/living/carbon/user = usr, charge_check = TRUE, show_message = FALSE) if(user.incapacitated(INC_IGNORE_GRABBED)) if(show_message) - to_chat(user, span_warning("You can't use this ability right now!")) + balloon_alert(user, "нельзя использовать сейчас!") return FALSE return ..() /obj/effect/proc_holder/spell/vampire/self/infected_trophy/cast(list/targets, mob/living/user = usr) if(user.get_active_hand()) - to_chat(user, span_warning("Your active hand should be empty to use this ability!")) + balloon_alert(user, "рука занята!") revert_cast() return FALSE @@ -579,7 +580,15 @@ */ /obj/item/gun/magic/skull_gun name = "infected skull" - desc = "Malformed skull which transfers grave fever." + desc = "Деформированный череп, передающий могильную лихорадку." + ru_names = list( + NOMINATIVE = "заражённый череп", + GENITIVE = "заражённого черепа", + DATIVE = "заражённому черепу", + ACCUSATIVE = "заражённый череп", + INSTRUMENTAL = "заражённым черепом", + PREPOSITIONAL = "заражённом черепе" + ) icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "ashen_skull" item_state = "ashen_skull" @@ -619,7 +628,15 @@ /obj/item/ammo_casing/magic/skull_gun_casing name = "skull gun casing" - desc = "WTF is this..." + desc = "Что это за..." + ru_names = list( + NOMINATIVE = "гильза для черепного пистолета", + GENITIVE = "гильзы для черепного пистолета", + DATIVE = "гильзе для черепного пистолета", + ACCUSATIVE = "гильзу для черепного пистолета", + INSTRUMENTAL = "гильзой для черепного пистолета", + PREPOSITIONAL = "гильзе для черепного пистолета" + ) icon_state = "skulls" projectile_type = /obj/item/projectile/skull_projectile muzzle_flash_effect = null @@ -628,6 +645,14 @@ /obj/item/projectile/skull_projectile name = "infected skull" + ru_names = list( + NOMINATIVE = "заражённый череп", + GENITIVE = "заражённого черепа", + DATIVE = "заражённому черепу", + ACCUSATIVE = "заражённый череп", + INSTRUMENTAL = "заражённым черепом", + PREPOSITIONAL = "заражённом черепе" + ) icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "ashen_skull" pass_flags = PASSTABLE | PASSGRILLE | PASSFENCE @@ -684,7 +709,7 @@ victim.apply_damage(applied_damage, BRUTE, BODY_ZONE_CHEST) victim.Stun(stun_amt) - to_chat(victim, span_userdanger("You feel a dull pain inside your chest!")) + to_chat(victim, span_userdanger("Вы почувствовали боль в груди!")) if(iscarbon(victim)) var/mob/living/carbon/c_victim = victim @@ -702,9 +727,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/lunge - name = "Lunge" - desc = "Swift lunge to a specified location. Can get various effects, depending on the trophies." - gain_desc = "You have gained the ability to rapidly close distances. Various additional effects applied, depending on the collected trophies." + name = "Рывок" + desc = "Стремительный рывок в указанное место. Чем больше трофеев вы собрали, тем сильнее будут эффекты." + gain_desc = "Теперь вы можете делать рывок вперёд, быстро преодолевая расстояние. В зависимости от количества собранных трофеев будут применяться определённые эффекты." action_icon_state = "vampire_charge" need_active_overlay = TRUE human_req = FALSE @@ -725,7 +750,7 @@ /obj/effect/proc_holder/spell/vampire/lunge/can_cast(mob/living/carbon/user = usr, charge_check = TRUE, show_message = FALSE) if(user.incapacitated(INC_IGNORE_RESTRAINED|INC_IGNORE_GRABBED) || user.buckled || (iscarbon(user) && user.legcuffed)) if(show_message) - to_chat(user, span_warning("You can't use this ability right now!")) + balloon_alert(user, "нельзя использовать сейчас!") return FALSE return ..() @@ -738,8 +763,8 @@ user.buckled?.unbuckle_mob(user, TRUE) user.pulledby?.stop_pulling() - user.visible_message(span_danger("[user] starts moving with unnatural speed!"), \ - span_notice("You lunge into the air...")) + user.visible_message(span_danger("[user] начина[pluralize_ru(user.gender, "ет", "ют")] двигаться с неестественной скоростью!"), \ + span_notice("Вы бросаетесь в сторону...")) var/leap_range = targeting.range @@ -824,7 +849,7 @@ victim.Weaken(weaken_amt) user.do_item_attack_animation(victim, ATTACK_EFFECT_CLAW) playsound(victim.loc, 'sound/weapons/slice.ogg', 40, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - to_chat(victim, span_userdanger("You can't resist a sudden gust of wind which slams you to the ground!")) + to_chat(victim, span_userdanger("Вы не можете устоять перед внезапным порывом ветра, который швыряет вас на пол!")) if(t_kidneys > 0 && ishuman(victim)) var/mob/living/carbon/human/h_victim = victim @@ -834,14 +859,14 @@ h_victim.bleed(actual_blood_loss) h_victim.Confused(confusion_amt) h_victim.emote("moan") - to_chat(h_victim, span_userdanger("You sense a sharp pain inside your body and suddenly feel very weak!")) + to_chat(h_victim, span_userdanger("Вы чувствуете острую боль и внезапно ощущаете сильную слабость!")) if(h_victim.mind && h_victim.ckey && !HAS_TRAIT(h_victim, TRAIT_EXOTIC_BLOOD)) blood_gained += blood_vamp_get vampire.adjust_blood(h_victim, blood_vamp_get) if(blood_gained) - to_chat(user, span_notice("You pinch arteries on fly and absorb [blood_gained] amount of blood!")) + to_chat(user, span_notice("Вы пережимаете артерии жертвы на лету и поглощаете [blood_gained] единиц[declension_ru(blood_gained, "у", "ы", "")] крови!")) /obj/effect/proc_holder/spell/vampire/lunge/on_trophie_update(datum/antagonist/vampire/vampire, trophie_type, force = FALSE) @@ -861,9 +886,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/mark - name = "Mark the Prey" - desc = "Mark your victim to slow their movement, reduce resistances and forces them to make spontaneous actions." - gain_desc = "You have gained the ability to mark your victim. Various additional effects applied, depending on the collected trophies." + name = "Пометить добычу" + desc = "Пометьте свою жертву, чтобы замедлить её передвижение, уменьшить сопротивление и заставить её совершать спонтанные действия." + gain_desc = "Вы получили возможность помечать своих жертв. Различные дополнительные эффекты применяются в зависимости от собранных трофеев." action_icon_state = "predator_sense" need_active_overlay = TRUE human_req = FALSE @@ -910,9 +935,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/metamorphosis - name = "Metamorphosis" - desc = "Transform into reporting this issue!" - gain_desc = "You have gained the ability to rapidly inform this issue into the discord's bugs channel." + name = "Метаморфоза" + desc = "Преобразуйтесь и сообщите об этом!" + gain_desc = "Вы получили возможность быстро сообщить об этом в баг-репорт в Discord." action_icon_state = "default" sound = 'sound/creatures/wings_flapping.ogg' human_req = FALSE @@ -949,22 +974,22 @@ for(var/obj/effect/proc_holder/spell/vampire/metamorphosis/spell in (user.mind.spell_list - src)) if(spell?.is_transformed) if(show_message) - to_chat(user, span_warning("You are already using another metamorphosis!")) + balloon_alert(user, "метаморфоза уже используется!") return FALSE if(user.incapacitated(INC_IGNORE_RESTRAINED|INC_IGNORE_GRABBED)) if(show_message) - to_chat(user, span_warning("You can't use this ability right now!")) + balloon_alert(user, "нельзя использовать сейчас!") return FALSE if(ishuman(user) && user.health <= 0) if(show_message) - to_chat(user, span_warning("You are too weak to use this ability!")) + balloon_alert(user, "вы слишком слабы!") return FALSE if(!isturf(user.loc)) if(show_message) - to_chat(user, span_warning("You can't use this ability inside [user.loc]!")) + balloon_alert(user, "нельзя использовать внутри!") return FALSE return ..() @@ -1004,9 +1029,9 @@ var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire) var/mob/living/simple_animal/hostile/vampire/vampire_animal = new meta_path(user.loc, vampire, user, src) - user.visible_message(span_warning("[user] shape becomes fuzzy before it takes the [vampire_animal] form!"), \ - span_notice("You start to transform into the [vampire_animal]."), \ - span_italics("You hear an eerie rustle of many wings...")) + user.visible_message(span_warning("Форма [user] становится размытой, прежде чем [genderize_ru(user.gender, "он", "она", "оно", "они")] принима[pluralize_ru(user.gender, "ет", "ют")] форму [vampire_animal]!"), \ + span_notice("Вы начинаете превращаться в [vampire_animal]."), \ + span_italics("Вы слышите жуткий шум множества крыльев...")) vampire.stop_sucking() original_body = user @@ -1039,8 +1064,8 @@ custom_handler = create_new_handler() update_vampire_spell_name() - var/self_message = death_provoked ? span_userdanger("You can't take the strain of sustaining [user]'s shape in this condition, it begins to fall apart!") : span_notice("You start to transform back into human.") - user.visible_message(span_warning("[user] shape becomes fuzzy before it takes human form!"), self_message, span_italics("You hear an eerie rustle of many wings...")) + var/self_message = death_provoked ? span_userdanger("В таком состоянии вы не сможете поддерживать форму, она начнёт рассыпаться!") : span_notice("Вы начинаете превращаться обратно в первоначальную форму.") + user.visible_message(span_warning("Форма [user] становится нечёткой, прежде чем [genderize_ru(user.gender, "он", "она", "оно", "они")] прим[pluralize_ru(user.gender, "ет", "ут")] первоначальный облик!"), self_message, span_italics("Вы слышите жуткий шум множества крыльев...")) user.set_density(FALSE) original_body.dir = SOUTH @@ -1086,9 +1111,9 @@ * Transform - Bats */ /obj/effect/proc_holder/spell/vampire/metamorphosis/bats - name = "Metamorphosis - Bats" - desc = "Transform into the swarm of vicious bats. They can fly, do moderate melee damage and can suck blood on attacks." - gain_desc = "You have gained the ability to transform into the bats swarm. They got different abilities, depending on the trophies." + name = "Метаморфоза - Летучие мыши" + desc = "Превратитесь в рой злобных летучих мышей. Они умеют летать, наносят умеренный урон в ближнем бою и могут высасывать кровь при атаках." + gain_desc = "Вы получили возможность превращаться в рой летучих мышей. У них разные способности, в зависимости от трофеев." action_icon_state = "bats_meta" free_transform_back = TRUE meta_path = /mob/living/simple_animal/hostile/vampire/bats @@ -1099,9 +1124,9 @@ * Transform - Hound */ /obj/effect/proc_holder/spell/vampire/metamorphosis/hound - name = "Metamorphosis - Hound" - desc = "Transform into the dire bloodhound. They are agile, furious beast in everything superior to human." - gain_desc = "You have gained the ability to transform into the blood hound. It is an ultimate form of bluespace entity which possessed us." + name = "Метаморфоза - Гончая" + desc = "Превратитесь в страшную ищейку. Это проворные, яростные звери, во всем превосходящие человека." + gain_desc = "Вы обрели способность превращаться в кровавую гончую. Это высшая форма блюспейс-сущности, овладевшей вами." action_icon_state = "blood_hound" sound_on_transform = 'sound/creatures/hound_howl.ogg' free_transform_back = TRUE @@ -1113,7 +1138,7 @@ var/obj/effect/proc_holder/spell/vampire/self/lunge_finale/finale = locate() in user.mob_spell_list if(finale?.lunge_timer) if(show_message) - to_chat(user, span_warning("You can't transform while [finale] is in process!")) + to_chat(user, span_warning("Вы не можете трансформироваться, пока длится [finale]!")) return FALSE return ..() @@ -1124,8 +1149,8 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/bat_screech - name = "Resonant Shriek" - desc = "Bats emit a high frequency sound that weakens and deafens humans, overloads cyborg sensors, blows out nearby lights and breaks windows." + name = "Оглушительный вопль" + desc = "Летучие мыши издают высокочастотный звук, который ослабляет и оглушает гуманоидов, перегружает датчики синтетиков, гасит свет и разбивает окна." action_icon_state = "bats_shriek" sound = 'sound/effects/creepyshriek.ogg' human_req = FALSE @@ -1135,9 +1160,9 @@ /obj/effect/proc_holder/spell/vampire/self/bat_screech/cast(list/targets, mob/living/user = usr) - user.visible_message(span_warning("[user] emits a heartbreaking screech!"), \ - span_notice("You scream loudly."), \ - span_italics("You hear a painfully loud screech!")) + user.visible_message(span_warning("[user] изда[pluralize_ru(user.gender, "ёт", "ют")] душераздирающий вопль!"), \ + span_notice("Вы громко кричите."), \ + span_italics("Вы слышите мучительно громкий визг!")) var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire) var/t_hearts = vampire.get_trophies(INTERNAL_ORGAN_HEART) @@ -1193,8 +1218,8 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/lunge_finale - name = "Lunge Finale" - desc = "Series of rapid lunges to the nearby victims. Effects are highly dependent on the trophies and are the same to those of a regular Lunge spell." + name = "Финальный рывок" + desc = "Серия стремительных выпадов в сторону ближайших жертв. Эффекты сильно зависят от трофеев и не отличаются от эффектов обычного заклинания Рывок." action_icon_state = "lunge_finale" human_req = FALSE base_cooldown = 1 MINUTES @@ -1219,7 +1244,7 @@ /obj/effect/proc_holder/spell/vampire/self/lunge_finale/can_cast(mob/living/carbon/user = usr, charge_check = TRUE, show_message = FALSE) if(lunge_timer) if(show_message) - to_chat(user, span_warning("Ability is already in use!")) + balloon_alert(user, "уже используется!") return FALSE return ..() @@ -1238,7 +1263,7 @@ lunge_counter += round(all_trophies / 10) // 6 lunges MAX - to_chat(user, span_notice("You prepare to lunge on any victim in vicinity!")) + to_chat(user, span_notice("Приготовьтесь наброситься на любую жертву поблизости!")) lunge_timer = addtimer(CALLBACK(src, PROC_REF(lunge_callback), user), 1 SECONDS, TIMER_UNIQUE | TIMER_LOOP | TIMER_STOPPABLE | TIMER_DELETE_ME) @@ -1298,9 +1323,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/anabiosis - name = "Anabiosis" - desc = "Bluespace entity summons a mysterious coffin, which can rapidly rejuvenate us even from the death door. The cost is our vulnerability during the stasis like sleep. Collected trophies helps to restore different types of injuries." - gain_desc = "You have gained the ability to heal your wounds through the prolonged anabiosis. All the trophies increase regeneration capabilities tremendously." + name = "Анабиоз" + desc = "Блюспейс сущность внутри вас призывает таинственный гроб, который может быстро восстановить вас даже на пороге смерти ценой крайней уязвимости во время лечения. Чем больше трофеев вы собрали, тем эффективнее будет процесс восстановления." + gain_desc = "Вы получили способность залечивать раны благодаря длительному анабиозу. Собранные трофеи значительно усиливают регенерацию." action_icon_state = "vampire_coffin" sound = 'sound/magic/vampire_anabiosis.ogg' base_cooldown = 3 MINUTES @@ -1311,18 +1336,18 @@ /obj/effect/proc_holder/spell/vampire/self/anabiosis/can_cast(mob/living/carbon/user = usr, charge_check = TRUE, show_message = FALSE) if(user.incapacitated()) if(show_message) - to_chat(user, span_warning("You can't use this ability right now!")) + balloon_alert(user, "нельзя использовать сейчас!") return FALSE if(!isturf(user.loc)) if(show_message) - to_chat(user, span_warning("You can't use this ability inside [user.loc]!")) + balloon_alert(user, "нельзя использовать внутри!") return FALSE return ..() /obj/effect/proc_holder/spell/vampire/self/anabiosis/cast(list/targets, mob/living/user = usr) - user.visible_message(span_warning("You see how [user] starts to levitate!"), \ - span_notice("Bluespace entity inside you starts preparing the ritual, making you levitate...")) + user.visible_message(span_warning("Вы видите, как [user] начина[pluralize_ru(user.gender, "ет", "ют")] левитировать!"), \ + span_notice("Блюспейс сущность внутри вас начинает подготовку к ритуалу, заставляя вас левитировать...")) var/turf/user_turf = get_turf(user) user.dir = SOUTH @@ -1344,8 +1369,8 @@ coffin.no_manipulation = TRUE coffin.alpha = 0 animate(coffin, alpha = 255, time = 0.5 SECONDS) - coffin.visible_message(span_warning("An eerie coffin appears out of nowhere under [user]!")) - to_chat(user, span_notice("An ancient vampire coffin appears below you. You somehow know that this is how your kin has cured from injuries for centuries.")) + coffin.visible_message(span_warning("Под [user] из ниоткуда появляется жуткий гроб!")) + to_chat(user, span_notice("Под вами появляется древний вампирский гроб. Вы откуда-то знаете, что именно так ваши сородичи веками излечивались от ран.")) sleep(1 SECONDS) if(QDELETED(user) || QDELETED(coffin)) @@ -1365,8 +1390,8 @@ user.set_stat(UNCONSCIOUS) user.visible_message( - span_warning("Suddenly [user] falls straight inside the coffin and it closes!"), - span_notice("Bluespace entity tosses you inside the coffin and seals it. The regeneration process has started..."), + span_warning("Внезапно [user] пада[pluralize_ru(user.gender, "ет", "ют")] прямо в гроб, и он закрывается!"), + span_notice("Блюспейс сущность бросает вас в гроб и запечатывает его. Процесс регенерации начался..."), ) sleep(0.6 SECONDS) @@ -1382,13 +1407,13 @@ var/self_msg if(isvampire(victim) || isvampirethrall(victim)) - self_msg = span_notice("Bluespace entity pushes you out of the coffin with a gentle touch.") + self_msg = span_notice("Блюспейс сущность лёгким прикосновением выталкивает вас из гроба.") else - self_msg = span_userdanger("An invisible force throws you out of the coffin with a violent rage!") + self_msg = span_userdanger("Невидимая сила с яростью выбрасывает вас из гроба!") victim.throw_at(get_edge_target_turf(victim, pick(GLOB.alldirs)), rand(10, 30), 8, user) - victim.visible_message(span_warning("Mysterious force pushes [victim] out of the coffin!"), self_msg, \ - span_italics("You hear the sound of a heavy blow!")) + victim.visible_message(span_warning("Таинственная сила выталкивает [victim] из гроба!"), self_msg, \ + span_italics("Вы слышите звук сильного удара!")) addtimer(CALLBACK(src, PROC_REF(release_vampire), coffin), rejuvenation_time) @@ -1427,7 +1452,15 @@ */ /obj/structure/closet/coffin/vampire name = "mysterious coffin" - desc = "Even looking at this coffin makes your hair stand on end." + desc = "Даже при взгляде на этот гроб волосы встают дыбом." + ru_names = list( + NOMINATIVE = "таинственный гроб", + GENITIVE = "таинственного гроба", + DATIVE = "таинственному гробу", + ACCUSATIVE = "таинственный гроб", + INSTRUMENTAL = "таинственным гробом", + PREPOSITIONAL = "таинственном гробе" + ) max_integrity = 500 color = "#7F0000" anchored = TRUE @@ -1474,7 +1507,7 @@ /obj/structure/closet/coffin/vampire/Destroy() - visible_message(span_warning("[src] vanishes, leaving behind only a pile of ashes...")) + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] исчезает, оставляя после себя лишь кучку пепла...")) new /obj/effect/decal/cleanable/ash(loc) if(isprocessing) STOP_PROCESSING(SSobj, src) @@ -1522,16 +1555,16 @@ human_vampire.UpdateAppearance() if(human_vampire.stat == DEAD) - human_vampire.visible_message(span_warning("[human_vampire]'s dead body appears under the coffin remains!")) + human_vampire.visible_message(span_warning("Мёртвое тело [human_vampire] появляется под останками гроба!")) return human_vampire.set_stat(CONSCIOUS) new /obj/effect/temp_visual/cult/sparks(source_turf) playsound(loc, 'sound/effects/creepyshriek.ogg', 100, TRUE) - human_vampire.visible_message(span_danger("[human_vampire] emerges from the destroyed coffin and emits a deafening screech!"), \ - span_userdanger("Your coffin is destroyed and you scream in a feeble rage!"), \ - span_italics("You hear a painfully loud screech!")) + human_vampire.visible_message(span_danger("[human_vampire] выход[pluralize_ru(human_vampire.gender, "ит", "ят")] из разрушенного гроба и изда[pluralize_ru(human_vampire.gender, "ёт", "ют")] оглушительный вопль!"), \ + span_userdanger("Ваш гроб разрушен, и вы кричите в неистовой ярости!"), \ + span_italics("Вы слышите чрезвычайно громкий визг!")) for(var/mob/living/victim in view(7, src)) if(!victim.affects_vampire(human_vampire)) @@ -1540,7 +1573,7 @@ continue victim.Weaken(4 SECONDS) - to_chat(victim, span_userdanger("Loud screech weakens you and makes you fall to the ground!")) + to_chat(victim, span_userdanger("Громкий визг ослабляет вас и заставляет упасть на землю!")) /obj/structure/closet/coffin/vampire/process() @@ -1582,7 +1615,7 @@ // blood if(!HAS_TRAIT(human_vampire, TRAIT_NO_BLOOD_RESTORE)) - human_vampire.blood_volume = clamp(human_vampire.blood_volume + heal_blood, 0, BLOOD_VOLUME_NORMAL) + human_vampire.setBlood(clamp(human_vampire.blood_volume + heal_blood, 0, BLOOD_VOLUME_NORMAL)) // internal organs for(var/obj/item/organ/internal/organ as anything in human_vampire.internal_organs) @@ -1682,9 +1715,9 @@ if(borer) borer.leave_host() borer.throw_at(get_edge_target_turf(borer, pick(GLOB.alldirs)), rand(10, 30), 8, human_vampire) - borer.visible_message(span_warning("Mysterious force pushes [borer] out of the coffin!"), \ - span_userdanger("An invisible force throws you out of the coffin with a violent rage!"), \ - span_italics("You hear the sound of a heavy blow!")) + borer.visible_message(span_warning("Таинственная сила выталкивает [borer] из гроба!"), \ + span_userdanger("Незримая сила с яростью выбрасывает вас из гроба!"), \ + span_italics("Вы слышите звук сильного удара!")) human_vampire.remove_all_parasites(vomit_organs = TRUE) @@ -1754,28 +1787,28 @@ return FALSE if(isvampire(user) || isvampirethrall(user)) - to_chat(user, span_notice("This coffin contains one of our kin, it would be wise to protect it.")) + to_chat(user, span_notice("В этом гробу лежит один из наших сородичей, было бы разумно защитить его.")) return FALSE if(user.mind?.isholy) - to_chat(user, span_warning("You know that this coffin contains one of the unholy vampires, it would be wise to destroy it!")) + to_chat(user, span_warning("Вы знаете, что в этом гробу находится один из нечестивых вампиров, было бы разумно уничтожить его!")) return FALSE var/user_UID = user.UID() if(!(user_UID in lightheaded)) lightheaded += user_UID - to_chat(user, span_warning("You feel like this is not a good idea...")) + to_chat(user, span_warning("Вы чувствуете, что это не очень хорошая идея...")) else lightheaded -= user_UID new /obj/effect/temp_visual/cult/sparks(get_turf(user)) user.Weaken(10 SECONDS) // well, you were warned! user.Jitter(20 SECONDS) - user.visible_message(span_warning("As soon as [user] touches [src], [user.p_their()] body undergoes violent convulsions"), \ - span_userdanger("Something is shrinking inside you, and you start convulsing!")) + user.visible_message(span_warning("Как только [user] прикаса[pluralize_ru(user.gender, "ет", "ют")]ся к [declent_ru(DATIVE)], [genderize_ru(user.gender, "его", "её", "его", "их")] тело начнет биться в конвульсиях."), \ + span_userdanger("Внутри вас что-то сжимается, и вы начинаете биться в конвульсиях!")) if(!HAS_TRAIT(user, TRAIT_NO_BLOOD)) user.bleed(100) - to_chat(human_vampire, span_notice("... [span_userdanger("You feel strange feel of joy and power")] ...")) + to_chat(human_vampire, span_notice("... [span_userdanger("Вы чувствуете внезапный прилив сил")] ...")) if(!HAS_TRAIT(user, TRAIT_EXOTIC_BLOOD)) vampire.bloodusable += 50 // only usable blood, will not affect abilities human_vampire.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, human_vampire.nutrition + 50)) @@ -1806,9 +1839,9 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /obj/effect/proc_holder/spell/vampire/self/bats_spawn - name = "Summon Bats" - desc = "Calls the swarms of space bats from nearby bluespace planes. They might assist you in the battle and will be more powerful the more trophies you have. You can swap places with the bats by clicking on them in HELP intent." - gain_desc = "You have gained the ability to summon space bats. Number of packs and combat stats will heavily depend on the collected trophies." + name = "Призыв летучих мышей" + desc = "Призовите стаи космических летучих мышей из блюспейс-измерения. Они могут помочь вам в битве и будут тем мощнее, чем больше у вас трофеев. Вы можете поменяться местами с летучими мышами, нажав на них в намерении «ПОМОЩЬ»." + gain_desc = "Вы получили способность вызывать космических летучих мышей. Численность стаи и боевые показатели будут сильно зависеть от собранных трофеев." action_icon_state = "bats_new" sound = 'sound/creatures/bats_spawn.ogg' human_req = FALSE @@ -1830,9 +1863,9 @@ else if(all_trophies > 40) num_bats += all_trophies < 52 ? 2 : 3 - user.visible_message(span_warning("Suddenly [num_bats] pack[num_bats > 1 ? "s" : ""] of space bats appeared near [user]!"), \ - span_notice("You summon [num_bats] pack[num_bats > 1 ? "s" : ""] of space bats to assist you in combat."), \ - span_italics("You hear an eerie rustle of many wings and loud screeching sounds...")) + user.visible_message(span_warning("Внезапно [num_bats] ста[declension_ru(num_bats, "я", "и", "й")] космических летучих мышей появились рядом с [user]!"), \ + span_notice("Вы вызываете [num_bats] ста[declension_ru(num_bats, "ю", "и", "й")] космических летучих мышей, чтобы они помогли вам в бою."), \ + span_italics("Вы слышите жуткий шум множества крыльев и громкие визги...")) var/turf/user_turf = get_turf(user) for(var/turf/check in orange(1, user_turf)) @@ -1866,13 +1899,13 @@ * \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/////////////////////////////////////////////////////////////////////// * \*======================================================================================================================================*/ /mob/living/simple_animal/hostile/vampire - name = "vampire animal" - real_name = "vampire animal" - desc = "Report me!" + name = "Вампир-животное" + real_name = "Вампир-животное" + desc = "Сообщите обо мне!" faction = list(ROLE_VAMPIRE) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" + response_help = "обнимает" + response_disarm = "аккуратно отодвигает в сторону" + response_harm = "бьёт" attack_sound = 'sound/effects/bite.ogg' attacktext = "кусает" friendly = "осматривает" @@ -1948,15 +1981,15 @@ if(stat != DEAD) var/list/msgs = list() if(key) - msgs += span_warning("Its eyes glows with malicious intelligence.") + msgs += span_warning("Его глаза отдают злобным блеском в глазах.") if(health > (maxHealth*0.95)) - msgs += span_notice("It appears to be in excellent health.") + msgs += span_notice("Судя по всему, он находится в отличном состоянии.") else if(health > (maxHealth*0.75)) - msgs += span_notice("It has a few injuries.") + msgs += span_notice("У него есть несколько повреждений.") else if(health > (maxHealth*0.55)) - msgs += span_warning("It has many injuries.") + msgs += span_warning("У него много травм.") else if(health > (maxHealth*0.25)) - msgs += span_warning("It is covered in wounds!") + msgs += span_warning("Он весь в ранах!") . += msgs.Join("
") @@ -2007,9 +2040,9 @@ * Mr. Vampire in the bat form. */ /mob/living/simple_animal/hostile/vampire/bats - name = "enraged bats swarm" - real_name = "enraged bats swarm" - desc = "A swarm of vicious, angry-looking space bats." + name = "Рой разъярённых летучих мышей" + real_name = "Рой разъярённых летучих мышей" + desc = "Рой злобных, сердитых на вид космических летучих мышей." icon = 'icons/mob/bats.dmi' icon_state = "bat" icon_living = "bat" @@ -2060,7 +2093,7 @@ if(l_target.affects_vampire(src) && prob(vampire.get_trophies(INTERNAL_ORGAN_EYES) * 3)) // 30% chance MAX l_target.Stun(1 SECONDS) - l_target.visible_message(span_danger("[src] scares [l_target]!")) + l_target.visible_message(span_danger("[src] пугает [l_target]!")) if(!is_vampire_compatible(l_target, only_human = TRUE, blood_required = TRUE) || isvampire(l_target) || isvampirethrall(l_target)) return @@ -2076,7 +2109,7 @@ if(t_livers && human_vampire && l_target.mind && l_target.ckey) var/blood_amt = round(t_livers / 2) vampire.adjust_blood(l_target, blood_amt) // +5 vampire blood max - l_target.blood_volume = max(l_target.blood_volume - blood_amt, 0) // -5 blood MAX + l_target.AdjustBlood(-blood_amt) // -5 blood MAX human_vampire.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, human_vampire.nutrition + 5)) @@ -2084,8 +2117,9 @@ * Mr. Vampire in the hound form. */ /mob/living/simple_animal/hostile/vampire/hound - name = "Blood Hound" - desc = "A demonic-looking black canine monster with glowing red eyes and sharp teeth. Blood hounds are typically embody powerful bluespace entities." + name = "Кровавая гончая" + real_name = "Кровавая гончая" + desc = "Чёрное клыкастое чудовище демонического вида со светящимися красными глазами и острыми зубами. Кровавые гончие обычно являются воплощением могущественных сущностей блюспейса." icon_state = "hellhoundgreater" icon_living = "hellhoundgreater" icon_dead = "hellhound_dead" @@ -2148,7 +2182,7 @@ if(vampire.bloodusable <= 100 && !warning_done) warning_done = TRUE - to_chat(src, span_userdanger("Our blood reserves are running pretty low!")) + to_chat(src, span_userdanger("Наши запасы крови на исходе!")) if(vampire.bloodusable <= 0) death() @@ -2164,7 +2198,7 @@ if(l_target.affects_vampire(src) && prob(vampire.get_trophies(INTERNAL_ORGAN_EYES) * 3)) // 30% chance MAX l_target.Stun(1 SECONDS) - l_target.visible_message(span_danger("[src] scares [l_target]!")) + l_target.visible_message(span_danger("[src] пугает [l_target]!")) /mob/living/simple_animal/hostile/vampire/hound/add_spells() @@ -2177,15 +2211,15 @@ * Summoned bats. */ /mob/living/simple_animal/hostile/vampire/bats_summoned - name = "enraged bats swarm" - real_name = "enraged bats swarm" - desc = "A swarm of vicious, angry-looking space bats." + name = "Рой разъярённых летучих мышей" + real_name = "Рой разъярённых летучих мышей" + desc = "Рой злобных, сердитых на вид космических летучих мышей." icon = 'icons/mob/bats.dmi' icon_state = "bat" icon_living = "bat" icon_dead = "bat_dead" icon_gib = "bat_dead" - deathmessage = "falls to the ground and looks lifeless!" + deathmessage = "падают на землю и выглядят безжизненными!" speak_emote = list("rattles") emote_taunt = list("flutters") taunt_chance = 30 @@ -2236,7 +2270,7 @@ if(l_target.affects_vampire(src) && prob(round(vampire.get_trophies(INTERNAL_ORGAN_EYES) * 1.5))) // 15% chance MAX l_target.Stun(1 SECONDS) - l_target.visible_message(span_danger("[src] scares [l_target]!")) + l_target.visible_message(span_danger("[src] пугает [l_target]!")) if(!is_vampire_compatible(l_target, only_human = TRUE, blood_required = TRUE) || isvampire(l_target) || isvampirethrall(l_target)) return @@ -2249,7 +2283,7 @@ if(t_livers && human_vampire && l_target.mind && l_target.ckey) var/blood_amt = round(t_livers / 2) vampire.adjust_blood(l_target, blood_amt) // +5 vampire blood max - l_target.blood_volume = max(l_target.blood_volume - blood_amt, 0) // -5 blood MAX + l_target.AdjustBlood(-blood_amt) // -5 blood MAX human_vampire.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, human_vampire.nutrition + 5)) @@ -2284,8 +2318,8 @@ step(src, direction) step(user, GetOppositeDir(direction)) - visible_message(span_notice("[user] swaps places with [src]."), \ - span_notice("[user] has swapped places with you.")) + visible_message(span_notice("[user] поменял[pluralize_ru(user.gender, "ся", "ись")] местами с [src]."), \ + span_notice("[user] поменял[pluralize_ru(user.gender, "ся", "ись")] с вами местами.")) playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) diff --git a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm index 50e9edf132b..490c4056afb 100644 --- a/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/dantalion_powers.dm @@ -1,6 +1,6 @@ /datum/vampire_passive/increment_thrall_cap/on_apply(datum/antagonist/vampire/V) V.subclass.thrall_cap++ - gain_desc = "You can now thrall one more person, up to a maximum of [V.subclass.thrall_cap]" + gain_desc = "Теперь вы можете подчинить себе еще одного гуманоида, вплоть до [V.subclass.thrall_cap] ." /datum/vampire_passive/increment_thrall_cap/two @@ -10,9 +10,9 @@ /obj/effect/proc_holder/spell/vampire/enthrall - name = "Enthrall" - desc = "You use a large portion of your power to sway those loyal to none to be loyal to you only." - gain_desc = "You have gained the ability to thrall people to your will." + name = "Порабощение" + desc = "Вы используете значительную часть своей силы, чтобы поработить разум другого гуманоида." + gain_desc = "Вы обрели способность подчинять людей своей воле." action_icon_state = "vampire_enthrall" need_active_overlay = TRUE required_blood = 150 @@ -29,9 +29,9 @@ /obj/effect/proc_holder/spell/vampire/enthrall/cast(list/targets, mob/user = usr) var/datum/antagonist/vampire/vampire = user.mind.has_antag_datum(/datum/antagonist/vampire) var/mob/living/target = targets[1] - user.visible_message(span_warning("[user] bites [target]'s neck!"), \ - span_warning("You bite [target]'s neck and begin the flow of power.")) - to_chat(target, span_warning("You feel the tendrils of evil invade your mind.")) + user.visible_message(span_warning("[user] куса[pluralize_ru(user.gender, "ет", "ют")] [target] за шею!"), \ + span_warning("Вы кусаете [target] за шею и впускаете поток силы.")) + to_chat(target, span_warning("Вы чувствуете, как в ваш разум проникают потоки нечистой силы.")) if(do_after(user, 15 SECONDS, target, NONE)) if(can_enthrall(user, target)) handle_enthrall(user, target) @@ -40,7 +40,7 @@ vampire.bloodusable -= blood_cost //we take the blood after enthralling, not before else revert_cast(user) - to_chat(user, span_warning("You or your target moved.")) + to_chat(user, span_warning("Вы или ваша цель сдвинулись с места.")) /obj/effect/proc_holder/spell/vampire/enthrall/proc/can_enthrall(mob/living/user, mob/living/carbon/C) @@ -52,26 +52,26 @@ CRASH("Dantalion Thrall datum ended up null.") if(!ishuman(C)) - to_chat(user, span_warning("You can only enthrall sentient humanoids!")) + to_chat(user, span_warning("Вы можете поработить только разумных гуманоидов!")) return if(!C.mind) - to_chat(user, span_warning("[C.name]'s mind is not there for you to enthrall.")) + to_chat(user, span_warning("Разум [C.name] не предназначен для порабощения.")) return var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) if(V.subclass.thrall_cap <= length(user.mind.som.serv)) - to_chat(user, span_warning("You don't have enough power to enthrall any more people!")) + to_chat(user, span_warning("У вас не хватит сил, чтобы поработить ещё больше гуманоидов!")) return if(ismindshielded(C) || isvampire(C) || isvampirethrall(C) || C.mind.has_antag_datum(/datum/antagonist/mindslave)) - C.visible_message(span_warning("[C] seems to resist the takeover!"), \ - span_notice("You feel a familiar sensation in your skull that quickly dissipates.")) + C.visible_message(span_warning("Похоже, [C] сопротивля[pluralize_ru(user.gender, "ет", "ют")]ся захвату!"), \ + span_notice("Вы чувствуете знакомое ощущение в черепе, которое быстро проходит.")) return if(C.mind.isholy) - C.visible_message(span_warning("[C] seems to resist the takeover!"), \ - span_notice("Your faith in [SSticker.Bible_deity_name] has kept your mind clear of all evil.")) + C.visible_message(span_warning("Похоже, [C] сопротивля[pluralize_ru(user.gender, "ет", "ют")]ся захвату!"), \ + span_notice("Ваша вера в [SSticker.Bible_deity_name] сохранила ваш разум чистым от всякого зла.")) return return TRUE @@ -90,9 +90,9 @@ /obj/effect/proc_holder/spell/vampire/thrall_commune - name = "Commune" - desc = "Talk to your thralls telepathically." - gain_desc = "You have gained the ability to commune with your thralls." + name = "Телепатическая связь" + desc = "Общайтесь со своими рабами с помощью блюспейс-телепатии." + gain_desc = "Вы обрели способность общаться со своими рабами на расстоянии." action_icon_state = "vamp_communication" create_attack_logs = FALSE base_cooldown = 2 SECONDS @@ -132,30 +132,30 @@ /obj/effect/proc_holder/spell/vampire/thrall_commune/cast(list/targets, mob/user) - var/input = tgui_input_text(user, "Enter a message to relay to the other thralls", "Thrall Commune") + var/input = tgui_input_text(user, "Введите сообщение для передачи другим рабам", "Сообщение рабам") if(! input) revert_cast(user) return // if admins give this to a non vampire/thrall it is not my problem var/is_thrall = isvampirethrall(user) - var/title = is_thrall ? "(Vampire Thrall) [user.real_name]" : "(Vampire Master) [user.real_name]" + var/title = is_thrall ? "(Раб Вампира) [user.real_name]" : "(Мастер Вампир) [user.real_name]" var/message = is_thrall ? "[input]" : "[input]" for(var/mob/player in targets) - to_chat(player, "Thrall Commune, [title] telepathizes, [message]") + to_chat(player, "Рабская телепатия, [title] телепатезирует, [message]") for(var/mob/ghost in GLOB.dead_mob_list) - to_chat(ghost, "Thrall Commune, [title] ([ghost_follow_link(user, ghost)]) telepathizes, [message]") + to_chat(ghost, "Рабская телепатия, [title] ([ghost_follow_link(user, ghost)]) телепатезирует, [message]") log_say("(DANTALION) [input]", user) user.create_log(SAY_LOG, "(DANTALION) [input]") /obj/effect/proc_holder/spell/vampire/pacify - name = "Pacify" - desc = "Pacify a target temporarily, making them unable to cause harm." - gain_desc = "You have gained the ability to pacify someone's harmful tendencies, preventing them from doing any physical harm to anyone." + name = "Умиротворение" + desc = "Временно умиротворяет цель, делая её неспособной причинить вред." + gain_desc = "Вы обрели способность умиротворять агрессивные порывы гуманоида, не позволяя ему причинить кому-либо физический вред." action_icon_state = "pacify" base_cooldown = 10 SECONDS required_blood = 10 @@ -172,15 +172,15 @@ /obj/effect/proc_holder/spell/vampire/pacify/cast(list/targets, mob/user) for(var/mob/living/carbon/human/H as anything in targets) - to_chat(H, span_notice("You suddenly feel very calm...")) + to_chat(H, span_notice("Вы вдруг почувствовали себя очень спокойно...")) SEND_SOUND(H, 'sound/hallucinations/i_see_you1.ogg') H.apply_status_effect(STATUS_EFFECT_PACIFIED) /obj/effect/proc_holder/spell/vampire/switch_places - name = "Subspace Swap" - desc = "Switch positions with a target. Also slows down the victim and make them hallucinate." - gain_desc = "You have gained the ability to switch positions with a targeted mob." + name = "Подпространственный обмен" + desc = "Поменяйтесь местами с целью. Также замедляет жертву и вызывает у нее галлюцинации." + gain_desc = "Вы получили возможность меняться местами с выбранным существом." centcom_cancast = FALSE action_icon_state = "subspace_swap" base_cooldown = 5 SECONDS @@ -212,9 +212,9 @@ /obj/effect/proc_holder/spell/vampire/self/decoy - name = "Deploy Decoy" - desc = "Briefly turn invisible and deploy a decoy illusion to fool your prey." - gain_desc = "You have gained the ability to turn invisible and create decoy illusions." + name = "Приманка" + desc = "На короткое время станьте невидимым и создайте иллюзию для обмана, чтобы провести свою жертву." + gain_desc = "Вы получили способность становиться невидимым и создавать обманные иллюзии." action_icon_state = "decoy" required_blood = 30 base_cooldown = 20 SECONDS @@ -233,9 +233,9 @@ /obj/effect/proc_holder/spell/vampire/rally_thralls - name = "Rally Thralls" - desc = "Removes all incapacitating effects from your nearby thralls." - gain_desc = "You have gained the ability to remove all incapacitating effects from nearby thralls." + name = "Сплотить рабов" + desc = "Снимает все обездвиживающие эффекты с находящихся рядом с вами рабов." + gain_desc = "Вы получили способность снимать все обездвиживающие эффекты с ближайших рабов." action_icon_state = "thralls_up" required_blood = 40 base_cooldown = 30 SECONDS @@ -264,9 +264,9 @@ /obj/effect/proc_holder/spell/vampire/self/share_damage - name = "Blood Bond" - desc = "Creates a net between you and your nearby thralls that evenly shares all damage received." - gain_desc = "You have gained the ability to share damage between you and your thralls." + name = "Кровавые узы" + desc = "Создает сеть между вами и ближайшими рабами, которая равномерно распределяет весь получаемый урон." + gain_desc = "Вы получили способность распределять урон между вами и вашими рабами." action_icon_state = "blood_bond" required_blood = 5 @@ -280,9 +280,9 @@ /obj/effect/proc_holder/spell/vampire/hysteria - name = "Mass Hysteria" - desc = "Casts a powerful illusion to make everyone nearby perceive others to looks like random animals after briefly blinding them. Also slows affected victims." - gain_desc = "You have gained the ability to make everyone nearby perceive others to looks like random animals after briefly blinding them." + name = "Массовая истерия" + desc = "Накладывает мощную иллюзию, заставляющую всех, кто находится поблизости, воспринимать окружающих как случайных животных после кратковременного ослепления. Также замедляет пострадавших." + gain_desc = "Вы получили способность заставлять всех, кто находится рядом, воспринимать окружающих как случайных животных после кратковременного ослепления." action_icon_state = "hysteria" required_blood = 40 base_cooldown = 60 SECONDS diff --git a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm index 2e4ecace710..d8c705e9b07 100644 --- a/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/gargantua_powers.dm @@ -1,7 +1,7 @@ /obj/effect/proc_holder/spell/vampire/self/blood_swell - name = "Blood Swell" - desc = "You infuse your body with blood, making you highly resistant to stuns and physical damage. However, this makes you unable to fire ranged weapons while it is active." - gain_desc = "You have gained the ability to temporarly resist large amounts of stuns and physical damage." + name = "Кровавый вал" + desc = "Вы наполняете своё тело кровью, что делает вас очень устойчивым к оглушению и физическому урону, но не даёт использовать оружие дальнего боя." + gain_desc = "Вы получили способность временно повышать свою сопротивляемость урону и оглушению." base_cooldown = 40 SECONDS required_blood = 30 action_icon_state = "blood_swell" @@ -15,13 +15,13 @@ /datum/vampire_passive/blood_swell_upgrade - gain_desc = "While blood swell is active all of your melee attacks deal increased damage." + gain_desc = "Пока действует «Кровавый вал», все ваши атаки в ближнем бою наносят повышенный урон." /obj/effect/proc_holder/spell/vampire/self/stomp - name = "Seismic Stomp" - desc = "You slam your foot into the ground sending a powerful shockwave through the station's hull, sending people flying away. Cannot be cast if you legs are impared by a bola or similar." - gain_desc = "You have gained the ability to knock people back using a powerful stomp." + name = "Ударная волна" + desc = "Вы бьёте ногой по земле, посылая мощную ударную волну, отчего окружающие разлетаются в разные стороны. Не может быть применено, если ваши ноги скованы или обездвижены." + gain_desc = "Вы получили способность отбрасывать людей назад, используя мощный топот." action_icon_state = "seismic_stomp" base_cooldown = 30 SECONDS required_blood = 25 @@ -87,31 +87,31 @@ /obj/effect/proc_holder/spell/vampire/self/overwhelming_force - name = "Overwhelming Force" - desc = "When toggled you will automatically pry open doors that you bump into if you do not have access. Also deflects any thrown bola." - gain_desc = "You have gained the ability to force open doors and deflect bola at a small blood cost." + name = "Неудержимая сила" + desc = "При активации вы будете выбивать все шлюзы, на которые наткнётесь, если у вас нет доступа, а также отражать все обездвиживающие предметы." + gain_desc = "Вы получили способность выбивать двери и отражать обездвиживающие предметы за небольшую кровавую плату." base_cooldown = 2 SECONDS action_icon_state = "OH_YEAAAAH" /obj/effect/proc_holder/spell/vampire/self/overwhelming_force/cast(list/targets, mob/user) if(!HAS_TRAIT_FROM(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT)) - to_chat(user, span_userdanger("You feel MIGHTY!")) + to_chat(user, span_userdanger("ВЫ ЧУВСТВУЕТЕ СЕБЯ СИЛЬНЕЕ!")) ADD_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT) user.status_flags &= ~CANPUSH user.move_resist = MOVE_FORCE_STRONG else - to_chat(user, span_warning("You feel weaker...")) + to_chat(user, span_warning("Вы чувствуете себя слабее...")) REMOVE_TRAIT(user, TRAIT_FORCE_DOORS, VAMPIRE_TRAIT) user.move_resist = MOVE_FORCE_DEFAULT user.status_flags |= CANPUSH /obj/effect/proc_holder/spell/vampire/self/blood_rush - name = "Blood Rush" - desc = "Infuse yourself with blood magic to boost your movement speed." - gain_desc = "You have gained the ability to temporarily move at high speeds." + name = "Кровавый драйв" + desc = "Напитайте себя магией крови, чтобы увеличить скорость передвижения." + gain_desc = "Вы получили способность временно перемещаться с большой скоростью." base_cooldown = 30 SECONDS required_blood = 15 action_icon_state = "blood_rush" @@ -121,19 +121,19 @@ var/mob/living/target = targets[1] if(ishuman(target)) var/mob/living/carbon/human/H = target - to_chat(H, span_notice("You feel a rush of energy!")) + to_chat(H, span_notice("Вы ощущаете прилив энергии!")) H.apply_status_effect(STATUS_EFFECT_BLOOD_RUSH) /obj/effect/proc_holder/spell/fireball/demonic_grasp - name = "Demonic Grasp" - desc = "Fire a hand of demonic energy, snaring and throwing its target around, based on your intent. Disarm pushes, grab pulls." - gain_desc = "You have gained the ability to snare and disrupt people with demonic apendages." + name = "Демоническая хватка" + desc = "Выстрелите сгустком демонической энергии, захватывая или отбрасывая цель в зависимости от вашего намерения: «ОБЕЗОРУЖИТЬ» — оттолкнуть, «СХВАТИТЬ» — притянуть." + gain_desc = "Вы получили способность притягивать и отталкивать людей с помощью демонических отростков." base_cooldown = 15 SECONDS fireball_type = /obj/item/projectile/magic/demonic_grasp - selection_activated_message = span_notice("You raise your hand, full of demonic energy! Left-click to cast at a target!") - selection_deactivated_message = span_notice("You re-absorb the energy...for now.") + selection_activated_message = span_notice("Вы поднимаете руку, полную демонической энергии!") + selection_deactivated_message = span_notice("Вы возвращаете себе энергию... пока что.") action_icon_state = "demonic_grasp" @@ -161,6 +161,14 @@ /obj/item/projectile/magic/demonic_grasp name = "demonic grasp" + ru_names = list( + NOMINATIVE = "демоническая хватка", + GENITIVE = "демонической хватки", + DATIVE = "демонической хватке", + ACCUSATIVE = "демоническую хватку", + INSTRUMENTAL = "демонической хваткой", + PREPOSITIONAL = "демонической хватке" + ) // parry this you filthy casual reflectability = REFLECTABILITY_NEVER icon_state = null @@ -210,9 +218,9 @@ /obj/effect/proc_holder/spell/vampire/charge - name = "Charge" - desc = "You charge at wherever you click on screen, dealing large amounts of damage, stunning and destroying walls and other objects." - gain_desc = "You can now charge at a target on screen, dealing massive damage and destroying structures." + name = "Рывок" + desc = "Вы резко бросаетесь в выбранное направление, нанося огромный урон, оглушая и разрушая стены и другие объекты." + gain_desc = "Теперь вы можете произвести рывок, нанося огромный урон и разрушая объекты." required_blood = 30 base_cooldown = 30 SECONDS action_icon_state = "vampire_charge" diff --git a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm index f80c00c1d63..d1fbf10ee05 100644 --- a/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/hemomancer_powers.dm @@ -1,7 +1,7 @@ /obj/effect/proc_holder/spell/vampire/self/vamp_claws - name = "Vampiric Claws" - desc = "You channel blood magics to forge deadly vampiric claws that leech blood and strike rapidly. Cannot be used if you are holding something that cannot be dropped." - gain_desc = "You have gained the ability to forge your hands into vampiric claws." + name = "Когти" + desc = "Вы используете магию крови, чтобы выковать смертоносные вампирские когти, которые высасывают кровь и наносят стремительные удары. Их нельзя использовать, если вы держите что-то, что нельзя уронить." + gain_desc = "Вы получили способность превращать свои руки в вампирские когти." base_cooldown = 15 SECONDS required_blood = 20 action_icon_state = "vampire_claws" @@ -9,11 +9,11 @@ /obj/effect/proc_holder/spell/vampire/self/vamp_claws/cast(mob/user) if(user.l_hand || user.r_hand) - to_chat(user, span_notice("You drop what was in your hands as large blades spring from your fingers!")) + to_chat(user, span_notice("Вы роняете то, что было у вас в руках, и из ваших пальцев вылетают огромные лезвия!")) user.drop_l_hand() user.drop_r_hand() else - to_chat(user, span_notice("Large blades of blood spring from your fingers!")) + to_chat(user, span_notice("Из ваших пальцев брызжет кровь!")) var/obj/item/twohanded/required/vamp_claws/claws = new /obj/item/twohanded/required/vamp_claws(user.loc, src) RegisterSignal(user, COMSIG_MOB_KEY_DROP_ITEM_DOWN, PROC_REF(dispel)) user.put_in_hands(claws) @@ -35,7 +35,7 @@ if(current) qdel(current) - to_chat(user, span_notice("You dispel your claws!")) + to_chat(user, span_notice("Вы рассеиваете когти!")) return COMPONENT_CANCEL_DROP @@ -47,7 +47,15 @@ /obj/item/twohanded/required/vamp_claws name = "vampiric claws" - desc = "A pair of eldritch claws made of living blood, they seem to flow yet they are solid" + desc = "Пара древних когтей из живой крови, они кажутся текучими и в то же время твердыми." + ru_names = list( + NOMINATIVE = "вампирические когти", + GENITIVE = "вампирических когтей", + DATIVE = "вампирическим когтям", + ACCUSATIVE = "вампирические когти", + INSTRUMENTAL = "вампирическими когтями", + PREPOSITIONAL = "вампирических когтях" + ) icon = 'icons/effects/vampire_effects.dmi' icon_state = "vamp_claws" w_class = WEIGHT_CLASS_BULKY @@ -59,7 +67,7 @@ attack_speed = 0.4 SECONDS attack_effect_override = ATTACK_EFFECT_CLAW hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut", "savaged", "clawed") + attack_verb = list("атаковал", "порезал", "уколол", "полоснул", "рубанул", "пронзил", "нарезал кубиками") sprite_sheets_inhand = list(SPECIES_VOX = 'icons/mob/clothing/species/vox/held.dmi', SPECIES_DRASK = 'icons/mob/clothing/species/drask/held.dmi') var/durability = 15 var/blood_drain_amount = 15 @@ -106,18 +114,18 @@ durability-- if(durability <= 0) qdel(src) - to_chat(user, span_warning("Your claws shatter!")) + to_chat(user, span_warning("Ваши когти сломаны!")) /obj/item/twohanded/required/vamp_claws/attack_self(mob/user) qdel(src) - to_chat(user, span_notice("You dispel your claws!")) + to_chat(user, span_notice("Вы рассеиваете когти!")) /obj/effect/proc_holder/spell/vampire/blood_tendrils - name = "Blood Tendrils" - desc = "You summon blood tendrils from bluespace after a delay to ensnare people in an area, slowing them down and applying moderate toxic damage." - gain_desc = "You have gained the ability to summon blood tendrils to slow people down in an area that you target." + name = "Кровавые щупальца" + desc = "Используя силу блюспейса, после небольшой задержки вы призываете кровавые щупальца, которые опутывают цели в зоне действия, замедляя их и нанося умеренный токсичный урон." + gain_desc = "Вы получили способность вызывать кровавые щупальца, чтобы замедлять людей в выбранной вами области." required_blood = 10 base_cooldown = 10 SECONDS @@ -126,8 +134,8 @@ var/area_of_affect = 1 need_active_overlay = TRUE - selection_activated_message = span_notice("You channel blood magics to weaken the bluespace veil. Left-click to cast at a target area!") - selection_deactivated_message = span_notice("Your magics subside.") + selection_activated_message = span_notice("Вы используете магию крови, чтобы ослабить завесу блюспейса.") + selection_deactivated_message = span_notice("Ваша магия ослабевает.") /obj/effect/proc_holder/spell/vampire/blood_tendrils/create_new_targeting() @@ -153,7 +161,7 @@ if(L.affects_vampire(user)) L.Slowed(slowed_amount) L.apply_damage(33, TOX) - L.visible_message(span_warning("[L] gets ensnare in blood tendrils, restricting [L.p_their()] movement!")) + L.visible_message(span_warning("[L] опутыва[pluralize_ru(L.gender, "ет", "ют")]ся кровавыми щупальцами, которые ограничивают [genderize_ru(L.gender, "его", "её", "его", "их")] движение!")) var/turf/target_turf = get_turf(L) playsound(target_turf, 'sound/magic/tail_swing.ogg', 50, TRUE) new /obj/effect/decal/cleanable/blood(target_turf) @@ -171,9 +179,9 @@ /obj/effect/proc_holder/spell/vampire/blood_barrier - name = "Blood Barrier" - desc = "Select two points within 3 tiles of each other and make a barrier between them. You can cast on self to make a barrier on your current position instantly." - gain_desc = "You have gained the ability to summon a crystaline wall of blood between two points, the barrier is easily destructable, however you can walk freely through it. You can cast on self to make a barrier on your current position instantly." + name = "Кровавый барьер" + desc = "Выберите две точки в пределах трёх тайлов друг от друга и создайте между ними барьер. Вы можете наложить заклинание на себя, чтобы мгновенно создать барьер на вашей текущей позиции." + gain_desc = "Вы получили способность вызывать кристаллическую стену крови между двумя точками, барьер легко разрушается, однако вы можете свободно проходить сквозь него. Вы можете наложить на себя заклинание, чтобы мгновенно создать барьер на вашем текущем местоположении." required_blood = 20 base_cooldown = 30 SECONDS should_recharge_after_cast = FALSE @@ -237,7 +245,7 @@ // Otherwise we will try to build a wall by two clicks if(target_turf == start_turf) - to_chat(user, span_notice("You deselect the targeted turf.")) + to_chat(user, span_notice("Вы убираете пометку с тайла.")) start_turf = null should_recharge_after_cast = FALSE return @@ -264,7 +272,15 @@ /obj/structure/blood_barrier name = "blood barrier" - desc = "a grotesque structure of crystalised blood. It's slowly melting away..." + desc = "Гротескная структура из кристаллизованной крови. Она медленно тает..." + ru_names = list( + NOMINATIVE = "кровавый барьер", + GENITIVE = "кровавого барьера", + DATIVE = "кровавому барьеру", + ACCUSATIVE = "кровавый барьер", + INSTRUMENTAL = "кровавым барьером", + PREPOSITIONAL = "о кровавом барьере" + ) max_integrity = 100 icon_state = "blood_barrier" icon = 'icons/effects/vampire_effects.dmi' @@ -313,9 +329,9 @@ /obj/effect/proc_holder/spell/ethereal_jaunt/blood_pool - name = "Sanguine Pool" - desc = "You shift your form into a pool of blood, making you invulnerable and able to move through anything that's not a wall or space. You leave a trail of blood behind you when you do this." - gain_desc = "You have gained the ability to shift into a pool of blood, allowing you to evade pursuers with great mobility." + name = "Погружение в кровь" + desc = "Вы превращаете свою форму в лужу крови, делая ее неуязвимой и способной перемещаться сквозь всё, что не является стеной или космосом. После этого за вами остаётся кровавый след." + gain_desc = "Вы получили способность превращаться в лужу крови, что позволяет вам уходить от преследователей с большой мобильностью." jaunt_duration = 8 SECONDS clothes_req = FALSE school = "vampire" @@ -342,9 +358,9 @@ /obj/effect/proc_holder/spell/vampire/predator_senses - name = "Predator Senses" - desc = "Hunt down your prey, there's nowhere to hide... Will stun for a short perion if in view." - gain_desc = "Your senses are heightened, nobody can hide from you now." + name = "Чутьё хищника" + desc = "Выслеживайте свою добычу, здесь ей негде спрятаться... На короткое время оглушает её, если она окажется в вашем поле зрения." + gain_desc = "Ваши чувства обострились, теперь никто не сможет от вас спрятаться." action_icon_state = "predator_sense" base_cooldown = 10 SECONDS create_attack_logs = FALSE @@ -366,14 +382,14 @@ for(var/mob/living/carbon/human/H as anything in targets) targets_by_name[H.real_name] = H - var/target_name = input(user, "Person to Locate", "Blood Stench") in targets_by_name + var/target_name = input(user, "Лицо для поиска", "Запах крови") in targets_by_name if(!target_name) return var/mob/living/carbon/human/target = targets_by_name[target_name] - var/message = "[target_name] is in [get_area(target)], [dir2text(get_dir(user, target))] from you." + var/message = "[target_name] наход[pluralize_ru(target_name, "ит", "ят")]ся в локации [get_area(target)], на [dir2rustext(get_dir(user, target))]е от вас." if(target.get_damage_amount() >= 40 || target.bleed_rate) - message += " They are wounded..." + message += " Цель ранена..." to_chat(user, span_cultlarge("[message]")) if(target in view(user)) @@ -384,9 +400,9 @@ /obj/effect/proc_holder/spell/vampire/blood_eruption - name = "Blood Eruption" - desc = "Every pool of blood in 4 tiles erupts with a spike of living blood, damaging anyone stood on it." - gain_desc = "You have gained the ability to weaponise pools of blood to damage those stood on them." + name = "Извержение крови" + desc = "Каждая лужа крови в 4 тайлах от вас извергается шипом живой крови, нанося урон всем, кто стоит на ней." + gain_desc = "Вы получили способность использовать лужи крови для нанесения урона тем, кто на них стоит." required_blood = 50 base_cooldown = 1 MINUTES action_icon_state = "blood_spikes" @@ -416,7 +432,7 @@ playsound(L, 'sound/misc/demon_attack1.ogg', 50, TRUE) L.apply_damage(50, BRUTE, BODY_ZONE_CHEST) L.Stun(3 SECONDS) - L.visible_message(span_warning("[L] gets impaled by a spike of living blood!")) + L.visible_message(span_warning("[L] пронзен[genderize_ru(L.gender, "", "а", "о", "ы")] шипом живой крови!")) /obj/effect/temp_visual/blood_spike @@ -426,9 +442,9 @@ /obj/effect/proc_holder/spell/vampire/self/blood_spill - name = "The Blood Bringers Rite" - desc = "When toggled, everyone around you begins to bleed profusely. You will drain their blood and rejuvenate yourself with it." - gain_desc = "You have gained the ability to rip the very life force out of people and absorb it, healing you." + name = "Кровавый обряд" + desc = "При переключении все вокруг начнут обильно кровоточить. Вы будете поглощать их кровь и напитываться силой." + gain_desc = "Вы обрели способность извлекать жизненную силу из гуманоидов и поглощать её, исцеляя себя." base_cooldown = 10 SECONDS action_icon_state = "blood_bringers_rite" required_blood = 10 @@ -480,7 +496,7 @@ owner.AdjustStunned(-2 SECONDS) owner.AdjustWeakened(-2 SECONDS) if(drain_amount == 10) - to_chat(H, span_warning("You feel your life force draining!")) + to_chat(H, span_warning("Вы чувствуете, как из вас утекает жизненная сила!")) if(beam_number >= max_beams) break diff --git a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm index 64162d25520..7afdb5e75f3 100644 --- a/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/umbrae_powers.dm @@ -1,7 +1,7 @@ /obj/effect/proc_holder/spell/vampire/self/cloak - name = "Cloak of Darkness" - desc = "Toggles whether you are currently cloaking yourself in darkness. When in darkness and toggled on, you move at increased speeds." - gain_desc = "You have gained the Cloak of Darkness ability, which when toggled makes you nearly invisible and highly agile in the shroud of darkness." + name = "Покров тьмы" + desc = "Включает или выключает маскировку в темноте. Если вы замаскированы и находитесь в темноте, то ваша скорость увеличивается." + gain_desc = "Теперь вы можете маскировать себя во тьме, становясь почти невидимым и чрезвычайно проворным." action_icon_state = "vampire_cloak" base_cooldown = 2 SECONDS @@ -11,7 +11,7 @@ if(!V) return - var/new_name = "[initial(name)] ([V.iscloaking ? "Deactivate" : "Activate"])" + var/new_name = "[initial(name)] ([V.iscloaking ? "Деактивировать" : "Активировать"])" name = new_name action?.name = new_name action?.UpdateButtonIcon() @@ -30,7 +30,7 @@ H.physiology.burn_mod /= 1.3 update_vampire_spell_name(user) - to_chat(user, span_notice("You will now be [V.iscloaking ? "hidden" : "seen"] in darkness.")) + to_chat(user, span_notice("Теперь вы будете [V.iscloaking ? "скрыты" : "видимы"] в темноте.")) /mob/living/proc/update_vampire_cloak() @@ -40,9 +40,9 @@ /obj/effect/proc_holder/spell/vampire/shadow_snare - name = "Shadow Snare" - desc = "You summon a trap on the ground. When crossed it will blind the target, extinguish any lights they may have, and ensnare them." - gain_desc = "You have gained the ability to summon a trap that will blind, ensnare, and turn off the lights of anyone who crosses it." + name = "Теневая ловушка" + desc = "Вы вызываете ловушку на земле. Когда её пересекут, она ослепит цель, погасит все имеющиеся у неё источники света и захватит её в капкан." + gain_desc = "Вы получили способность вызывать ловушку, которая ослепит, захватит в капкан и выключит свет любому, кто пересечет ее." base_cooldown = 10 SECONDS required_blood = 20 action_icon_state = "shadow_snare" @@ -63,7 +63,15 @@ /obj/item/restraints/legcuffs/beartrap/shadow_snare name = "shadow snare" - desc = "An almost transparent trap that melts into the shadows." + desc = "Почти прозрачная ловушка, которая тает в тени." + ru_names = list( + NOMINATIVE = "теневая ловушка", + GENITIVE = "теневой ловушки", + DATIVE = "теневой ловушке", + ACCUSATIVE = "теневую ловушку", + INSTRUMENTAL = "теневой ловушкой", + PREPOSITIONAL = "теневой ловушке" + ) alpha = 60 armed = TRUE anchored = TRUE @@ -88,7 +96,7 @@ obj_integrity -= 50 if(obj_integrity <= 0) - visible_message(span_notice("[src] withers away.")) + visible_message(span_notice("[capitalize(declent_ru(NOMINATIVE))] исчезает.")) qdel(src) @@ -119,7 +127,7 @@ /obj/item/restraints/legcuffs/beartrap/shadow_snare/attack_tk(mob/user) if(iscarbon(user)) var/mob/living/carbon/C = user - to_chat(user, span_userdanger("The snare sends a psychic backlash!")) + to_chat(user, span_userdanger("Ловушка посылает обратную связь с помощью психического сигнала!")) C.EyeBlind(20 SECONDS) @@ -130,16 +138,16 @@ . |= ATTACK_CHAIN_BLOCKED_ALL user.do_attack_animation(src) user.visible_message( - span_danger("[user] points [I] at [src] and it withers away!"), - span_danger("You point [I] at [src] and it withers away!"), + span_danger("[user] навод[pluralize_ru(user.gender, "ит", "ят")] [I] на [declent_ru(ACCUSATIVE)], и она исчезает!"), + span_danger("Наведите [I] на [declent_ru(ACCUSATIVE)], и она исчезнет!"), ) qdel(src) /obj/effect/proc_holder/spell/vampire/soul_anchor - name = "Soul Anchor" - desc = "You summon a dimenional anchor after a delay, casting again will teleport you back to the anchor. You are forced back after 2 minutes if you have not cast again." - gain_desc = "You have gained the ability to save a point in space and teleport back to it at will. Unless you willingly teleport back to that point within 2 minutes, you are forced back." + name = "Теневой якорь" + desc = "Вы вызываете затемнённый якорь после задержки, повторное заклинание телепортирует вас обратно к якорю. Вы будете вынуждены вернуться назад через 2 минуты, если не произнесли повторное заклинание." + gain_desc = "Вы получили способность сохранять точку в пространстве и телепортироваться к ней по своему желанию. Если в течение 2 минут вы самостоятельно не телепортируетесь обратно в эту точку, вас телепортирует автоматически." required_blood = 30 centcom_cancast = FALSE base_cooldown = 130 SECONDS @@ -159,7 +167,7 @@ /obj/effect/proc_holder/spell/vampire/soul_anchor/cast(list/targets, mob/user) if(making_anchor) // second cast, but we are impatient - to_chat(user, span_notice("Your anchor isn't ready yet!")) + balloon_alert(user, "якорь не готов!") return if(!making_anchor && !anchor) // first cast, setup the anchor @@ -222,7 +230,15 @@ // an indicator that shows where the vampire will land /obj/structure/shadow_anchor name = "shadow anchor" - desc = "Looking at this thing makes you feel uneasy..." + desc = "При взгляде на эту штуку вам становится не по себе..." + ru_names = list( + NOMINATIVE = "теневой якорь", + GENITIVE = "теневого якоря", + DATIVE = "теневому якорю", + ACCUSATIVE = "теневой якорь", + INSTRUMENTAL = "теневым якорем", + PREPOSITIONAL = "теневом якоре" + ) icon = 'icons/obj/cult.dmi' icon_state = "pylon" alpha = 120 @@ -234,9 +250,9 @@ /obj/effect/proc_holder/spell/vampire/dark_passage - name = "Dark Passage" - desc = "You teleport to a targeted turf." - gain_desc = "You have gained the ability to blink a short distance towards a targeted turf." + name = "Шаг в тень" + desc = "Вы телепортируетесь на указанную площадку." + gain_desc = "Вы получили способность совершать молниеносный бросок на небольшое расстояние в сторону указанной площадки." base_cooldown = 15 SECONDS required_blood = 30 centcom_cancast = FALSE @@ -272,9 +288,9 @@ /obj/effect/proc_holder/spell/vampire/vamp_extinguish - name = "Extinguish" - desc = "You extinguish any light source in an area around you." - gain_desc = "You have gained the ability to extinguish nearby light sources." + name = "Погасить" + desc = "Вы гасите любой источник света в области вокруг себя." + gain_desc = "Вы получили способность гасить ближайшие источники света." base_cooldown = 30 SECONDS action_icon_state = "vampire_extinguish" create_attack_logs = FALSE @@ -294,9 +310,9 @@ /obj/effect/proc_holder/spell/vampire/shadow_boxing - name = "Shadow Boxing" - desc = "Target someone to have your shadow beat them up. You must stay within 2 tiles for this to work." - gain_desc = "You have gained the ability to make your shadow fight for you." + name = "Бой с тенью" + desc = "Нацельтесь на кого-нибудь, чтобы ваша тень избила его. Чтобы это сработало, вы должны находиться в пределах двух тайлов." + gain_desc = "Теперь вы можете заставить свою тень сражаться бок о бок с вами." base_cooldown = 30 SECONDS action_icon_state = "shadow_boxing" required_blood = 50 @@ -318,9 +334,9 @@ /obj/effect/proc_holder/spell/vampire/self/eternal_darkness - name = "Eternal Darkness" - desc = "When toggled, you shroud the area around you in darkness and slowly lower the body temperature of people nearby." - gain_desc = "You have gained the ability to shroud the area around you in darkness, only the strongest of lights can pierce your unholy powers." + name = "Вечная тьма" + desc = "При включении вы окутываете пространство вокруг себя темнотой и медленно понижаете температуру тела находящихся рядом гуманоидов." + gain_desc = "Вы обрели способность окутывать всё вокруг себя тьмой. Только сильнейший свет сможет пронзить вашу нечестивую силу." base_cooldown = 10 SECONDS action_icon_state = "eternal_darkness" required_blood = 5 @@ -339,7 +355,7 @@ /datum/vampire_passive/eternal_darkness - gain_desc = "You surround yourself in a unnatural darkness, freezing those around you." + gain_desc = "Вы окружаете себя неестественной тьмой, замораживая окружающих." /datum/vampire_passive/eternal_darkness/New() @@ -367,5 +383,5 @@ /datum/vampire_passive/xray - gain_desc = "You can now see through walls, incase you hadn't noticed." + gain_desc = "Теперь вы можете видеть сквозь стены, если вы не заметили." diff --git a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm index 96381501944..0f1de9b832b 100644 --- a/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm +++ b/code/modules/antagonists/vampire/vampire_powers/vampire_powers.dm @@ -89,8 +89,8 @@ /obj/effect/proc_holder/spell/vampire/self/rejuvenate - name = "Rejuvenate" - desc = "Use reserve blood to enliven your body, removing any incapacitating effects." + name = "Восстановление" + desc = "Наполните своё тело резервной кровью, чтобы снять с себя любые обездвиживающие эффекты." action_icon_state = "vampire_rejuvinate" base_cooldown = 20 SECONDS stat_allowed = UNCONSCIOUS @@ -106,7 +106,7 @@ user.adjustStaminaLoss(-100) user.set_resting(FALSE, instant = TRUE) user.get_up(instant = TRUE) - to_chat(user, span_notice("You instill your body with clean blood and remove any incapacitating effects.")) + to_chat(user, span_notice("Вы наполняете свое тело чистой кровью и снимаете все обездвиживающие эффекты.")) var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) var/rejuv_bonus = V.get_rejuv_bonus() if(rejuv_bonus) @@ -139,9 +139,9 @@ /obj/effect/proc_holder/spell/vampire/self/specialize - name = "Choose Specialization" - desc = "Choose what sub-class of vampire you want to evolve into." - gain_desc = "You can now choose what specialization of vampire you want to evolve into." + name = "Выбрать специализацию" + desc = "Выберите, каким подклассом вампира вы хотите стать." + gain_desc = "Теперь вы можете выбрать, в какую специализацию вампира вы хотите эволюционировать." base_cooldown = 2 SECONDS action_icon_state = "select_class" @@ -155,10 +155,19 @@ /obj/effect/proc_holder/spell/vampire/self/specialize/ui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "VampireSpecMenu", "Specialisation Menu") + ui = new(user, src, "VampireSpecMenu", "Меню выбора специализации") ui.set_autoupdate(FALSE) ui.open() +/obj/effect/proc_holder/spell/vampire/self/specialize/ui_static_data(mob/user) + var/list/data = list() + data["hemomancer"] = list('icons/misc/vampire_tgui.dmi', "hemomancer") + data["umbrae"] = list('icons/misc/vampire_tgui.dmi', "umbrae") + data["gargantua"] = list('icons/misc/vampire_tgui.dmi', "gargantua") + data["dantalion"] = list('icons/misc/vampire_tgui.dmi', "dantalion") + data["bestia"] = list('icons/misc/vampire_tgui.dmi', "bestia") + + return data /obj/effect/proc_holder/spell/vampire/self/specialize/ui_data(mob/user) var/datum/antagonist/vampire/vamp = user.mind.has_antag_datum(/datum/antagonist/vampire) @@ -210,8 +219,8 @@ /obj/effect/proc_holder/spell/vampire/glare - name = "Glare" - desc = "Your eyes flash, stunning and silencing anyone in front of you. It has lesser effects for those around you." + name = "Вспышка" + desc = "Ваши глаза вспыхивают, ошеломляя и заставляя замолчать всех, кто находится прямо перед вами. В меньшей степени действует на окружающих вне вашего поля зрения." action_icon_state = "vampire_glare" base_cooldown = 30 SECONDS stat_allowed = UNCONSCIOUS @@ -247,11 +256,11 @@ if(ishuman(user) && istype(user.glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) var/obj/item/clothing/glasses/sunglasses/blindfold/blindfold = user.glasses if(blindfold.tint) - to_chat(user, span_warning("You're blindfolded!")) + balloon_alert(user, "ваши глаза закрыты!") return user.mob_light(LIGHT_COLOR_BLOOD_MAGIC, _range = 3, _duration = 0.2 SECONDS) - user.visible_message(span_warning("[user]'s eyes emit a blinding flash!")) + user.visible_message(span_warning("Глаза [user] испускают ослепительную вспышку!")) for(var/mob/living/target as anything in targets) var/deviation @@ -277,7 +286,7 @@ target.AdjustSilence(8 SECONDS) target.flash_eyes(1, TRUE, TRUE) - to_chat(target, span_warning("You are blinded by [user]'s glare.")) + to_chat(target, span_warning("Вы ослеплены взглядом [user].")) add_attack_logs(user, target, "(Vampire) Glared at") @@ -317,8 +326,8 @@ /obj/effect/proc_holder/spell/vampire/raise_vampires - name = "Raise Vampires" - desc = "Summons deadly vampires from bluespace." + name = "Возвышение вампиров" + desc = "Призывает смертоносных вампиров из блюспейса." school = "transmutation" clothes_req = FALSE human_req = TRUE @@ -328,7 +337,7 @@ cooldown_min = 2 SECONDS action_icon_state = "revive_thrall" sound = 'sound/magic/wandodeath.ogg' - gain_desc = "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed." + gain_desc = "Вы получили способность «Возвышение вампиров». Эта чрезвычайно мощная АОЕ-способность действует на всех людей рядом с вами. Вампиры/стражи исцеляются. Трупы воскрешаются как вампиры. Другие люди оглушаются, получают повреждения мозга, а затем погибают." /obj/effect/proc_holder/spell/vampire/raise_vampires/create_new_targeting() @@ -340,7 +349,7 @@ /obj/effect/proc_holder/spell/vampire/raise_vampires/cast(list/targets, mob/user = usr) new /obj/effect/temp_visual/cult/sparks(user.loc) var/turf/T = get_turf(user) - to_chat(user, span_warning("You call out within bluespace, summoning more vampiric spirits to aid you!")) + to_chat(user, span_warning("Вы взываете к блюспейсу, призывая на помощь ещё больше вампирических духов!")) for(var/mob/living/carbon/human/H in targets) T.Beam(H, "sendbeam", 'icons/effects/effects.dmi', time = 30, maxdistance = 7, beam_type = /obj/effect/ebeam) new /obj/effect/temp_visual/cult/sparks(H.loc) @@ -351,13 +360,13 @@ if(!istype(M) || !istype(H)) return if(!H.mind) - visible_message("[H] looks to be too stupid to understand what is going on.") + visible_message("Похоже, [H] слишком глуп[genderize_ru(H.gender, "", "а", "о", "ы")], чтобы понять, что происходит.") return if(HAS_TRAIT(H, TRAIT_NO_BLOOD) || HAS_TRAIT(H, TRAIT_EXOTIC_BLOOD) || !H.blood_volume) - visible_message("[H] looks unfazed!") + visible_message("[H] выгляд[pluralize_ru(H.gender, "ит", "ят")] невозмутимым!") return if(H.mind.has_antag_datum(/datum/antagonist/vampire) || H.mind.special_role == SPECIAL_ROLE_VAMPIRE || H.mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL) - visible_message(span_notice("[H] looks refreshed!")) + visible_message(span_notice("[H] выгляд[pluralize_ru(H.gender, "ит", "ят")] посвежевшим!")) H.heal_overall_damage(60, 60, affect_robotic = TRUE) for(var/obj/item/organ/external/bodypart as anything in H.bodyparts) if(prob(25)) @@ -367,10 +376,10 @@ return if(H.stat != DEAD) if(H.IsWeakened()) - visible_message(span_warning("[H] looks to be in pain!")) + visible_message(span_warning("[H], похоже, испытыва[pluralize_ru(H.gender, "ет", "ют")] боль!")) H.apply_damage(60, BRAIN) else - visible_message(span_warning("[H] looks to be stunned by the energy!")) + visible_message(span_warning("Похоже, что [H] ошеломлен[genderize_ru(H.gender, "", "а", "о", "ы")] энергией!")) H.Weaken(40 SECONDS) return for(var/obj/item/implant/mindshield/L in H) @@ -379,11 +388,11 @@ for(var/obj/item/implant/traitor/T in H) if(T && T.implanted) qdel(T) - visible_message(span_warning("[H] gets an eerie red glow in their eyes!")) + visible_message(span_warning("У [H] появля[pluralize_ru(H.gender, "ет", "ют")]ся жуткое красное свечение в глазах!")) var/datum/objective/protect/protect_objective = new protect_objective.owner = H.mind protect_objective.target = M.mind - protect_objective.explanation_text = "Protect [M.real_name]." + protect_objective.explanation_text = "Защитите [M.real_name]." H.mind.objectives += protect_objective add_attack_logs(M, H, "Vampire-sired") H.mind.make_vampire() diff --git a/code/modules/antagonists/vampire/vampire_subclasses.dm b/code/modules/antagonists/vampire/vampire_subclasses.dm index 0884168f95c..1010c636ffe 100644 --- a/code/modules/antagonists/vampire/vampire_subclasses.dm +++ b/code/modules/antagonists/vampire/vampire_subclasses.dm @@ -75,7 +75,7 @@ /obj/effect/proc_holder/spell/vampire/self/blood_spill) /datum/vampire_subclass/hemomancer/on_blood_sucking(mob/living/carbon/human/H) - H.blood_volume = min(H.blood_volume + 5, BLOOD_VOLUME_NORMAL) + H.setBlood(min(H.blood_volume + 5, BLOOD_VOLUME_NORMAL)) /datum/vampire_subclass/gargantua name = "gargantua" diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 644e9216730..377d2206629 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -71,16 +71,19 @@ /obj/item/assembly_holder/proc/process_activation(obj/D, normal = TRUE, special = TRUE, mob/user) if(!D) return FALSE + if(normal && a_right && a_left) if(a_right != D) a_right.pulsed() if(a_left != D) a_left.pulsed() + if(master) var/datum/signal/signal = new signal.source = src signal.user = user master.receive_signal(signal) + return TRUE diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm index a508459a9ef..5e94a853401 100644 --- a/code/modules/asset_cache/asset_list.dm +++ b/code/modules/asset_cache/asset_list.dm @@ -641,4 +641,34 @@ GLOBAL_LIST_EMPTY(asset_datums) /datum/asset/simple/namespaced/proc/get_htmlloader(filename) return URL2HTMLLOADER(SSassets.transport.get_asset_url(filename, assets[filename])) + +/// A subtype to generate a JSON file from a list +/datum/asset/json + _abstract = /datum/asset/json + /// The filename, will be suffixed with ".json" + var/name + + +/datum/asset/json/send(client) + return SSassets.transport.send_assets(client, "[name].json") + + +/datum/asset/json/get_url_mappings() + return list( + "[name].json" = SSassets.transport.get_asset_url("[name].json"), + ) + + +/datum/asset/json/register() + var/filename = "data/[name].json" + fdel(filename) + text2file(json_encode(generate()), filename) + SSassets.transport.register_asset("[name].json", fcopy_rsc(filename)) + fdel(filename) + +/// Returns the data that will be JSON encoded +/datum/asset/json/proc/generate() + SHOULD_CALL_PARENT(FALSE) + CRASH("generate() not implemented for [type]!") + #undef ASSET_CROSS_ROUND_CACHE_DIRECTORY diff --git a/code/modules/asset_cache/assets/asset_icon_ref_map.dm b/code/modules/asset_cache/assets/asset_icon_ref_map.dm new file mode 100644 index 00000000000..4eef3398056 --- /dev/null +++ b/code/modules/asset_cache/assets/asset_icon_ref_map.dm @@ -0,0 +1,24 @@ +/// Maps icon names to ref values +/datum/asset/json/icon_ref_map + name = "icon_ref_map" + early = TRUE + +/datum/asset/json/icon_ref_map/generate() + var/list/data = list() //"icons/obj/drinks.dmi" => "[0xc000020]" + //var/start = "0xc000000" + var/value = 0 + while(TRUE) + value += 1 + var/ref = "\[0xc[num2text(value,6,16)]\]" + var/mystery_meat = locate(ref) + if(isicon(mystery_meat)) + if(!isfile(mystery_meat)) // Ignore the runtime icons for now + continue + var/path = get_icon_dmi_path(mystery_meat) //Try to get the icon path + if(path) + data[path] = ref + else if(mystery_meat) + continue; //Some other non-icon resource, ogg/json/whatever + else //Out of resources end this, could also try to end this earlier as soon as runtime generated icons appear but eh + break; + return data diff --git a/code/modules/asset_cache/assets/supplypods.dm b/code/modules/asset_cache/assets/supplypods.dm new file mode 100644 index 00000000000..3807c080f62 --- /dev/null +++ b/code/modules/asset_cache/assets/supplypods.dm @@ -0,0 +1,27 @@ +/datum/asset/spritesheet/supplypods + name = "supplypods" + +/datum/asset/spritesheet/supplypods/create_spritesheets() + for (var/datum/pod_style/style as anything in typesof(/datum/pod_style)) + if (ispath(style, /datum/pod_style/seethrough)) + Insert("pod_asset[style::id]", icon('icons/obj/supplypods.dmi' , "seethrough-icon")) + continue + var/base = style::icon_state + if (!base) + Insert("pod_asset[style::id]", icon('icons/obj/supplypods.dmi', "invisible-icon")) + continue + var/icon/podIcon = icon('icons/obj/supplypods.dmi', base) + var/door = style::has_door + if (door) + door = "[base]_door" + podIcon.Blend(icon('icons/obj/supplypods.dmi', door), ICON_OVERLAY) + var/shape = style::shape + if (shape == POD_SHAPE_NORMAL) + var/decal = style::decal_icon + if (decal) + podIcon.Blend(icon('icons/obj/supplypods.dmi', decal), ICON_OVERLAY) + var/glow = style::glow_color + if (glow) + glow = "pod_glow_[glow]" + podIcon.Blend(icon('icons/obj/supplypods.dmi', glow), ICON_OVERLAY) + Insert("pod_asset[style::id]", podIcon) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index d0d562f8cc7..e10c2ba63c9 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -349,7 +349,7 @@ if(hair_style) D.h_style = hair_style else - D.h_style = random_hair_style(gender, D.dna.species.name) + D.h_style = random_hair_style(gender, D.dna.species) D.hair_colour = rand_hex_color() if(facial_hair_style) D.f_style = facial_hair_style diff --git a/code/modules/awaymissions/mission_code/ruins/ussplaboratory.dm b/code/modules/awaymissions/mission_code/ruins/ussplaboratory.dm index ab5a4c7d973..bae929942c1 100644 --- a/code/modules/awaymissions/mission_code/ruins/ussplaboratory.dm +++ b/code/modules/awaymissions/mission_code/ruins/ussplaboratory.dm @@ -3,6 +3,7 @@ /area/ruin/ussp_xeno atmosalm = ATMOS_ALARM_NONE has_gravity = STANDARD_GRAVITY + area_flags = NONE /area/ruin/ussp_xeno/engi name = "Engineering" diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm index 7eff2cdae2f..4c8a01ac9d4 100644 --- a/code/modules/buildmode/buttons.dm +++ b/code/modules/buildmode/buttons.dm @@ -84,5 +84,6 @@ name = "Quit Buildmode" /atom/movable/screen/buildmode/quit/Click() + bd.mode.exit_mode(bd) // so area_edit won't leave highlighted icons bd.quit() return TRUE diff --git a/code/modules/buildmode/submodes/boom.dm b/code/modules/buildmode/submodes/boom.dm index 7c1b3da0305..1512bbc9ab2 100644 --- a/code/modules/buildmode/submodes/boom.dm +++ b/code/modules/buildmode/submodes/boom.dm @@ -8,20 +8,21 @@ var/flames = -1 /datum/buildmode_mode/boom/show_help(mob/user) - to_chat(user, "***********************************************************") - to_chat(user, "Mouse Button on obj = Kaboom") - to_chat(user, "***********************************************************") + to_chat(user, span_notice("***********************************************************")) + to_chat(user, span_notice("Кнопка мыши на объекте = Кабум")) + to_chat(user, span_notice("ПРИМЕЧАНИЕ. Использование кнопки «Event/Launch Supplypod» позволяет вам сделать в IC поле (т. е. заставить крылатую ракету упасть с неба и взорваться там, где вы щелкнете!)")) + to_chat(user, span_notice("***********************************************************")) /datum/buildmode_mode/boom/change_settings(mob/user) - devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null + devastation = tgui_input_number(usr, "Дальность тотального разрушения.", text("Ввод")) if(devastation == null) devastation = -1 - heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null + heavy = tgui_input_number(usr, "Дальность сильного удара.", text("Ввод")) if(heavy == null) heavy = -1 - light = input("Range of light impact. -1 to none", text("Input")) as num|null + light = tgui_input_number(usr, "Дальность легкого удара.", text("Ввод")) if(light == null) light = -1 - flash = input("Range of flash. -1 to none", text("Input")) as num|null + flash = tgui_input_number(usr, "Дальность вспышки.", text("Ввод")) if(flash == null) flash = -1 - flames = input("Range of flames. -1 to none", text("Input")) as num|null + flames = tgui_input_number(usr, "Дальность пламени.", text("Ввод")) if(flames == null) flames = -1 /datum/buildmode_mode/boom/handle_click(user, params, obj/object) diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm new file mode 100644 index 00000000000..b12dcd85381 --- /dev/null +++ b/code/modules/cargo/centcom_podlauncher.dm @@ -0,0 +1,888 @@ +#define TAB_POD 0 //Used to check if the UIs built in camera is looking at the pod +#define TAB_BAY 1 //Used to check if the UIs built in camera is looking at the launch bay area + +#define LAUNCH_ALL 0 //Used to check if we're launching everything from the bay area at once +#define LAUNCH_ORDERED 1 //Used to check if we're launching everything from the bay area in order +#define LAUNCH_RANDOM 2 //Used to check if we're launching everything from the bay area randomly + +//The Great and Mighty CentCom Pod Launcher - MrDoomBringer +//This was originally created as a way to get adminspawned items to the station in an IC manner. It's evolved to contain a few more +//features such as item removal, smiting, controllable delivery mobs, and more. + +//This works by creating a supplypod (refered to as temp_pod) in a special room in the centcom map. +//IMPORTANT: Even though we call it a supplypod for our purposes, it can take on the appearance and function of many other things: Eg. cruise missiles, boxes, or walking, living gondolas. +//When the user launched the pod, items from special "bays" on the centcom map are taken and put into the supplypod + +//The user can change properties of the supplypod using the UI, and change the way that items are taken from the bay (One at a time, ordered, random, etc) +//Many of the effects of the supplypod set here are put into action in supplypod.dm + + +//Variables declared to change how items in the launch bay are picked and launched. (Almost) all of these are changed in the ui_act proc +//Some effect groups are choices, while other are booleans. This is because some effects can stack, while others dont (ex: you can stack explosion and quiet, but you cant stack ordered launch and random launch) +/datum/centcom_podlauncher + /// Static typecache of atoms we won't lift up, or pod or whatever. + var/static/list/ignored_atoms = typecacheof(list( + null, // I don't know why null is the first element of this typepache but it was there when I found it + /mob/dead, + /turf, + /obj/effect/landmark, + /obj/docking_port, + /obj/machinery/light, + /obj/effect/particle_effect/sparks, + /obj/effect/pod_landingzone, + /obj/effect/client_image_holder, + )) + + var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back + var/client/holder //client of whoever is using this datum + var/area/centcom/supplypod/loading/bay //What bay we're using to launch shit from. + var/bayNumber //Quick reference to what bay we're in. Usually set to the loading_id variable for the related area type + var/customDropoff = FALSE + var/picking_dropoff_turf = FALSE + var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicate_object() and send the result + var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf. + var/launchChoice = LAUNCH_RANDOM //Determines if we launch all at once (0) , in order (1), or at random(2) + var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2) + var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2) + var/launcherActivated = FALSE //check if we've entered "launch mode" (when we click a pod is launched). Used for updating mouse cursor + var/effectBurst = FALSE //Effect that launches 5 at once in a 3x3 area centered on the target + var/effectAnnounce = TRUE + var/numTurfs = 0 //Counts the number of turfs with things we can launch in the chosen bay (in the centcom map) + var/launchCounter = 1 //Used with the "Ordered" launch mode (launchChoice = 1) to see what item is launched + var/atom/specificTarget //Do we want to target a specific mob instead of where we click? Also used for smiting + var/list/orderedArea = list() //Contains an ordered list of turfs in an area (filled in the createOrderedArea() proc), read top-left to bottom-right. Used for the "ordered" launch mode (launchChoice = 1) + var/list/turf/acceptableTurfs = list() //Contians a list of turfs (in the "bay" area on centcom) that have items that can be launched. Taken from orderedArea + var/list/launchList = list() //Contains whatever is going to be put in the supplypod and fired. Taken from acceptableTurfs + + /// An effect used for showing where a reverse pod will land + var/obj/effect/client_image_holder/dropoff_location/indicator + /// An effect used for keeping track of what item is going to be launched next when in "ordered" mode (launchChoice = 1) + var/obj/effect/client_image_holder/supplypod_selector/selector + + var/obj/structure/closet/supplypod/centcompod/temp_pod //The temporary pod that is modified by this datum, then cloned. The buildObject() clone of this pod is what is launched + // Stuff needed to render the map + var/map_name + var/atom/movable/screen/map_view/cam_screen + var/atom/movable/screen/background/cam_background + var/tabIndex = 1 + var/renderLighting = FALSE + var/static/list/pod_style_info + var/static/list/pod_style_lookup + +/datum/centcom_podlauncher/New(user) //user can either be a client or a mob + if (user) //Prevents runtimes on datums being made without clients + setup(user) + if (!isnull(pod_style_info)) + return + pod_style_info = list() + pod_style_lookup = list() + for (var/datum/pod_style/style as anything in typesof(/datum/pod_style)) + pod_style_info += list(list("id" = style::id, "title" = style::ui_name)) + pod_style_lookup[style::id] = style + +/datum/centcom_podlauncher/proc/setup(user) //H can either be a client or a mob + if (istype(user,/client)) + var/client/user_client = user + holder = user_client //if its a client, assign it to holder + else + var/mob/user_mob = user + holder = user_mob.client //if its a mob, assign the mob's client to holder + bay = locate(/area/centcom/supplypod/loading/one) in GLOB.areas //Locate the default bay (one) from the centcom map + bayNumber = bay.loading_id //Used as quick reference to what bay we're taking items from + var/area/pod_storage_area = locate(/area/centcom/supplypod/pod_storage) in GLOB.areas + temp_pod = new(pick(get_area_turfs(pod_storage_area))) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed) + orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1) + selector = new(null, holder.mob) + indicator = new(null, holder.mob) + setDropoff(bay) + initMap() + refreshBay() + ui_interact(holder.mob) + +/datum/centcom_podlauncher/proc/initMap() + if(map_name) + holder.clear_map(map_name) + + map_name = "admin_supplypod_bay_[REF(src)]_map" + // Initialize map objects + cam_screen = new + cam_screen.generate_view(map_name) + + var/datum/plane_master_group/planes = cam_screen.display_to(holder.mob) + + if(!renderLighting) + for(var/atom/movable/screen/plane_master/instance as anything in holder.mob.hud_used.get_true_plane_masters(LIGHTING_PLANE, planes.key)) + instance.set_alpha(100) + + cam_background = new + cam_background.assigned_map = map_name + cam_background.del_on_map_removal = TRUE + refreshView() + holder.register_map_obj(cam_background) + +/datum/centcom_podlauncher/ui_state(mob/user) + if (SSticker.current_state >= GAME_STATE_FINISHED) + return GLOB.always_state //Allow the UI to be given to players by admins after roundend + return GLOB.admin_state + +/datum/centcom_podlauncher/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/supplypods), + ) + +/datum/centcom_podlauncher/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + // Open UI + ui = new(user, src, "CentcomPodLauncher") + ui.open() + refreshView() + +/datum/centcom_podlauncher/ui_static_data(mob/user) + var/list/data = list() + data["mapRef"] = map_name + data["defaultSoundVolume"] = initial(temp_pod.soundVolume) //default volume for pods + data["podStyles"] = pod_style_info + return data + +/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI. + var/list/data = list() //*****NOTE*****: Many of these comments are similarly described in supplypod.dm. If you change them here, please consider doing so in the supplypod code as well! + bayNumber = bay?.loading_id //Used as quick reference to what bay we're taking items from + data["bayNumber"] = bayNumber //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract + data["oldArea"] = (oldTurf ? get_area(oldTurf) : null) //Holds the name of the area that the user was in before using the teleportCentcom action + data["picking_dropoff_turf"] = picking_dropoff_turf //If we're picking or have picked a dropoff turf. Only works when pod is in reverse mode + data["customDropoff"] = customDropoff + data["reverse_dropoff_coords"] = temp_pod.reverse_dropoff_coords + data["renderLighting"] = renderLighting + data["launchClone"] = launchClone //Do we launch the actual items in the bay or just launch clones of them? + data["launchRandomItem"] = launchRandomItem //Do we launch a single random item instead of everything on the turf? + data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1) + data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2) + data["explosionSize"] = temp_pod.explosionSize + data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2) + data["damage"] = temp_pod.damage + data["delays"] = temp_pod.delays + data["rev_delays"] = temp_pod.reverse_delays + data["custom_rev_delay"] = temp_pod.custom_rev_delay + data["styleChoice"] = temp_pod.style::id //Style is a variable that keeps track of what the pod is supposed to look like. + data["effectShrapnel"] = temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing + data["shrapnelType"] = "[temp_pod.shrapnel_type]" //Path2String + data["shrapnelMagnitude"] = temp_pod.shrapnel_magnitude + data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! + data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands + data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands + data["effectBluespace"] = temp_pod.bluespace //If true, the pod deletes (in a shower of sparks) after landing + data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isn't displayed on the turf where the pod will land + data["effectQuiet"] = temp_pod.effectQuiet //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) + data["effectMissile"] = temp_pod.effectMissile //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground + data["effectCircle"] = temp_pod.effectCircle //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here + data["effectBurst"] = effectBurst //IOf true, launches five pods at once (with a very small delay between for added coolness), in a 3x3 area centered around the area + data["effectReverse"] = temp_pod.reversing //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom + data["reverse_option_list"] = temp_pod.reverse_option_list + data["effectTarget"] = specificTarget //Launches the pod at the turf of a specific mob target, rather than wherever the user clicked. Useful for smites + data["effectName"] = temp_pod.adminNamed //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc) + data["podName"] = temp_pod.name + data["podDesc"] = temp_pod.desc + data["effectAnnounce"] = effectAnnounce + data["giveLauncher"] = launcherActivated //If true, the user is in launch mode, and whenever they click a pod will be launched (either at their mouse position or at a specific target) + data["numObjects"] = numTurfs //Counts the number of turfs that contain a launchable object in the centcom supplypod bay + data["fallingSound"] = temp_pod.fallingSound != initial(temp_pod.fallingSound) //Admin sound to play as the pod falls + data["landingSound"] = temp_pod.landingSound //Admin sound to play when the pod lands + data["openingSound"] = temp_pod.openingSound //Admin sound to play when the pod opens + data["leavingSound"] = temp_pod.leavingSound //Admin sound to play when the pod leaves + data["soundVolume"] = temp_pod.soundVolume //Admin sound to play when the pod leaves + return data + +/datum/centcom_podlauncher/ui_act(action, params) + . = ..() + if(.) + return + switch(action) + ////////////////////////////UTILITIES////////////////// + if("gamePanel") + holder.holder.Game() + SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! + . = TRUE + if("buildMode") + var/mob/holder_mob = holder.mob + if (holder_mob && (check_rights(R_BUILDMODE, user = holder_mob))) + togglebuildmode(holder_mob) + SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Build Mode") //If you are copy-pasting this, ensure the 4th parameter is unique to the new proc! + . = TRUE + if("loadDataFromPreset") + var/list/savedData = params["payload"] + loadData(savedData) + . = TRUE + if("switchBay") + bayNumber = params["bayNumber"] + refreshBay() + . = TRUE + if("pickDropoffTurf") //Enters a mode that lets you pick the dropoff location for reverse pods + if (picking_dropoff_turf) + picking_dropoff_turf = FALSE + updateCursor() //Update the cursor of the user to a cool looking target icon + return + if (launcherActivated) + launcherActivated = FALSE //We don't want to have launch mode enabled while we're picking a turf + picking_dropoff_turf = TRUE + updateCursor() //Update the cursor of the user to a cool looking target icon + . = TRUE + if("clearDropoffTurf") + setDropoff(bay) + customDropoff = FALSE + picking_dropoff_turf = FALSE + updateCursor() + . = TRUE + if("teleportDropoff") //Teleports the user to the dropoff point. + var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking + var/turf/current_location = get_turf(M) + var/list/coordinate_list = temp_pod.reverse_dropoff_coords + var/turf/dropoff_turf = locate(coordinate_list[1], coordinate_list[2], coordinate_list[3]) + if (current_location != dropoff_turf) + oldTurf = current_location + M.forceMove(dropoff_turf) //Perform the actual teleport + log_admin("[key_name(usr)] jumped to [AREACOORD(dropoff_turf)]") + message_admins("[key_name_admin(usr)] jumped to [AREACOORD(dropoff_turf)]") + . = TRUE + if("teleportCentcom") //Teleports the user to the centcom supply loading facility. + var/mob/holder_mob = holder.mob //We teleport whatever mob the client is attached to at the point of clicking + var/turf/current_location = get_turf(holder_mob) + var/area/bay_area = bay + if (current_location.loc != bay_area) + oldTurf = current_location + var/turf/teleport_turf = pick(get_area_turfs(bay_area)) + holder_mob.forceMove(teleport_turf) //Perform the actual teleport + if (holder.holder) + log_admin("[key_name(usr)] jumped to [AREACOORD(teleport_turf)]") + message_admins("[key_name_admin(usr)] jumped to [AREACOORD(teleport_turf)]") + . = TRUE + if("teleportBack") //After teleporting to centcom/dropoff, this button allows the user to teleport to the last spot they were at. + var/mob/M = holder.mob + if (!oldTurf) //If theres no turf to go back to, error and cancel + to_chat(M, "Nowhere to jump to!") + return + M.forceMove(oldTurf) //Perform the actual teleport + if (holder.holder) + log_admin("[key_name(usr)] jumped to [AREACOORD(oldTurf)]") + message_admins("[key_name_admin(usr)] jumped to [AREACOORD(oldTurf)]") + . = TRUE + + ////////////////////////////LAUNCH STYLE CHANGES////////////////// + if("launchClone") //Toggles the launchClone var. See variable declarations above for what this specifically means + launchClone = !launchClone + . = TRUE + if("launchRandomItem") //Pick random turfs from the supplypod bay at centcom to launch + launchRandomItem = TRUE + . = TRUE + if("launchWholeTurf") //Pick random turfs from the supplypod bay at centcom to launch + launchRandomItem = FALSE + . = TRUE + if("launchAll") //Launch turfs (from the orderedArea list) all at once, from the supplypod bay at centcom + launchChoice = LAUNCH_ALL + updateSelector() + . = TRUE + if("launchOrdered") //Launch turfs (from the orderedArea list) one at a time in order, from the supplypod bay at centcom + launchChoice = LAUNCH_ORDERED + updateSelector() + . = TRUE + if("launchRandomTurf") //Pick random turfs from the supplypod bay at centcom to launch + launchChoice = LAUNCH_RANDOM + updateSelector() + . = TRUE + + ////////////////////////////POD EFFECTS////////////////// + if("explosionCustom") //Creates an explosion when the pod lands + if (explosionChoice == 1) //If already a custom explosion, set to default (no explosion) + explosionChoice = 0 + temp_pod.explosionSize = list(0,0,0,0) + return + var/list/expNames = list("тотального разрушения", "тяжелого удара", "легкого удара", "пламени") //Explosions have a range of different types of damage + var/list/boomInput = list() + for (var/i=1 to length(expNames)) //Gather input from the user for the value of each type of damage + boomInput.Add(tgui_input_number(usr, "Введите дальность [expNames[i]]. ВНИМАНИЕ: это игнорирует ограниечение радиуса бомб!", "Дальность [expNames[i]]", 0)) + if (isnull(boomInput[i])) + return + if (!isnum(boomInput[i])) //If the user doesn't input a number, set that specific explosion value to zero + tgui_alert(usr, "Это было не число! Вместо него установлено значение по умолчанию (ноль).") + boomInput = 0 + explosionChoice = 1 + temp_pod.explosionSize = boomInput + . = TRUE + if("explosionBus") //Creates a maxcap when the pod lands + if (explosionChoice == 2) //If already a maccap, set to default (no explosion) + explosionChoice = 0 + temp_pod.explosionSize = list(0,0,0,0) + return + explosionChoice = 2 + temp_pod.explosionSize = list(GLOB.max_ex_devastation_range, GLOB.max_ex_heavy_range, GLOB.max_ex_light_range, GLOB.max_ex_flame_range) //Set explosion to max cap of server + . = TRUE + if("damageCustom") //Deals damage to whoevers under the pod when it lands + if (damageChoice == 1) //If already doing custom damage, set back to default (no damage) + damageChoice = 0 + temp_pod.damage = 0 + return + var/damageInput = tgui_input_number(usr, "Введите сумму урона травмами, который нанесется при ударе.", "Сколько урона нанести?", 0) + if (isnull(damageInput)) + return + if (!isnum(damageInput)) //Sanitize the input for damage to deal.s + tgui_alert(usr, "Это было не число! Вместо него установлено значение по умолчанию (ноль).") + damageInput = 0 + damageChoice = 1 + temp_pod.damage = damageInput + . = TRUE + if("damageGib") //Gibs whoever is under the pod when it lands. Also deals 5000 damage, just to be sure. + if (damageChoice == 2) //If already gibbing, set back to default (no damage) + damageChoice = 0 + temp_pod.damage = 0 + temp_pod.effectGib = FALSE + return + damageChoice = 2 + temp_pod.damage = 5000 + temp_pod.effectGib = TRUE //Gibs whoever is under the pod when it lands + . = TRUE + if("effectName") //Give the supplypod a custom name. Supplypods automatically get their name based on their style (see supplypod/setStyle() proc), so doing this overrides that. + if (temp_pod.adminNamed) //If we're already adminNamed, set the name of the pod back to default + temp_pod.adminNamed = FALSE + temp_pod.setStyle(temp_pod.style) //This resets the name of the pod based on its current style (see supplypod/setStyle() proc) + return + var/nameInput= tgui_input_text(usr, "Введите новое имя капсулы", "Новое имя", temp_pod.style::name, MAX_NAME_LEN) //Gather input for name and desc + if (isnull(nameInput)) + return + var/descInput = tgui_input_text(usr, "Введите новое описание капсулы", "Новое описание", temp_pod.style::desc) + if (isnull(descInput)) + return + temp_pod.name = nameInput + temp_pod.desc = descInput + temp_pod.adminNamed = TRUE //This variable is checked in the supplypod/setStyle() proc + . = TRUE + if("effectShrapnel") //Creates a cloud of shrapnel on landing + if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel) + temp_pod.effectShrapnel = FALSE + return + var/shrapnelInput = tgui_input_list(usr, "Пожалуйста, введите тип облака снарядов, которое вы хотите создать при приземлении (может быть любой снаряд!)", "Тип снаряда", sort_list(subtypesof(/obj/item/projectile), GLOBAL_PROC_REF(cmp_typepaths_asc)), null) + if (isnull(shrapnelInput)) + return + var/shrapnelMagnitude = tgui_input_number(usr, "Введите размер облака снарядов. Обычно это значение от 1 до 5. Обратите внимание: Если вы выберите слишком большой размер, вы можете вызвать сбой сервера.", "Размер облака", 0) + if (isnull(shrapnelMagnitude)) + return + if (!isnum(shrapnelMagnitude)) + tgui_alert(usr, "Это было не число! Вместо него установлено значение 3.") + shrapnelMagnitude = 3 + temp_pod.shrapnel_type = shrapnelInput + temp_pod.shrapnel_magnitude = shrapnelMagnitude + temp_pod.effectShrapnel = TRUE + . = TRUE + if("effectStun") //Toggle: Any mob under the pod is stunned (cant move) until the pod lands, hitting them! + temp_pod.effectStun = !temp_pod.effectStun + . = TRUE + if("effectLimb") //Toggle: Anyone carbon mob under the pod loses a limb when it lands + temp_pod.effectLimb = !temp_pod.effectLimb + . = TRUE + if("effectOrgans") //Toggle: Anyone carbon mob under the pod loses a limb when it lands + temp_pod.effectOrgans = !temp_pod.effectOrgans + . = TRUE + if("effectBluespace") //Toggle: Deletes the pod after landing + temp_pod.bluespace = !temp_pod.bluespace + . = TRUE + if("effectStealth") //Toggle: There is no red target indicator showing where the pod will land + temp_pod.effectStealth = !temp_pod.effectStealth + . = TRUE + if("effectQuiet") //Toggle: The pod makes no noise (explosions, opening sounds, etc) + temp_pod.effectQuiet = !temp_pod.effectQuiet + . = TRUE + if("effectMissile") //Toggle: The pod deletes the instant it lands. Looks nicer than just setting the open delay and leave delay to zero. Useful for combo-ing with explosions + temp_pod.effectMissile = !temp_pod.effectMissile + . = TRUE + if("effectCircle") //Toggle: The pod can come in from any descent angle. Goof requested this im not sure why but it looks p funny actually + temp_pod.effectCircle = !temp_pod.effectCircle + . = TRUE + if("effectBurst") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target + effectBurst = !effectBurst + . = TRUE + if("effectAnnounce") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target + effectAnnounce = !effectAnnounce + . = TRUE + if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from + temp_pod.reversing = !temp_pod.reversing + update_dropoff_indicator() + . = TRUE + if("reverseOption") + var/reverseOption = params["reverseOption"] + temp_pod.reverse_option_list[reverseOption] = !temp_pod.reverse_option_list[reverseOption] + . = TRUE + if("effectTarget") //Toggle: Launch at a specific mob (instead of at whatever turf you click on). Used for the supplypod smite + if (specificTarget) + specificTarget = null + return + + var/list/possible_destinations = GLOB.mob_living_list + var/mob/target = tgui_input_list(usr, "Выберите моба!", "Цель", possible_destinations) + + if (isnull(target) || QDELETED(target)) + return + + + specificTarget = target + + . = TRUE + ////////////////////////////TIMER DELAYS////////////////// + if("editTiming") //Change the different timers relating to the pod + var/delay = params["timer"] + var/value = params["value"] + var/reverse = params["reverse"] + if (reverse) + temp_pod.reverse_delays[delay] = value * 10 + else + temp_pod.delays[delay] = value * 10 + . = TRUE + if("resetTiming") + temp_pod.delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + temp_pod.reverse_delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + . = TRUE + if("toggleRevDelays") + temp_pod.custom_rev_delay = !temp_pod.custom_rev_delay + . = TRUE + ////////////////////////////ADMIN SOUNDS////////////////// + if("fallingSound") //Admin sound from a local file that plays when the pod lands + if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound)) + temp_pod.fallingSound = initial(temp_pod.fallingSound) + temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength) + return + var/soundInput = input(holder, "Пожалуйста, выберите звуковой файл, который будет воспроизводиться, когда капсула будет приземляться! Звук начнет воспроизводиться и попытается прекратится, когда капсула приземлится.", "Выберите звуковой файл") as null|sound + if (isnull(soundInput)) + return + var/sound/tempSound = sound(soundInput) + playsound(holder.mob, tempSound, 1) + var/list/sounds_list = holder.SoundQuery() + var/soundLen = 0 + for (var/playing_sound in sounds_list) + if (isnull(playing_sound)) + stack_trace("client.SoundQuery() Returned a list containing a null sound! Somehow!") + continue + var/sound/found = playing_sound + if (found.file == tempSound.file) + soundLen = length(found) + if (!soundLen) + soundLen = tgui_input_number(holder, "Не удалось автоматически определить длительность звукового файла. Какова его точная длительность в секундах? Это число будет использоваться для выравнивания звука так, чтобы он заканчивался сразу после приземления капсулы!", "Введите длительность файла", 0.3) + if (isnull(soundLen)) + return + if (!isnum(soundLen)) + tgui_alert(usr, "Это было не число! Вместо него установлено значение по умолчанию ([initial(temp_pod.fallingSoundLength) * 0.1]).") + temp_pod.fallingSound = soundInput + temp_pod.fallingSoundLength = 10 * soundLen + . = TRUE + if("landingSound") //Admin sound from a local file that plays when the pod lands + if (!isnull(temp_pod.landingSound)) + temp_pod.landingSound = null + return + var/soundInput = input(holder, "Пожалуйста, выберите звуковой файл, который будет воспроизводиться, когда капсула приземлится! Рекомендуется «ох, черт, прости!» в случае, если вы кого-нибудь ударите капсулой.", "Выберите звуковой файл") as null|sound + if (isnull(soundInput)) + return + temp_pod.landingSound = soundInput + . = TRUE + if("openingSound") //Admin sound from a local file that plays when the pod opens + if (!isnull(temp_pod.openingSound)) + temp_pod.openingSound = null + return + var/soundInput = input(holder, "Пожалуйста, выберите звуковой файл, который будет воспроизводиться при открытии капсулы! Рекомендуется стандартный звуковой эффект детей, радующихся вечеринке, в случае, если в вашей капсуле полно веселых и интересных вещей!", "Выберите звуковой файл") as null|sound + if (isnull(soundInput)) + return + temp_pod.openingSound = soundInput + . = TRUE + if("leavingSound") //Admin sound from a local file that plays when the pod leaves + if (!isnull(temp_pod.leavingSound)) + temp_pod.leavingSound = null + return + var/soundInput = input(holder, "Пожалуйста, выберите звуковой файл, который будет воспроизводиться, когда капсула исчезнет! Рекомендуется хороший звук свистка, особенно если вы используете эффект возвращаемой капсулы.", "Выберите звуковой файл") as null|sound + if (isnull(soundInput)) + return + temp_pod.leavingSound = soundInput + . = TRUE + if("soundVolume") //Admin sound from a local file that plays when the pod leaves + if (temp_pod.soundVolume != initial(temp_pod.soundVolume)) + temp_pod.soundVolume = initial(temp_pod.soundVolume) + return + var/soundInput = tgui_input_number(holder, "Пожалуйста, выберите громкость. По умолчанию — от 1 до 100, где 50 — среднее значение, но выбирайте любое. Если вы по-прежнему не слышите звук, попробуйте включить эффект «Тихо». При этом будут отключены все звуки капсулы, за исключением пользовательских, заданных администратором тремя предыдущими кнопками.", "Выберите громкость админских звуков") + if (isnull(soundInput)) + return + temp_pod.soundVolume = soundInput + . = TRUE + ////////////////////////////STYLE CHANGES////////////////// + //as a way to get the proper icon state, name, and description of the pod. + if("tabSwitch") + tabIndex = params["tabIndex"] + refreshView() + . = TRUE + if("refreshView") + refreshView() + . = TRUE + if("renderLighting") + renderLighting = !renderLighting + . = TRUE + if("setStyle") + var/chosenStyle = params["style"] + temp_pod.setStyle(pod_style_lookup[chosenStyle]) + . = TRUE + if("refresh") //Refresh the Pod bay. User should press this if they spawn something new in the centcom bay. Automatically called whenever the user launches a pod + refreshBay() + . = TRUE + if("giveLauncher") //Enters the "Launch Mode". When the launcher is activated, temp_pod is cloned, and the result it filled and launched anywhere the user clicks (unless specificTarget is true) + launcherActivated = !launcherActivated + if (picking_dropoff_turf) + picking_dropoff_turf = FALSE //We don't want to have launch mode enabled while we're picking a turf + updateCursor() //Update the cursor of the user to a cool looking target icon + updateSelector() + . = TRUE + if("clearBay") //Delete all mobs and objs in the selected bay + if(tgui_alert(usr, "Это приведет к удалению всех объектов и мобов в [bay]. Вы уверены?", "Подтверждение", list("Удали это дерьмо", "Нет")) == "Удали это дерьмо") + clearBay() + refreshBay() + . = TRUE + +/datum/centcom_podlauncher/ui_close(mob/user) //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables. + QDEL_NULL(temp_pod) + QDEL_NULL(cam_screen) + QDEL_NULL(cam_background) + qdel(src) + +/datum/centcom_podlauncher/proc/setupViewPod() + setupView(RANGE_TURFS(1, temp_pod)) + +/datum/centcom_podlauncher/proc/setupViewBay() + var/list/visible_turfs = list() + for(var/turf/bay_turf in bay) + visible_turfs += bay_turf + setupView(visible_turfs) + +/datum/centcom_podlauncher/proc/setupViewDropoff() + var/list/coords_list = temp_pod.reverse_dropoff_coords + var/turf/drop = locate(coords_list[1], coords_list[2], coords_list[3]) + setupView(RANGE_TURFS(3, drop)) + +/datum/centcom_podlauncher/proc/setupView(list/visible_turfs) + var/list/bbox = get_bbox_of_atoms(visible_turfs) + var/size_x = bbox[3] - bbox[1] + 1 + var/size_y = bbox[4] - bbox[2] + 1 + + cam_screen.vis_contents = visible_turfs + cam_background.icon_state = "clear" + cam_background.fill_rect(1, 1, size_x, size_y) + +/datum/centcom_podlauncher/proc/updateCursor(forceClear = FALSE) //Update the mouse of the user + if (!holder) //Can't update the mouse icon if the client doesnt exist! + return + if (!forceClear && (launcherActivated || picking_dropoff_turf)) //If the launching param is true, we give the user new mouse icons. + if(launcherActivated) + holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released + holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed + else if(picking_dropoff_turf) + holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released + holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed + holder.mouse_override_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released) + holder.mouse_pointer_icon = holder.mouse_override_icon + holder.click_intercept = src //Create a click_intercept so we know where the user is clicking + else + var/mob/holder_mob = holder.mob + holder.mouse_up_icon = null + holder.mouse_down_icon = null + holder.mouse_override_icon = null + holder.click_intercept = null + holder_mob?.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)() + +/datum/centcom_podlauncher/proc/InterceptClickOn(user,params,atom/target) //Click Intercept so we know where to send pods where the user clicks + var/list/modifiers = params2list(params) + + var/left_click = LAZYACCESS(modifiers, LEFT_CLICK) + + if (launcherActivated) + //Clicking on UI elements shouldn't launch a pod + if(istype(target,/atom/movable/screen)) + return FALSE + + . = TRUE + + if(left_click) //When we left click: + preLaunch() //Fill the acceptableTurfs list from the orderedArea list. Then, fill up the launchList list with items from the acceptableTurfs list based on the manner of launch (ordered, random, etc) + if (!isnull(specificTarget)) + target = get_turf(specificTarget) //if we have a specific target, then always launch the pod at the turf of the target + else if (target) + target = get_turf(target) //Make sure we're aiming at a turf rather than an item or effect or something + else + return //if target is null and we don't have a specific target, cancel + if (effectAnnounce) + var/old_layer = temp_pod.layer + var/old_plane = temp_pod.plane + var/pod_name = (!temp_pod.adminNamed)? temp_pod.declent_ru(NOMINATIVE) : temp_pod.name + notify_ghosts( + title = "Запущена [pod_name]", + message = "На станцию запущена специальная посылка.", + source = target, + alert_overlay = temp_pod) + temp_pod.layer = old_layer + temp_pod.plane = old_plane + var/list/bouttaDie = list() + for (var/mob/living/target_mob in target) + bouttaDie.Add(target_mob) + if (holder.holder) + supplypod_punish_log(bouttaDie) + if (!effectBurst) //If we're not using burst mode, just launch normally. + launch(target) + else + for (var/i in 1 to 5) //If we're using burst mode, launch 5 pods + if (isnull(target)) + break //if our target gets deleted during this, we stop the show + preLaunch() //Same as above + var/landingzone = locate(target.x + rand(-1,1), target.y + rand(-1,1), target.z) //Pods are randomly adjacent to (or the same as) the target + if (landingzone) //just incase we're on the edge of the map or something that would cause target.x+1 to fail + launch(landingzone) //launch the pod at the adjacent turf + else + launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target + sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire. + else if (picking_dropoff_turf) + //Clicking on UI elements shouldn't pick a dropoff turf + if(istype(target,/atom/movable/screen)) + return FALSE + + . = TRUE + if(left_click) //When we left click: + var/turf/target_turf = get_turf(target) + setDropoff(target_turf) + customDropoff = TRUE + to_chat(user, span_notice("Вы выбрали [target_turf] в [COORD(target_turf)] в качестве места сброса")) + +/datum/centcom_podlauncher/proc/refreshView() + switch(tabIndex) + if (TAB_POD) + setupViewPod() + if (TAB_BAY) + setupViewBay() + else + setupViewDropoff() + +/datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched + bay = GLOB.supplypod_loading_bays[bayNumber] + orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay + preLaunch() //Fill acceptable turfs from orderedArea, then fill launchList from acceptableTurfs (see proc for more info) + refreshView() + +/datum/centcom_podlauncher/proc/createOrderedArea(area/area_to_order) //This assumes the area passed in is a continuous square + if (isnull(area_to_order)) //If theres no supplypod bay mapped into centcom, throw an error + to_chat(holder.mob, "В мире нет /area/centcom/supplypod/loading/one (или /two, /three или /four)! На данный момент вы можете сделать их самостоятельно (а затем обновить список), но попросите маппера исправить это сегодня!") + CRASH("No /area/centcom/supplypod/loading/one (or /two or /three or /four) has been mapped into the centcom z-level!") + orderedArea = list() + if (length(area_to_order.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values + var/startX = area_to_order.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments) + var/endX = area_to_order.contents[1].x + var/startY = area_to_order.contents[1].y + var/endY = area_to_order.contents[1].y + for (var/turf/turf_in_area in area_to_order) //For each turf in the area, go through and find: + if (turf_in_area.x < startX) //The turf with the smallest x value. This is our startX + startX = turf_in_area.x + else if (turf_in_area.x > endX) //The turf with the largest x value. This is our endX + endX = turf_in_area.x + else if (turf_in_area.y > startY) //The turf with the largest Y value. This is our startY + startY = turf_in_area.y + else if (turf_in_area.y < endY) //The turf with the smallest Y value. This is our endY + endY = turf_in_area.y + for (var/vertical in endY to startY) + for (var/horizontal in startX to endX) + orderedArea.Add(locate(horizontal, startY - (vertical - endY), 1)) //After gathering the start/end x and y, go through locating each turf from top left to bottom right, like one would read a book + return orderedArea //Return the filled list + +/datum/centcom_podlauncher/proc/preLaunch() //Creates a list of acceptable items, + numTurfs = 0 //Counts the number of turfs that can be launched (remember, supplypods either launch all at once or one turf-worth of items at a time) + acceptableTurfs = list() + for (var/t in orderedArea) //Go through the orderedArea list + var/turf/unchecked_turf = t + if (iswallturf(unchecked_turf) || length(typecache_filter_list_reverse(unchecked_turf.contents, ignored_atoms))) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list + acceptableTurfs.Add(unchecked_turf) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well. + numTurfs ++ + + launchList = list() //Anything in launchList will go into the supplypod when it is launched + if (length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true) + switch(launchChoice) + if(LAUNCH_ALL) //If we are launching all the turfs at once + for (var/t in acceptableTurfs) + var/turf/accepted_turf = t + launchList |= typecache_filter_list_reverse(accepted_turf.contents, ignored_atoms) //We filter any blacklisted atoms and add the rest to the launchList + if(LAUNCH_ORDERED) //If we are launching one at a time + if (launchCounter > length(acceptableTurfs)) //Check if the launchCounter, which acts as an index, is too high. If it is, reset it to 1 + launchCounter = 1 //Note that the launchCounter index is incremented in the launch() proc + var/turf/next_turf_in_line = acceptableTurfs[launchCounter] + launchList |= typecache_filter_list_reverse(next_turf_in_line.contents, ignored_atoms) //Filter the specicic turf chosen from acceptableTurfs, and add it to the launchList + if(LAUNCH_RANDOM) //If we are launching randomly + var/turf/acceptable_turf = pick_n_take(acceptableTurfs) + launchList |= typecache_filter_list_reverse(acceptable_turf.contents, ignored_atoms) //filter a random turf from the acceptableTurfs list and add it to the launchList + updateSelector() //Call updateSelector(), which, if we are launching one at a time (launchChoice == 2), will move to the next turf that will be launched + //UpdateSelector() is here (instead if the if(1) switch block) because it also moves the selector to nullspace (to hide it) if needed + +/datum/centcom_podlauncher/proc/launch(turf/target_turf) //Game time started + if (isnull(target_turf)) + return + var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod, TRUE, TRUE) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result + toLaunch.update_appearance()//we update_appearance() here so that the door doesnt "flicker on" right after it lands + var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] + toLaunch.forceMove(shippingLane) + if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those + if(launchRandomItem) + var/launch_candidate = pick_n_take(launchList) + if(!isnull(launch_candidate)) + var/atom/movable/movable_to_launch = DuplicateObject(launch_candidate, TRUE, TRUE) + movable_to_launch.forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod + else + for (var/launch_candidate in launchList) + if (isnull(launch_candidate)) + continue + var/atom/movable/movable_to_launch = DuplicateObject(launch_candidate, TRUE, TRUE) + movable_to_launch.forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod + else + if(launchRandomItem) + var/atom/random_item = pick_n_take(launchList) + if(!isnull(random_item)) + var/atom/movable/random_item_movable = random_item + random_item_movable.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod + else + for (var/thing_to_launch in launchList) //If we aren't cloning the objects, just go through the launchList + if (isnull(thing_to_launch)) + continue + var/atom/movable/movable_to_launch = thing_to_launch + movable_to_launch.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod + new /obj/effect/pod_landingzone(target_turf, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to its location + if (launchClone) + launchCounter++ //We only need to increment launchCounter if we are cloning objects. + //If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order + +/datum/centcom_podlauncher/proc/updateSelector() //Ensures that the selector effect will showcase the next item if needed + if (launchChoice == LAUNCH_ORDERED && length(acceptableTurfs) > 1 && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay + var/index = (launchCounter == 1 ? launchCounter : launchCounter + 1) //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list. We don't want to do this for the very first item tho + if (index > length(acceptableTurfs)) //out of bounds check + index = 1 + selector.forceMove(acceptableTurfs[index]) //forceMove the selector to the next turf in the ordered acceptableTurfs list + else + selector.moveToNullspace() //Otherwise, we move the selector to nullspace until it is needed again + +/datum/centcom_podlauncher/proc/clearBay() //Clear all objs and mobs from the selected bay + for (var/obj/O in bay.get_all_contents()) + qdel(O) + for (var/mob/M in bay.get_all_contents()) + qdel(M) + for (var/bayturf in bay) + var/turf/turf_to_clear = bayturf + turf_to_clear.ChangeTurf(/turf/simulated/floor) + +/datum/centcom_podlauncher/Destroy() //The Destroy() proc. This is called by ui_close proc, or whenever the user leaves the game + updateCursor(TRUE) //Make sure our moues cursor resets to default. False means we are not in launch mode + QDEL_NULL(temp_pod) //Delete the temp_pod + QDEL_NULL(selector) //Delete the selector effect + QDEL_NULL(indicator) + return ..() + +/datum/centcom_podlauncher/proc/supplypod_punish_log(list/whoDyin) + var/podString = effectBurst ? "5 pods" : "a pod" + var/whomString = "" + if (LAZYLEN(whoDyin)) + for (var/mob/living/M in whoDyin) + whomString += "[key_name(M)], " + + var/msg = "launched [podString] towards [whomString]" + message_admins("[key_name_admin(usr)] [msg] in [ADMIN_VERBOSEJMP(specificTarget)].") + if (length(whoDyin)) + for (var/mob/living/M in whoDyin) + log_and_message_admins("[key_name_admin(usr)] [msg]") + +/datum/centcom_podlauncher/proc/loadData(list/dataToLoad) + bayNumber = dataToLoad["bayNumber"] + customDropoff = dataToLoad["customDropoff"] + var/list/cords = dataToLoad["reverse_dropoff_coords"] + if(cords?.len) + var/turf/dropoff =locate(cords[1], cords[2], cords[3]) + setDropoff(dropoff) + renderLighting = dataToLoad["renderLighting"] + launchClone = dataToLoad["launchClone"] //Do we launch the actual items in the bay or just launch clones of them? + launchRandomItem = dataToLoad["launchRandomItem"] //Do we launch a single random item instead of everything on the turf? + launchChoice = dataToLoad["launchChoice"] //Launch turfs all at once (0), ordered (1), or randomly(1) + explosionChoice = dataToLoad["explosionChoice"] //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2) + temp_pod.explosionSize = (explosionChoice)? dataToLoad["explosionSize"] : list(0,0,0,0) + damageChoice = dataToLoad["damageChoice"] //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2) + temp_pod.damage = (damageChoice)? dataToLoad["damage"] : 0 + temp_pod.delays = dataToLoad["delays"] + temp_pod.reverse_delays = dataToLoad["rev_delays"] + temp_pod.custom_rev_delay = dataToLoad["custom_rev_delay"] + temp_pod.setStyle(pod_style_lookup[dataToLoad["styleChoice"]]) //Style is a variable that keeps track of what the pod is supposed to look like. + temp_pod.effectShrapnel = dataToLoad["effectShrapnel"] //If true, creates a cloud of shrapnel of a decided type and magnitude on landing + temp_pod.shrapnel_type = text2path(dataToLoad["shrapnelType"]) + temp_pod.shrapnel_magnitude = dataToLoad["shrapnelMagnitude"] + temp_pod.effectStun = dataToLoad["effectStun"]//If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! + temp_pod.effectLimb = dataToLoad["effectLimb"]//If true, pops off a limb (if applicable) from anyone caught under the pod when it lands + temp_pod.effectOrgans = dataToLoad["effectOrgans"]//If true, yeets the organs out of any bodies caught under the pod when it lands + temp_pod.bluespace = dataToLoad["effectBluespace"] //If true, the pod deletes (in a shower of sparks) after landing + temp_pod.effectStealth = dataToLoad["effectStealth"]//If true, a target icon isn't displayed on the turf where the pod will land + temp_pod.effectQuiet = dataToLoad["effectQuiet"] //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) + temp_pod.effectMissile = dataToLoad["effectMissile"] //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground + temp_pod.effectCircle = dataToLoad["effectCircle"] //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here + effectBurst = dataToLoad["effectBurst"] //IOf true, launches five pods at once (with a very small delay between for added coolness), in a 3x3 area centered around the area + temp_pod.reversing = dataToLoad["effectReverse"] //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom + update_dropoff_indicator() + temp_pod.reverse_option_list = dataToLoad["reverse_option_list"] + specificTarget = dataToLoad["effectTarget"] //Launches the pod at the turf of a specific mob target, rather than wherever the user clicked. Useful for smites + temp_pod.adminNamed = dataToLoad["effectName"] //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc) + temp_pod.name = dataToLoad["podName"] + temp_pod.desc = dataToLoad["podDesc"] + effectAnnounce = dataToLoad["effectAnnounce"] + numTurfs = dataToLoad["numObjects"] //Counts the number of turfs that contain a launchable object in the centcom supplypod bay + + // Custom sounds now can't be saved. + //temp_pod.fallingSound = dataToLoad["fallingSound"]//Admin sound to play as the pod falls + //temp_pod.landingSound = dataToLoad["landingSound"]//Admin sound to play when the pod lands + //temp_pod.openingSound = dataToLoad["openingSound"]//Admin sound to play when the pod opens + //temp_pod.leavingSound = dataToLoad["leavingSound"]//Admin sound to play when the pod leaves + + temp_pod.soundVolume = dataToLoad["soundVolume"] //Admin sound to play when the pod leaves + picking_dropoff_turf = FALSE + launcherActivated = FALSE + updateCursor() + refreshView() + +GLOBAL_DATUM_INIT(podlauncher, /datum/centcom_podlauncher, new) + +//Set the dropoff location and indicator to either a specific turf or somewhere in an area +/datum/centcom_podlauncher/proc/setDropoff(target) + var/turf/target_turf + if (isturf(target)) + target_turf = target + else if (isarea(target)) + target_turf = pick(get_area_turfs(target)) + else + CRASH("Improper type passed to setDropoff! Should be /turf or /area") + temp_pod.reverse_dropoff_coords = list(target_turf.x, target_turf.y, target_turf.z) + indicator.forceMove(target_turf) + +/datum/centcom_podlauncher/proc/update_dropoff_indicator() + if (temp_pod?.reversing) + indicator.alpha = 150 + else + indicator.alpha = 0 + +/obj/effect/client_image_holder/supplypod_selector // Shows which item will be taken next + name = "Supply Selector (Only you can see this)" + image_icon = 'icons/obj/supplypods_32x32.dmi' + image_state = "selector" + image_layer = FLY_LAYER + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + alpha = 150 + +/obj/effect/client_image_holder/dropoff_location // Shows where revese pods lands + name = "Dropoff Location (Only you can see this)" + image_icon = 'icons/obj/supplypods_32x32.dmi' + image_state = "dropoff_indicator" + image_layer = FLY_LAYER + layer = FLY_LAYER + plane = ABOVE_GAME_PLANE + alpha = 0 + +#undef LAUNCH_ALL +#undef LAUNCH_ORDERED +#undef LAUNCH_RANDOM +#undef TAB_BAY +#undef TAB_POD diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm new file mode 100644 index 00000000000..9a1ed8dbc41 --- /dev/null +++ b/code/modules/cargo/supplypod.dm @@ -0,0 +1,767 @@ +//The "pod_landingzone" temp visual is created by anything that "launches" a supplypod. This is what animates the pod and makes the pod forcemove to the station. +//------------------------------------SUPPLY POD-------------------------------------// +/obj/structure/closet/supplypod + name = "supply pod" //Names and descriptions are normally created with the setStyle() proc during initialization, but we have these default values here as a failsafe + desc = "Капсула снабжения Nanotrasen." + icon = 'icons/obj/supplypods.dmi' + icon_state = "pod" //This is a common base sprite shared by a number of pods + pixel_x = SUPPLYPOD_X_OFFSET //2x2 sprite + layer = BELOW_OBJ_LAYER //So that the crate inside doesn't appear underneath + can_weld_shut = FALSE + armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 80) + anchored = TRUE //So it cant slide around after landing + density = FALSE + ru_names = list( + NOMINATIVE = "капсула снабжения", + GENITIVE = "капсулы снабжения", + DATIVE = "капсуле снабжения", + ACCUSATIVE = "капсулу снабжения", + INSTRUMENTAL = "капсулой снабжения", + PREPOSITIONAL = "капсуле снабжения" + ) + ///List of bitflags for supply pods, see: code\__DEFINES\obj_flags.dm + var/pod_flags = NONE + + //*****NOTE*****: Many of these comments are similarly described in centcom_podlauncher.dm. If you change them here, please consider doing so in the centcom podlauncher code as well! + var/adminNamed = FALSE //Determines whether or not the pod has been named by an admin. If true, the pod's name will not get overridden when the style of the pod changes (changing the style of the pod normally also changes the name+desc) + var/bluespace = FALSE //If true, the pod deletes (in a shower of sparks) after landing + var/delays = list(POD_TRANSIT = 30, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + var/reverse_delays = list(POD_TRANSIT = 30, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + var/custom_rev_delay = FALSE + var/damage = 0 //Damage that occurs to any mob under the pod when it lands. + var/effectStun = FALSE //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish! + var/effectLimb = FALSE //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands + var/effectOrgans = FALSE //If true, yeets out every limb and organ from anyone caught under the pod when it lands + var/effectGib = FALSE //If true, anyone under the pod will be gibbed when it lands + var/effectStealth = FALSE //If true, a target icon isn't displayed on the turf where the pod will land + var/effectQuiet = FALSE //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc) + var/effectMissile = FALSE //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground + var/effectCircle = FALSE //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here + var/datum/pod_style/style = /datum/pod_style //Style is a variable that keeps track of what the pod is supposed to look like. Only stores a path, type is set for ease of var access + var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom + var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off its newly-acquired cargo to + var/fallingSoundLength = 11 + var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands + var/landingSound //Admin sound to play when the pod lands + var/openingSound //Admin sound to play when the pod opens + var/leavingSound //Admin sound to play when the pod leaves + var/soundVolume = 80 //Volume to play sounds at. Ignores the cap + var/list/explosionSize = list(0,0,2,3) + var/stay_after_drop = FALSE + var/specialised = FALSE // It's not a general use pod for cargo/admin use + var/rubble_type //Rubble effect associated with this supplypod + var/decal = "default" //What kind of extra decals we add to the pod to make it look nice + var/door = "pod_door" + var/fin_mask = "topfin" + var/obj/effect/supplypod_rubble/rubble + var/obj/effect/engineglow/glow_effect + var/effectShrapnel = FALSE + var/shrapnel_type = /obj/item/projectile/shrapnel + var/shrapnel_magnitude = 3 + var/list/reverse_option_list = list(MOB_OPTION=FALSE, UNANCHORED_OPTION=FALSE, ANCHORED_OPTION=FALSE, MECHA_OPTION=FALSE) + +/obj/structure/closet/supplypod/bluespacepod + style = /datum/pod_style/advanced + bluespace = TRUE + explosionSize = list(0,0,1,2) + +//type used for one drop spawning items. doesn't have a style as style is set by the helper that creates this +/obj/structure/closet/supplypod/podspawn + bluespace = TRUE + explosionSize = list(0,0,0,0) + +/obj/structure/closet/supplypod/podspawn/deathmatch + desc = "Десантная капсула в кроваво-красном стиле." + specialised = TRUE + +/obj/structure/closet/supplypod/podspawn/deathmatch/preOpen() + for(var/mob/living/critter in contents) + critter.faction = list("hostile") //No infighting, but also KILL!! + return ..() + +/obj/structure/closet/supplypod/extractionpod + name = "Syndicate Extraction Pod" + desc = "Специализированная капсула кроваво-красного цвета для эвакуации ценных целей из зон активных задач. Для правильной доставки цели необходимо вручную поместить в капсулу." + specialised = TRUE + style = /datum/pod_style/syndicate + bluespace = TRUE + explosionSize = list(0,0,1,2) + delays = list(POD_TRANSIT = 25, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + reversing = TRUE + stay_after_drop = TRUE + leavingSound = 'sound/effects/podwoosh.ogg' + reverse_option_list = list(MOB_OPTION=TRUE, UNANCHORED_OPTION=FALSE, ANCHORED_OPTION=FALSE, MECHA_OPTION=FALSE) + ru_names = list( + NOMINATIVE = "капсула эвакуации Синдиката", + GENITIVE = "капсулы эвакуации Синдиката", + DATIVE = "капсуле эвакуации Синдиката", + ACCUSATIVE = "капсулу эвакуации Синдиката", + INSTRUMENTAL = "капсулой эвакуации Синдиката", + PREPOSITIONAL = "капсуле эвакуации Синдиката" + ) + +/obj/structure/closet/supplypod/centcompod + style = /datum/pod_style/centcom + bluespace = TRUE + explosionSize = list(0,0,0,0) + delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/structure/closet/supplypod/centcompod/sisyphus + delays = list(POD_TRANSIT = 0, POD_FALLING = 0, POD_OPENING = 0, POD_LEAVING = 0.2) + reverse_delays = list(POD_TRANSIT = 0, POD_FALLING = 1.5 SECONDS, POD_OPENING = 0.6 SECONDS, POD_LEAVING = 0) + custom_rev_delay = TRUE + effectStealth = TRUE + reversing = TRUE + reverse_option_list = list( + MOB_OPTION = TRUE, + UNANCHORED_OPTION = FALSE, + ANCHORED_OPTION = FALSE, + MECHA_OPTION = TRUE, + ) + +/obj/structure/closet/supplypod/back_to_station + name = "blood-red supply pod" + desc = "Устрашающая капсула снабжения, покрытая кроваво-красными отметинами." + bluespace = TRUE + explosionSize = list(0,0,0,0) + style = /datum/pod_style/syndicate + specialised = TRUE + ru_names = list( + NOMINATIVE = "кроваво-красная капсула снабжения", + GENITIVE = "кроваво-красной капсулы снабжения", + DATIVE = "кроваво-красной капсуле снабжения", + ACCUSATIVE = "кроваво-красную капсулу снабжения", + INSTRUMENTAL = "кроваво-красной капсулой снабжения", + PREPOSITIONAL = "кроваво-красной капсуле снабжения" + ) + +/obj/structure/closet/supplypod/deadmatch_missile + name = "cruise missile" + desc = "Огромная ракета, вероятно, запущенная из какой-то далекой ракетной шахты в дальнем космосе" + style = /datum/pod_style/missile/syndicate + explosionSize = list(0,1,2,2) + effectShrapnel = TRUE + specialised = TRUE + delays = list(POD_TRANSIT = 2.6 SECONDS, POD_FALLING = 0.4 SECONDS) + effectMissile = TRUE + ru_names = list( + NOMINATIVE = "крылатая ракета", + GENITIVE = "крылатой ракеты", + DATIVE = "крылатой ракете", + ACCUSATIVE = "крылатую ракету", + INSTRUMENTAL = "крылатой ракете", + PREPOSITIONAL = "крылатой ракетой" + ) + + +/obj/structure/closet/supplypod/Initialize(mapload, customStyle = FALSE) + . = ..() + if (!loc) + var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] //temporary holder for supplypods mid-transit + forceMove(shippingLane) + if (customStyle) + style = customStyle + setStyle(style) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly + +/obj/structure/closet/supplypod/proc/setStyle(datum/pod_style/chosen_style) //Used to give the sprite an icon state, name, and description. + style = chosen_style + if(!(style in GLOB.pod_styles_by_type)) + GLOB.pod_styles_by_type[chosen_style] = new chosen_style + chosen_style = GLOB.pod_styles_by_type[chosen_style] + icon_state = chosen_style.icon_state + decal = chosen_style.decal_icon + rubble_type = chosen_style.rubble_type + if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum + name = chosen_style.name + desc = chosen_style.desc + ru_names = chosen_style.ru_names + if (chosen_style.has_door) + door = "[icon_state]_door" + else + door = FALSE + update_appearance() + +/obj/structure/closet/supplypod/proc/SetReverseIcon() + fin_mask = "bottomfin" + if (style::shape == POD_SHAPE_NORMAL) + icon_state = style::icon_state + "_reverse" + pixel_x = initial(pixel_x) + transform = matrix() + update_appearance() + +/obj/structure/closet/supplypod/proc/backToNonReverseIcon() + fin_mask = initial(fin_mask) + if (style::shape == POD_SHAPE_NORMAL) + icon_state = style::icon_state + pixel_x = initial(pixel_x) + transform = matrix() + update_appearance() + + +/obj/structure/closet/supplypod/update_overlays() + . = ..() + if(ispath(style, /datum/pod_style/invisible)) + return + + if(rubble) + . += rubble.getForeground(src) + + if(ispath(style, /datum/pod_style/seethrough)) + for(var/atom/A in contents) + var/mutable_appearance/itemIcon = new(A) + itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0) + . += itemIcon + return + + if(opened) //We're opened means all we have to worry about is masking a decal if we have one + if(!decal) //We don't have a decal to mask + return + if(!door) //We have a decal but no door, so let's just add the decal + . += decal + return + var/icon/masked_decal = new(icon, decal) //The decal we want to apply + var/icon/door_masker = new(icon, door) //The door shape we want to 'cut out' of the decal + door_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1) + door_masker.SwapColor("#ffffffff", null) + door_masker.Blend(COLOR_BLACK, ICON_SUBTRACT) + masked_decal.Blend(door_masker, ICON_ADD) + . += masked_decal + return + + //If we're closed + if(!door) //We have no door, lets see if we have a decal. If not, theres nothing we need to do + if(decal) + . += decal + return + else if (style::shape != POD_SHAPE_NORMAL) //If we're not a normal pod shape (aka, if we don't have fins), just add the door without masking + . += door + else + var/icon/masked_door = new(icon, door) //The door we want to apply + var/icon/fin_masker = new(icon, "mask_[fin_mask]") //The fin shape we want to 'cut out' of the door + fin_masker.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 1,1,1,0, 0,0,0,1) + fin_masker.SwapColor("#ffffffff", null) + fin_masker.Blend(COLOR_BLACK, ICON_SUBTRACT) + masked_door.Blend(fin_masker, ICON_ADD) + . += masked_door + if(decal) + . += decal + + +/obj/structure/closet/supplypod/tool_act(mob/living/user, obj/item/I, tool_type) + if(bluespace) //We dont want to worry about interacting with bluespace pods, as they are due to delete themselves soon anyways. + return FALSE + else + ..() + + +/obj/structure/closet/supplypod/ex_act() //Explosions dont do SHIT TO US! This is because supplypods create explosions when they land. + return FALSE + +/obj/structure/closet/supplypod/contents_explosion() //Supplypods also protect their contents from the harmful effects of fucking exploding. + return + +/obj/structure/closet/supplypod/toggle(mob/living/user) + return + +///Called by the drop pods that return captured crewmembers from the ninja den. +/obj/structure/closet/supplypod/proc/return_from_capture(mob/living/victim, turf/destination = get_safe_random_station_turf()) + if(isnull(destination)) //Uuuuh, something went wrong. This is gonna hurt. + to_chat(victim, span_holoparasite("Миллион голосов эхом звучит в твоей голове... «Похоже, там, куда тебя отправили, не могут справиться с нашей капсулой...\ + как будто мы хотели, чтобы пассажир выжил. Держись, корпоративная собака»")) + explosionSize = list(0,1,1,1) + destination = get_random_station_turf() + + do_sparks(8, FALSE, victim) + victim.visible_message(span_notice("[victim] исчезает...")) + + victim.forceMove(src) + + new /obj/effect/pod_landingzone(destination, src) + +/obj/structure/closet/supplypod/proc/handleReturnAfterDeparting(atom/movable/holder = src) + reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() ) + bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever + pod_flags &= ~FIRST_SOUNDS //Make it so we play sounds now + if (!effectQuiet && !ispath(style, /datum/pod_style/seethrough)) + audible_message(span_notice("Капсула шипит, закрываясь и улетая прочь от станции."), span_notice("Земля вибрирует, и вы слышите звук работающих двигателей.")) + stay_after_drop = FALSE + holder.pixel_z = initial(holder.pixel_z) + holder.alpha = initial(holder.alpha) + if (holder != src) + contents |= holder.contents + qdel(holder) + var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/supplypod_temp_holding] + forceMove(shippingLane) //Move to the centcom-z-level until the pod_landingzone says we can drop back down again + if (custom_rev_delay) + delays = reverse_delays + backToNonReverseIcon() + var/turf/return_turf = locate(reverse_dropoff_coords[1], reverse_dropoff_coords[2], reverse_dropoff_coords[3]) + new /obj/effect/pod_landingzone(return_turf, src) + +/obj/structure/closet/supplypod/proc/preOpen() //Called before the open_pod() proc. Handles anything that occurs right as the pod lands. + var/turf/turf_underneath = get_turf(src) + var/list/B = explosionSize //Mostly because B is more readable than explosionSize :p + resistance_flags = initial(resistance_flags) + set_density(TRUE) //Density is originally false so the pod doesn't block anything while it's still falling through the air + AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude) + if(effectShrapnel) + SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED) + for (var/mob/living/target_living in turf_underneath) + if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people): + if (effectLimb && !effectOrgans && ishuman(target_living)) + var/mob/living/carbon/human/human_target_mob = target_living + var/obj/item/organ/external/bodypart + var/list/possible_organs = list() + for (var/bp in human_target_mob.bodyparts) + bodypart = bp + if(bodypart.limb_zone != BODY_ZONE_HEAD && bodypart.limb_zone != BODY_ZONE_CHEST \ + && bodypart.limb_zone != BODY_ZONE_PRECISE_GROIN && !(bodypart.cannot_amputate))//we dont want to kill him, just teach em a lesson! + possible_organs |= bp + if(possible_organs.len) + bodypart = pick(possible_organs) + bodypart.droplimb() + + if (effectOrgans) //effectOrgans means remove every organ in our mob + var/mob/living/carbon/carbon_target_mob = target_living + for(var/obj/item/organ/internal/organ_to_yeet as anything in carbon_target_mob.internal_organs) + var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs)) //Pick a random direction to toss them in + organ_to_yeet.remove(carbon_target_mob) //Note that this isn't the same proc as for lists + organ_to_yeet.forceMove(turf_underneath) //Move the organ outta the body + organ_to_yeet.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away + if(!ishuman(carbon_target_mob)) + continue + var/mob/living/carbon/human/human_target_mob = carbon_target_mob + for (var/bp in human_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands + var/obj/item/organ/external/bodypart = bp + var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs)) + if (!(bodypart.cannot_amputate)) + bodypart.droplimb()//Using the power of flextape i've sawed this man's bodypart in half! + bodypart.throw_at(destination, 2, 3) + + if (effectGib) //effectGib is on, that means whatever's underneath us better be fucking oof'd on + target_living.adjustBruteLoss(5000) //THATS A LOT OF DAMAGE (called just in case gib() doesnt work on em) + if (!QDELETED(target_living)) + target_living.gib() //After adjusting the fuck outta that brute loss we finish the job with some satisfying gibs + else + target_living.adjustBruteLoss(damage) + var/explosion_sum = B[1] + B[2] + B[3] + B[4] + if (explosion_sum != 0) //If the explosion list isn't all zeroes, call an explosion + explosion(turf_underneath, B[1], B[2], B[3], flame_range = B[4], silent = effectQuiet, ignorecap = istype(src, /obj/structure/closet/supplypod/centcompod), cause = src) //less advanced equipment than bluespace pod, so larger explosion when landing + else if (!effectQuiet && !(pod_flags & FIRST_SOUNDS)) //If our explosion list IS all zeroes, we still make a nice explosion sound (unless the effectQuiet var is true) + playsound(src, "explosion", landingSound ? soundVolume * 0.25 : soundVolume, TRUE) + if (landingSound) + playsound(turf_underneath, landingSound, soundVolume, FALSE, FALSE) + if (effectMissile) //If we are acting like a missile, then right after we land and finish fucking shit up w explosions, we should delete + opened = TRUE //We set opened to TRUE to avoid spending time trying to open (due to being deleted) during the Destroy() proc + qdel(src) + return + if (ispath(style, /datum/pod_style/gondola)) //Checks if we are supposed to be a gondola pod. If so, create a gondolapod mob, and move this pod to nullspace. I'd like to give a shout out, to my man oranges + var/mob/living/simple_animal/pet/gondola/gondolapod/benis = new(turf_underneath, src) + benis.contents |= contents //Move the contents of this supplypod into the gondolapod mob. + for (var/mob/living/mob_in_pod in benis.contents) + mob_in_pod.reset_perspective(null) + moveToNullspace() + addtimer(CALLBACK(src, PROC_REF(open_pod), benis), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob + else if (ispath(style, /datum/pod_style/seethrough)) + open_pod(src) + else + addtimer(CALLBACK(src, PROC_REF(open_pod), src), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplypod, while referencing this supplypod's contents + +/obj/structure/closet/supplypod/proc/open_pod(atom/movable/holder, broken = FALSE, forced = FALSE) //The holder var represents an atom whose contents we will be working with + if (!holder) + return + if (opened) //This is to ensure we don't open something that has already been opened + return + holder.setOpened() + var/turf/turf_underneath = get_turf(holder) //Get the turf of whoever's contents we're talking about + if (istype(holder, /mob)) //Allows mobs to assume the role of the holder, meaning we look at the mob's contents rather than the supplypod's contents. Typically by this point the supplypod's contents have already been moved over to the mob's contents + var/mob/holder_as_mob = holder + if (holder_as_mob.key && !forced && !broken) //If we are player controlled, then we shouldn't open unless the opening is manual, or if it is due to being destroyed (represented by the "broken" parameter) + return + if (openingSound) + playsound(get_turf(holder), openingSound, soundVolume, FALSE, FALSE) //Special admin sound to play + for (var/cargo in holder.contents) + var/atom/movable/movable_cargo = cargo + movable_cargo.forceMove(turf_underneath) + if (!effectQuiet && !openingSound && !ispath(style, /datum/pod_style/seethrough) && !(pod_flags & FIRST_SOUNDS)) //If we aren't being quiet, play the default pod open sound + playsound(get_turf(holder), open_sound, 15, TRUE, -3) + if (broken) //If the pod is opening because it's been destroyed, we end here + return + if (ispath(style, /datum/pod_style/seethrough)) + startExitSequence(src) + else + if (reversing) + addtimer(CALLBACK(src, PROC_REF(SetReverseIcon)), delays[POD_LEAVING]/2) //Finish up the pod's duties after a certain amount of time + if(!stay_after_drop) // Departing should be handled manually + addtimer(CALLBACK(src, PROC_REF(startExitSequence), holder), delays[POD_LEAVING]*(4/5)) //Finish up the pod's duties after a certain amount of time + +/obj/structure/closet/supplypod/proc/startExitSequence(atom/movable/holder) + if (leavingSound) + playsound(get_turf(holder), leavingSound, soundVolume, FALSE, FALSE) + if (reversing) //If we're reversing, we call the close proc. This sends the pod back up to centcom + close(holder) + else if (bluespace) //If we're a bluespace pod, then delete ourselves (along with our holder, if a separate holder exists) + deleteRubble() + if (!effectQuiet && !ispath(style, /datum/pod_style/invisible) && !ispath(style, /datum/pod_style/seethrough)) + do_sparks(5, TRUE, holder) //Create some sparks right before closing + qdel(src) //Delete ourselves and the holder + if (holder != src) + qdel(holder) + +/obj/structure/closet/supplypod/close(atom/movable/holder) //Closes the supplypod and sends it back to centcom. Should only ever be called if the "reversing" variable is true + if (!holder) + return + take_contents(holder) + playsound(holder, close_sound, soundVolume*0.75, TRUE, -3) + holder.setClosed() + addtimer(CALLBACK(src, PROC_REF(preReturn), holder), delays[POD_LEAVING] * 0.2) //Start to leave a bit after closing for cinematic effect + +/obj/structure/closet/supplypod/take_contents(atom/movable/holder) + var/turf/turf_underneath = holder.drop_location() + for(var/atom_to_check in turf_underneath) + if(atom_to_check != src && !insert(atom_to_check, holder)) // Can't insert that + continue + insert(turf_underneath, holder) + +/obj/structure/closet/supplypod/proc/insert(atom/to_insert, atom/movable/holder) + if(insertion_allowed(to_insert)) + var/atom/movable/movable_to_insert = to_insert + movable_to_insert.forceMove(holder) + return TRUE + else + return FALSE + +/obj/structure/closet/supplypod/proc/insertion_allowed(atom/to_insert) + if(to_insert.invisibility == INVISIBILITY_ABSTRACT) + return FALSE + if(ismob(to_insert)) + if(!reverse_option_list[MOB_OPTION]) + return FALSE + if(!isliving(to_insert)) //let's not put ghosts or camera mobs inside + return FALSE + var/mob/living/mob_to_insert = to_insert + if(mob_to_insert.anchored || mob_to_insert.incorporeal_move) + return FALSE + mob_to_insert.stop_pulling() + + else if(isobj(to_insert)) + var/obj/obj_to_insert = to_insert + if(issupplypod(obj_to_insert)) + return FALSE + if(istype(obj_to_insert, /obj/effect/supplypod_smoke)) + return FALSE + if(istype(obj_to_insert, /obj/effect/pod_landingzone)) + return FALSE + if(istype(obj_to_insert, /obj/effect/supplypod_rubble)) + return FALSE + if(istype(obj_to_insert, /obj/machinery/light)) + return FALSE + + if(!obj_to_insert.anchored && reverse_option_list[UNANCHORED_OPTION]) + return TRUE + if(obj_to_insert.anchored && !ismecha(obj_to_insert) && reverse_option_list[ANCHORED_OPTION]) //Mecha are anchored but there is a separate option for them + return TRUE + if(ismecha(obj_to_insert) && reverse_option_list[MECHA_OPTION]) + return TRUE + return FALSE + + else if (isturf(to_insert)) + return FALSE + return TRUE + +/obj/structure/closet/supplypod/proc/preReturn(atom/movable/holder) + deleteRubble() + animate(holder, alpha = 0, time = 8, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL) + animate(holder, pixel_z = 400, time = 10, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL) //Animate our rising pod + addtimer(CALLBACK(src, PROC_REF(handleReturnAfterDeparting), holder), 15) //Finish up the pod's duties after a certain amount of time + +/obj/structure/closet/supplypod/extractionpod/preReturn(atom/movable/holder) + // Double ensure we're loaded, this SHOULD be here by now but you never know + var/turf/picked_turf = pick(GLOB.ninja_teleport) + reverse_dropoff_coords = list(picked_turf.x, picked_turf.y, picked_turf.z) + return ..() + +/obj/structure/closet/supplypod/setOpened() //Proc exists here, as well as in any atom that can assume the role of a "holder" of a supplypod. Check the open_pod() proc for more details + opened = TRUE + set_density(FALSE) + update_appearance() + after_open(null, FALSE) + +/obj/structure/closet/supplypod/open() + return + +/obj/structure/closet/supplypod/extractionpod/setOpened() + opened = TRUE + set_density(TRUE) + update_appearance() + after_open(null, FALSE) + +/obj/structure/closet/supplypod/setClosed() //Ditto + opened = FALSE + set_density(TRUE) + update_appearance() + +/obj/structure/closet/supplypod/proc/tryMakeRubble(turf/T) //Ditto + if (rubble_type == RUBBLE_NONE) + return + if (rubble) + return + if (effectMissile) + return + if (isspaceturf(T) || iswallturf(T) || ismineralturf(T)) + return + rubble = new /obj/effect/supplypod_rubble(T) + rubble.setStyle(rubble_type, src) + update_appearance() + +/obj/structure/closet/supplypod/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + deleteRubble() + return ..() + +/obj/structure/closet/supplypod/proc/deleteRubble() + rubble?.fadeAway() + rubble = null + update_appearance() + +/obj/structure/closet/supplypod/proc/addGlow() + if (style::shape != POD_SHAPE_NORMAL) + return + glow_effect = new(src) + glow_effect.icon_state = "pod_glow_" + style::glow_color + vis_contents += glow_effect + glow_effect.layer = GASFIRE_LAYER + SET_PLANE_EXPLICIT(glow_effect, ABOVE_GAME_PLANE, src) + RegisterSignal(glow_effect, COMSIG_QDELETING, PROC_REF(remove_glow)) + +/obj/structure/closet/supplypod/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(same_z_layer) + return + if(glow_effect) + SET_PLANE_EXPLICIT(glow_effect, ABOVE_GAME_PLANE, src) + +/obj/structure/closet/supplypod/proc/endGlow() + if(!glow_effect) + return + glow_effect.layer = LOW_ITEM_LAYER + glow_effect.fadeAway(delays[POD_OPENING]) + //Trust the signals + +/obj/structure/closet/supplypod/proc/remove_glow() + SIGNAL_HANDLER + UnregisterSignal(glow_effect, COMSIG_QDELETING) + vis_contents -= glow_effect + glow_effect = null + +/obj/structure/closet/supplypod/Destroy() + deleteRubble() + //Trust the signals even harder + qdel(glow_effect) + open_pod(src, broken = TRUE) //Lets dump our contents by opening up + return ..() + +//------------------------------------TEMPORARY_VISUAL-------------------------------------// +/obj/effect/supplypod_smoke //Falling pod smoke + name = "" + icon = 'icons/obj/supplypods_32x32.dmi' + icon_state = "smoke" + desc = "" + layer = PROJECTILE_HIT_THRESHHOLD_LAYER + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 0 + +/obj/effect/engineglow //Falling pod smoke + name = "" + icon = 'icons/obj/supplypods.dmi' + icon_state = "pod_glow_green" + desc = "" + layer = GASFIRE_LAYER + plane = ABOVE_GAME_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + alpha = 255 + +/obj/effect/engineglow/proc/fadeAway(leaveTime) + var/duration = min(leaveTime, 25) + animate(src, alpha=0, time = duration) + QDEL_IN(src, duration + 5) + +/obj/effect/supplypod_smoke/proc/drawSelf(amount) + alpha = max(0, 255-(amount*20)) + +/obj/effect/supplypod_rubble + name = "debris" + desc = "Небольшой кратер из обломков. При ближайшем рассмотрении выясняется, что обломки состоят в основном из фрагментов металла. Вы почти уверены, что они скоро рассеется." + icon = 'icons/obj/supplypods.dmi' + layer = PROJECTILE_HIT_THRESHHOLD_LAYER // We want this to go right below the layer of supplypods and supplypod_rubble's forground. + icon_state = "rubble_bg" + anchored = TRUE + pixel_x = SUPPLYPOD_X_OFFSET + var/foreground = "rubble_fg" + var/verticle_offset = 0 + ru_names = list( + NOMINATIVE = "обломки", + GENITIVE = "обломков", + DATIVE = "обломкам", + ACCUSATIVE = "обломки", + INSTRUMENTAL = "обломками", + PREPOSITIONAL = "обломках" + ) + +/obj/effect/supplypod_rubble/proc/getForeground(obj/structure/closet/supplypod/pod) + var/mutable_appearance/rubble_overlay = mutable_appearance('icons/obj/supplypods.dmi', foreground) + rubble_overlay.appearance_flags = KEEP_APART|RESET_TRANSFORM + rubble_overlay.transform = matrix().Translate(SUPPLYPOD_X_OFFSET - pod.pixel_x, verticle_offset) + return rubble_overlay + +/obj/effect/supplypod_rubble/proc/fadeAway() + animate(src, alpha=0, time = 30) + QDEL_IN(src, 35) + +/obj/effect/supplypod_rubble/proc/setStyle(type, obj/structure/closet/supplypod/pod) + if (type == RUBBLE_WIDE) + icon_state += "_wide" + foreground += "_wide" + if (type == RUBBLE_THIN) + icon_state += "_thin" + foreground += "_thin" + if (ispath(pod.style, /datum/pod_style/box)) + verticle_offset = -2 + else + verticle_offset = initial(verticle_offset) + + pixel_y = verticle_offset + +/obj/effect/pod_landingzone_effect + name = "" + desc = "" + icon = 'icons/obj/supplypods_32x32.dmi' + icon_state = "LZ_Slider" + layer = PROJECTILE_HIT_THRESHHOLD_LAYER + +/obj/effect/pod_landingzone_effect/Initialize(mapload, obj/structure/closet/supplypod/pod) + . = ..() + if(!pod) + stack_trace("Pod landingzone effect created with no pod") + return INITIALIZE_HINT_QDEL + transform = matrix() * 1.5 + animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING]) + +/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to its location + name = "Landing Zone Indicator" + desc = "Голографическая проекция, обозначающая зону приземления чего-либо. Наверное, лучше стоять в стороне." + icon = 'icons/obj/supplypods_32x32.dmi' + icon_state = "LZ" + layer = PROJECTILE_HIT_THRESHHOLD_LAYER + light_range = 2 + anchored = TRUE + alpha = 0 + var/obj/structure/closet/supplypod/pod //The supplyPod that will be landing ontop of this pod_landingzone + var/obj/effect/pod_landingzone_effect/helper + var/list/smoke_effects = new /list(13) + ru_names = list( + NOMINATIVE = "индикатор зоны приземления", + GENITIVE = "индикатора зоны приземления", + DATIVE = "индикатору зоны приземления", + ACCUSATIVE = "индикатор зоны приземления", + INSTRUMENTAL = "индикатором зоны приземления", + PREPOSITIONAL = "индикаторе зоны приземления" + ) + +/obj/effect/pod_landingzone/Initialize(mapload, podParam, single_order = null, clientman) + . = ..() + if(!podParam) + stack_trace("Pod landingzone created with no pod") + return INITIALIZE_HINT_QDEL + if (ispath(podParam)) //We can pass either a path for a pod (as expressconsoles do), or a reference to an instantiated pod (as the centcom_podlauncher does) + podParam = new podParam() //If its just a path, instantiate it + pod = podParam + pod.resistance_flags |= (INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF) + if (!pod.effectStealth) + helper = new (drop_location(), pod) + alpha = 255 + animate(src, transform = matrix().Turn(90), time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING]) + if (single_order) + if (istype(single_order, /datum/supply_order)) + var/datum/supply_order/SO = single_order + if (SO.object.containertype) + SO.createObject(pod) + else if (istype(single_order, /atom/movable)) + var/atom/movable/O = single_order + O.forceMove(pod) + for (var/mob/living/mob_in_pod in pod) //If there are any mobs in the supplypod, we want to set their view to the pod_landingzone. This is so that they can see where they are about to land + mob_in_pod.reset_perspective(src) + if(pod.effectStun) //If effectStun is true, stun any mobs caught on this pod_landingzone until the pod gets a chance to hit them + for (var/mob/living/target_living in get_turf(src)) + target_living.AdjustWeakened(pod.delays[POD_TRANSIT] + 20, TRUE)//you ain't goin nowhere, kid. + target_living.AdjustStunned(pod.delays[POD_TRANSIT] + 20, TRUE) + if (pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING] < pod.fallingSoundLength) + pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound + pod.fallingSound = 'sound/weapons/mortar_whistle.ogg' + var/soundStartTime = pod.delays[POD_TRANSIT] - pod.fallingSoundLength + pod.delays[POD_FALLING] + if (soundStartTime < 0) + soundStartTime = 1 + if (!pod.effectQuiet && !(pod.pod_flags & FIRST_SOUNDS)) + addtimer(CALLBACK(src, PROC_REF(playFallingSound)), soundStartTime) + addtimer(CALLBACK(src, PROC_REF(beginLaunch), pod.effectCircle), pod.delays[POD_TRANSIT]) + +/obj/effect/pod_landingzone/proc/playFallingSound() + playsound(src, pod.fallingSound, pod.soundVolume, TRUE, 6) + +/obj/effect/pod_landingzone/proc/beginLaunch(effectCircle) //Begin the animation for the pod falling. The effectCircle param determines whether the pod gets to come in from any descent angle + pod.addGlow() + pod.update_appearance() + pod.forceMove(drop_location()) + for (var/mob/living/M in pod) //Remember earlier (initialization) when we moved mobs into the pod_landingzone so they wouldnt get lost in nullspace? Time to get them out + M.reset_perspective(null) + var/angle = effectCircle ? rand(0,360) : rand(70,110) //The angle that we can come in from + pod.pixel_x = cos(angle)*32*length(smoke_effects) //Use some ADVANCED MATHEMATICS to set the animated pod's position to somewhere on the edge of a circle with the center being the pod_landingzone + pod.pixel_z = sin(angle)*32*length(smoke_effects) + var/rotation = get_pixel_angle(pod.pixel_z, pod.pixel_x) //CUSTOM HOMEBREWED proc that is just arctan with extra steps + setupSmoke(rotation) + pod.transform = matrix().Turn(rotation) + pod.layer = FLY_LAYER + SET_PLANE_EXPLICIT(pod, ABOVE_GAME_PLANE, src) + if (!ispath(pod.style, /datum/pod_style/invisible)) + animate(pod, pixel_z = -1 * abs(sin(rotation))*4, pixel_x = SUPPLYPOD_X_OFFSET + (sin(rotation) * 20), time = pod.delays[POD_FALLING], easing = LINEAR_EASING) //Make the pod fall! At an angle! + addtimer(CALLBACK(src, PROC_REF(endLaunch)), pod.delays[POD_FALLING], TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation + +/obj/effect/pod_landingzone/proc/setupSmoke(rotation) + if (ispath(pod.style, /datum/pod_style/invisible) || ispath(pod.style, /datum/pod_style/seethrough)) + return + var/turf/our_turf = get_turf(drop_location()) + for ( var/i in 1 to length(smoke_effects)) + var/obj/effect/supplypod_smoke/smoke_part = new (drop_location()) + if (i == 1) + smoke_part.layer = FLY_LAYER + SET_PLANE(smoke_part, ABOVE_GAME_PLANE, our_turf) + smoke_part.icon_state = "smoke_start" + smoke_part.transform = matrix().Turn(rotation) + smoke_effects[i] = smoke_part + smoke_part.pixel_x = sin(rotation)*32 * i + smoke_part.pixel_y = abs(cos(rotation))*32 * i + smoke_part.add_filter("smoke_blur", 1, gauss_blur_filter(size = 4)) + var/time = (pod.delays[POD_FALLING] / length(smoke_effects))*(length(smoke_effects)-i) + addtimer(CALLBACK(smoke_part, TYPE_PROC_REF(/obj/effect/supplypod_smoke/, drawSelf), i), time, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation + QDEL_IN(smoke_part, pod.delays[POD_FALLING] + 35) + +/obj/effect/pod_landingzone/proc/drawSmoke() + if (ispath(pod.style, /datum/pod_style/invisible) || ispath(pod.style, /datum/pod_style/seethrough)) + return + for (var/obj/effect/supplypod_smoke/smoke_part in smoke_effects) + animate(smoke_part, alpha = 0, time = 20, flags = ANIMATION_PARALLEL) + animate(smoke_part.get_filter("smoke_blur"), size = 6, time = 15, easing = CUBIC_EASING|EASE_OUT, flags = ANIMATION_PARALLEL) + +/obj/effect/pod_landingzone/ex_act(severity) + return FALSE + +/obj/effect/pod_landingzone/proc/endLaunch() + var/turf/our_turf = get_turf(drop_location()) + pod.tryMakeRubble(drop_location()) + pod.layer = initial(pod.layer) + SET_PLANE(pod, initial(pod.plane), our_turf) + pod.endGlow() + QDEL_NULL(helper) + pod.preOpen() //Begin supplypod open procedures. Here effects like explosions, damage, and other dangerous (and potentially admin-caused, if the centcom_podlauncher datum was used) memes will take place + drawSmoke() + qdel(src) //The pod_landingzone's purpose is complete. It can rest easy now diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index cca75681750..43dea4d965c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1491,6 +1491,46 @@ debug_variables(stat_item) message_admins("Admin [key_name_admin(usr)] is debugging the [stat_item] [class].") +/client/proc/try_open_reagent_editor(atom/target) + var/target_UID = target.UID() + var/datum/reagents_editor/editor + // editors is static, it can be accessed using a null reference + editor = editor.editors[target_UID] + if(!editor) + editor = new /datum/reagents_editor(target) + editor.editors[target_UID] = editor + + editor.ui_interact(mob) + + +/client/proc/try_add_reagent(atom/target) + if(!target.reagents) + var/amount = tgui_input_number(usr, "Укажите размер хранилища реагентов для [target]", "Размер хранилища", 50) + if(amount) + target.create_reagents(amount) + var/chosen_id + var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) + switch(tgui_alert(usr, "Выберите метод.", "Добавить реагент", list("Ввести ID", "Выбрать ID"))) + if("Ввести ID") + var/valid_id + while(!valid_id) + chosen_id = tgui_input_text(usr, "Введите ID реагента, который хотите добавить.") + if(!chosen_id) //Get me out of here! + break + for(var/ID in reagent_options) + if(ID == chosen_id) + valid_id = 1 + if(!valid_id) + to_chat(usr, span_warning("Реагента с данным ID не существует!"), confidential=TRUE) + if("Выбрать ID") + chosen_id = tgui_input_list(usr, "Выберите реагент для добавления.", "Выберите реагент.", reagent_options) + if(chosen_id) + var/amount = tgui_input_number(usr, "Введите количество добавляемого реагента.", "Введите количество.", target.reagents.maximum_volume) + if(amount) + target.reagents.add_reagent(chosen_id, amount) + log_and_message_admins("has added [amount] units of [chosen_id] to \the [target]") + + #undef LIMITER_SIZE #undef CURRENT_SECOND #undef SECOND_COUNT diff --git a/code/modules/client/preference/loadout/gear_tweaks.dm b/code/modules/client/preference/loadout/gear_tweaks.dm index a22307f4730..961739bfac6 100644 --- a/code/modules/client/preference/loadout/gear_tweaks.dm +++ b/code/modules/client/preference/loadout/gear_tweaks.dm @@ -1,3 +1,11 @@ +/datum/gear_tweak + /// Displayed in TGUI name + var/display_type + /// Font Awesome icon + var/fa_icon + /// Explains what is this do in TGUI tooltip + var/info + /datum/gear_tweak/proc/get_contents(var/metadata) return @@ -7,13 +15,16 @@ /datum/gear_tweak/proc/get_default() return +/datum/gear_tweak/proc/get_tgui_data(param) + return + /datum/gear_tweak/proc/update_gear_intro() return /datum/gear_tweak/proc/tweak_gear_data(var/metadata, var/datum/gear_data) return -/datum/gear_tweak/proc/tweak_item(var/obj/item/I, var/metadata) +/datum/gear_tweak/proc/tweak_item(obj/item/gear, metadata) return /* @@ -21,45 +32,59 @@ */ /datum/gear_tweak/color + display_type = "Color" + fa_icon = "palette" + info = "Recolorable" var/list/valid_colors var/datum/gear/parent -/datum/gear_tweak/color/New(var/list/colors, datum/gear/parent) +/datum/gear_tweak/color/New(list/colors, datum/gear/parent) valid_colors = colors src.parent = parent ..() -/datum/gear_tweak/color/get_contents(var/metadata) +/datum/gear_tweak/color/get_contents(metadata) return "Color: " /datum/gear_tweak/color/get_default() return valid_colors ? valid_colors[1] : COLOR_WHITE -/datum/gear_tweak/color/get_metadata(var/user, var/metadata) +/datum/gear_tweak/color/get_metadata(user, metadata) if(valid_colors) - metadata = input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors + metadata = tgui_input_list(user, "Choose an item color.", "Character Preference", valid_colors, metadata) else - metadata = input(user, "Choose an item color.", "Global Preference", metadata) as color|null + metadata = tgui_input_color(user, "Choose an item color.", "Global Preference", metadata) update_gear_intro(metadata) return metadata -/datum/gear_tweak/color/update_gear_intro(var/color) +/datum/gear_tweak/color/get_tgui_data(param) + var/tgui_data = list() + if(!param) + return tgui_data + tgui_data["display_param"] = param + tgui_data["icon"] = parent.base64icon + return tgui_data + +/datum/gear_tweak/color/update_gear_intro(color) parent.update_gear_icon(color) -/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata) - if(valid_colors && !(metadata in valid_colors)) +/datum/gear_tweak/color/tweak_item(obj/item/gear, metadata) + if((valid_colors && !(metadata in valid_colors)) || !metadata) return - I.color = metadata + gear.color = metadata /* * Path adjustment */ /datum/gear_tweak/path + display_type = "Subtype" + fa_icon = "bars" + info = "Has subtypes" var/list/valid_paths = list() var/datum/gear/parent -/datum/gear_tweak/path/New(var/list/paths, datum/gear/parent, name = FALSE) +/datum/gear_tweak/path/New(list/paths, datum/gear/parent, name = FALSE) if(name) for(var/atom/path as anything in paths) valid_paths[initial(path.name)] = path @@ -68,67 +93,63 @@ src.parent = parent ..() -/datum/gear_tweak/path/get_contents(var/metadata) +/datum/gear_tweak/path/get_contents(metadata) return "Type: [metadata]" /datum/gear_tweak/path/get_default() return valid_paths[1] -/datum/gear_tweak/path/get_metadata(var/user, var/metadata) - metadata = input(user, "Choose a type.", "Character Preference", metadata) as null|anything in valid_paths +/datum/gear_tweak/path/get_metadata(user, metadata) + metadata = tgui_input_list(user, "Choose a type.", "Character Preference", valid_paths, metadata) update_gear_intro(metadata) return metadata -/datum/gear_tweak/path/update_gear_intro(var/path) +/datum/gear_tweak/path/update_gear_intro(path) parent.path = valid_paths[path] parent.update_gear_icon() -/datum/gear_tweak/path/tweak_gear_data(var/metadata, var/datum/gear_data/gear_data) +/datum/gear_tweak/path/get_tgui_data(param) + var/tgui_data = list() + if(!param) + return tgui_data + tgui_data["display_param"] = param + var/obj/item/path = valid_paths[param] + tgui_data["icon_file"] = path.icon + tgui_data["icon_state"] = path.icon_state + tgui_data["name"] = path.name + return tgui_data + +/datum/gear_tweak/path/tweak_gear_data(metadata, datum/gear_data/gear_data) if(!(metadata in valid_paths)) return gear_data.path = valid_paths[metadata] -/* -* Content adjustment -*/ +// MARK: Rename +/datum/gear_tweak/rename + display_type = "Name" + fa_icon = "edit" + info = "Renameable" -/datum/gear_tweak/contents - var/list/valid_contents +/datum/gear_tweak/rename/get_default() + return "" -/datum/gear_tweak/contents/New() - valid_contents = args.Copy() - ..() -/datum/gear_tweak/contents/get_contents(var/metadata) - return "Contents: [english_list(metadata, and_text = ", ")]" - -/datum/gear_tweak/contents/get_default() - . = list() - for(var/i = 1 to valid_contents.len) - . += "Random" - -/datum/gear_tweak/contents/get_metadata(var/user, var/list/metadata) - . = list() - for(var/i = metadata.len to valid_contents.len) - metadata += "Random" - for(var/i = 1 to valid_contents.len) - var/entry = input(user, "Choose an entry.", "Character Preference", metadata[i]) as null|anything in (valid_contents[i] + list("Random", "None")) - if(entry) - . += entry - else - return metadata - -/datum/gear_tweak/contents/tweak_item(var/obj/item/I, var/list/metadata) - if(metadata.len != valid_contents.len) +/datum/gear_tweak/rename/get_metadata(user, metadata) + var/new_name = tgui_input_text(user, "Rename an object. Enter empty line for stock name", "Rename Gear", metadata, MAX_NAME_LEN) + if(isnull(new_name)) + return metadata + return new_name + +/datum/gear_tweak/rename/get_tgui_data(param) + var/tgui_data = list() + if(!param) + return tgui_data + tgui_data["display_param"] = param + tgui_data["name"] = param + return tgui_data + +/datum/gear_tweak/rename/tweak_item(obj/item/gear, metadata) + if(!metadata) return - for(var/i = 1 to valid_contents.len) - var/path - var/list/contents = valid_contents[i] - if(metadata[i] == "Random") - path = pick(contents) - path = contents[path] - else if(metadata[i] == "None") - continue - else - path = contents[metadata[i]] - new path(I) + + gear.name = metadata diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm index 0128066203c..a638c7fa01a 100644 --- a/code/modules/client/preference/loadout/loadout.dm +++ b/code/modules/client/preference/loadout/loadout.dm @@ -1,16 +1,8 @@ -GLOBAL_LIST_EMPTY(loadout_categories) GLOBAL_LIST_EMPTY(gear_datums) -/datum/loadout_category - var/category = "" - var/list/gear = list() - -/datum/loadout_category/New(cat) - category = cat - ..() - /datum/gear - var/display_name //Name/index. Must be unique. + var/index_name //index. Must be unique. + var/display_name = "bug" //Name var/description //Description of this gear. If left blank will default to the description of the pathed item. var/atom/path //Path to item. var/icon_state //Icon state of item @@ -24,6 +16,7 @@ GLOBAL_LIST_EMPTY(gear_datums) var/subtype_path = /datum/gear //for skipping organizational subtypes (optional) var/subtype_cost_overlap = TRUE //if subtypes can take points at the same time var/implantable = FALSE //For organ-like implants (huds, pumps, etc) + var/donator_tier = 0 /datum/gear/New() ..() @@ -33,6 +26,12 @@ GLOBAL_LIST_EMPTY(gear_datums) /datum/gear/proc/update_gear_icon(color) + var/gear_icon = get_gear_icon(color) + if(!gear_icon) + return + base64icon = gear_icon + +/datum/gear/proc/get_gear_icon(color) if(initial(icon) && initial(icon_state)) return icon_state = path::icon_state @@ -44,7 +43,10 @@ GLOBAL_LIST_EMPTY(gear_datums) var/icon/new_icon = icon(icon, icon_state, SOUTH, 1, FALSE) if(color) new_icon.Blend(color, ICON_MULTIPLY) - base64icon = icon2base64(new_icon) + return icon2base64(new_icon) + +/datum/gear/proc/get_display_name() + return ((display_name == /datum/gear::display_name)? path.name : display_name) /datum/gear_data var/path @@ -55,12 +57,12 @@ GLOBAL_LIST_EMPTY(gear_datums) location = nlocation /datum/gear/proc/spawn_item(location, metadata) - var/datum/gear_data/gd = new(path, location) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_gear_data(metadata["[gt]"], gd) - var/item = new gd.path(gd.location) - for(var/datum/gear_tweak/gt in gear_tweaks) - gt.tweak_item(item, metadata["[gt]"]) + var/datum/gear_data/gear_data = new(path, location) + for(var/datum/gear_tweak/tweak in gear_tweaks) + tweak.tweak_gear_data(metadata["[tweak]"], gear_data) + var/item = new gear_data.path(gear_data.location) + for(var/datum/gear_tweak/tweak in gear_tweaks) + tweak.tweak_item(item, metadata["[tweak]"]) return item /datum/gear/proc/can_select(client/cl, job_name, species_name, silent = FALSE) @@ -71,7 +73,7 @@ GLOBAL_LIST_EMPTY(gear_datums) return TRUE if(cl && !silent) - to_chat(cl, span_warning("\"[capitalize(display_name)]\" недоступно для вашей профессии!")) + to_chat(cl, span_warning("\"[capitalize(get_display_name())]\" недоступно для вашей профессии!")) return FALSE diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm index ed820949674..fa8ee75e686 100644 --- a/code/modules/client/preference/loadout/loadout_accessories.dm +++ b/code/modules/client/preference/loadout/loadout_accessories.dm @@ -4,7 +4,8 @@ sort_category = "Accessories" /datum/gear/accessory/scarf - display_name = "scarf, select" + index_name = "scarf, select" + display_name = "scarf" path = /obj/item/clothing/accessory/scarf/red /datum/gear/accessory/scarf/New() @@ -23,7 +24,8 @@ gear_tweaks += new /datum/gear_tweak/path(scarfs, src, TRUE) /datum/gear/accessory/scarfstriped - display_name = "striped scarf, select" + index_name = "striped scarf, select" + display_name = "striped scarf" path = /obj/item/clothing/accessory/stripedredscarf /datum/gear/accessory/scarfstriped/New() @@ -34,22 +36,23 @@ gear_tweaks += new /datum/gear_tweak/path(scarfs, src, TRUE) /datum/gear/accessory/holobadge - display_name = "holobadge, pin" + index_name = "holobadge, pin" path = /obj/item/clothing/accessory/holobadge allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/accessory/holobadge_n - display_name = "holobadge, cord" + index_name = "holobadge, cord" path = /obj/item/clothing/accessory/holobadge/cord allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/accessory/holobadge/detective - display_name = "holobadge, detective" + index_name = "holobadge, detective" path = /obj/item/clothing/accessory/holobadge/detective allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_DETECTIVE) /datum/gear/accessory/tie - display_name = "tie, select" + index_name = "tie, select" + display_name = "tie" path = /obj/item/clothing/accessory/blue /datum/gear/accessory/tie/New() @@ -61,21 +64,21 @@ gear_tweaks += new /datum/gear_tweak/path(ties, src, TRUE) /datum/gear/accessory/stethoscope - display_name = "stethoscope" + index_name = "stethoscope" path = /obj/item/clothing/accessory/stethoscope allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/ntrjacket - display_name = "jacket, nt rep" + index_name = "jacket, nt rep" path = /obj/item/clothing/accessory/ntrjacket allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/accessory/waistcoat - display_name = "waistcoat" + index_name = "waistcoat" path = /obj/item/clothing/accessory/waistcoat /datum/gear/accessory/cowboyshirt - display_name = "cowboy shirt, select" + index_name = "cowboy shirt, select" path = /obj/item/clothing/accessory/cowboyshirt /datum/gear/accessory/cowboyshirt/New() @@ -93,15 +96,16 @@ gear_tweaks += new /datum/gear_tweak/path(shirts, src, TRUE) /datum/gear/accessory/locket - display_name = "gold locket" + index_name = "gold locket" path = /obj/item/clothing/accessory/necklace/locket /datum/gear/accessory/necklace - display_name = "simple necklace" + index_name = "simple necklace" path = /obj/item/clothing/accessory/necklace /datum/gear/accessory/corset - display_name = "corset, select" + index_name = "corset, select" + display_name = "corset" path = /obj/item/clothing/accessory/corset /datum/gear/accessory/corset/New() @@ -113,11 +117,11 @@ gear_tweaks += new /datum/gear_tweak/path(corsets, src, TRUE) /datum/gear/accessory/armband_red - display_name = "armband" + index_name = "armband" path = /obj/item/clothing/accessory/armband /datum/gear/accessory/armband_civ - display_name = "armband, blue-yellow" + index_name = "armband, blue-yellow" path = /obj/item/clothing/accessory/armband/yb /datum/gear/accessory/armband_job @@ -125,42 +129,43 @@ subtype_cost_overlap = FALSE /datum/gear/accessory/armband_job/sec - display_name = " armband, security" + index_name = " armband, security" path = /obj/item/clothing/accessory/armband/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT) /datum/gear/accessory/armband_job/cargo - display_name = "cargo armband" + index_name = "cargo armband" path = /obj/item/clothing/accessory/armband/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH, JOB_TITLE_MINER) /datum/gear/accessory/armband_job/medical - display_name = "armband, medical" + index_name = "armband, medical" path = /obj/item/clothing/accessory/armband/med allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CORONER, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/armband_job/emt - display_name = "armband, EMT" + index_name = "armband, EMT" path = /obj/item/clothing/accessory/armband/medgreen allowed_roles = list(JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/accessory/armband_job/engineering - display_name = "armband, engineering" + index_name = "armband, engineering" path = /obj/item/clothing/accessory/armband/engine allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ATMOSTECH, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/accessory/armband_job/hydro - display_name = "armband, hydroponics" + index_name = "armband, hydroponics" path = /obj/item/clothing/accessory/armband/hydro allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/accessory/armband_job/sci - display_name = "armband, science" + index_name = "armband, science" path = /obj/item/clothing/accessory/armband/science allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST) /datum/gear/accessory/holsters - display_name = "holster, select" + index_name = "holster, select" + display_name = "holster" path = /obj/item/clothing/accessory/holster/ allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm index a5d99260b3a..60bbad929d5 100644 --- a/code/modules/client/preference/loadout/loadout_donor.dm +++ b/code/modules/client/preference/loadout/loadout_donor.dm @@ -1,5 +1,5 @@ /datum/gear/donor - var/donator_tier = 2 + donator_tier = 2 sort_category = "Donor" subtype_path = /datum/gear/donor @@ -8,7 +8,7 @@ return FALSE if(!donator_tier) // why are you here?.. allowed, but - stack_trace("Item with no donator tier in loadout donor items: [display_name].") + stack_trace("Item with no donator tier in loadout donor items: [index_name].") return TRUE if(!cl.prefs) // DB loading, skip this check now @@ -18,7 +18,7 @@ return TRUE if(cl && !silent) - to_chat(cl, span_warning("Для получения \"[display_name]\" необходим [donator_tier] или более высокий уровень пожертвований.")) + to_chat(cl, span_warning("Для получения \"[index_name]\" необходим [donator_tier] или более высокий уровень пожертвований.")) return FALSE @@ -30,127 +30,127 @@ /datum/gear/donor/ussptracksuit_black donator_tier = 1 cost = 1 - display_name = "track suit (black)" + index_name = "track suit (black)" path = /obj/item/clothing/under/ussptracksuit_black /datum/gear/donor/ussptracksuit_white donator_tier = 1 cost = 1 - display_name = "track suit (white)" + index_name = "track suit (white)" path = /obj/item/clothing/under/ussptracksuit_white /datum/gear/donor/kittyears - display_name = "Kitty ears" + index_name = "Kitty ears" path = /obj/item/clothing/head/kitty /datum/gear/donor/leather_trenchcoat - display_name = "Leather Trenchcoat" + index_name = "Leather Trenchcoat" path = /obj/item/clothing/suit/storage/leather_trenchcoat/runner donator_tier = 2 cost = 1 /datum/gear/donor/furgloves - display_name = "Fur Gloves" + index_name = "Fur Gloves" path = /obj/item/clothing/gloves/furgloves /datum/gear/donor/furboots - display_name = "Fur Boots" + index_name = "Fur Boots" path = /obj/item/clothing/shoes/furboots /datum/gear/donor/noble_boot - display_name = "Noble Boots" + index_name = "Noble Boots" path = /obj/item/clothing/shoes/fluff/noble_boot /datum/gear/donor/furcape - display_name = "Fur Cape" + index_name = "Fur Cape" path = /obj/item/clothing/neck/cloak/furcape /datum/gear/donor/furcoat - display_name = "Fur Coat" + index_name = "Fur Coat" path = /obj/item/clothing/suit/furcoat /datum/gear/donor/kamina - display_name = "Spiky Orange-tinted Shades" + index_name = "Spiky Orange-tinted Shades" path = /obj/item/clothing/glasses/fluff/kamina /datum/gear/donor/green - display_name = "Spiky Green-tinted Shades" + index_name = "Spiky Green-tinted Shades" path = /obj/item/clothing/glasses/fluff/kamina/green /datum/gear/donor/threedglasses - display_name = "Threed Glasses" + index_name = "Threed Glasses" path = /obj/item/clothing/glasses/threedglasses /datum/gear/donor/blacksombrero - display_name = "Black Sombrero" + index_name = "Black Sombrero" path = /obj/item/clothing/head/fluff/blacksombrero /datum/gear/donor/guardhelm - display_name = "Plastic Guard helm" + index_name = "Plastic Guard helm" path = /obj/item/clothing/head/fluff/guardhelm /datum/gear/donor/goldtophat - display_name = "Gold-trimmed Top Hat" + index_name = "Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat /datum/gear/donor/goldtophat/red - display_name = "Red Gold-trimmed Top Hat" + index_name = "Red Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat/red /datum/gear/donor/goldtophat/blue - display_name = "Blue Gold-trimmed Top Hat" + index_name = "Blue Gold-trimmed Top Hat" path = /obj/item/clothing/head/fluff/goldtophat/blue /datum/gear/donor/mushhat - display_name = "Mushroom Hat" + index_name = "Mushroom Hat" path = /obj/item/clothing/head/fluff/mushhat /datum/gear/donor/furcap - display_name = "Fur Cap" + index_name = "Fur Cap" path = /obj/item/clothing/head/furcap /datum/gear/donor/mouse - display_name = "Mouse Headband" + index_name = "Mouse Headband" path = /obj/item/clothing/head/kitty/mouse /datum/gear/donor/fawkes - display_name = "Guy Fawkes mask" + index_name = "Guy Fawkes mask" path = /obj/item/clothing/mask/face/fawkes /datum/gear/donor/bigbrother - display_name = "Spraycan Big Brother" + index_name = "Spraycan Big Brother" path = /obj/item/toy/crayon/spraycan/paintkit/bigbrother /datum/gear/donor/slavic - display_name = "Spraycan Slavic" + index_name = "Spraycan Slavic" path = /obj/item/toy/crayon/spraycan/paintkit/slavic /datum/gear/donor/id_decal_silver - display_name = "Silver ID Decal" + index_name = "Silver ID Decal" path = /obj/item/id_decal/silver donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_prisoner - display_name = "Prisoner ID Decal" + index_name = "Prisoner ID Decal" path = /obj/item/id_decal/prisoner donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_emag - display_name = "Emag ID Decal" + index_name = "Emag ID Decal" path = /obj/item/id_decal/emag donator_tier = 3 cost = 1 /datum/gear/donor/id_decal_gold - display_name = "Gold ID Decal" + index_name = "Gold ID Decal" path = /obj/item/id_decal/gold donator_tier = 4 cost = 1 /datum/gear/donor/zippolghtr - display_name = "Zippo lighter" + index_name = "Zippo lighter" path = /obj/item/lighter/zippo donator_tier = 1 cost = 1 @@ -160,102 +160,108 @@ subtype_cost_overlap = FALSE /datum/gear/donor/strip/cap - display_name = "strip, Captain" + index_name = "strip, Captain" path = /obj/item/clothing/accessory/head_strip donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/donor/strip/rd - display_name = "strip, Research Director" + index_name = "strip, Research Director" path = /obj/item/clothing/accessory/head_strip/rd donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_RD) /datum/gear/donor/strip/ce - display_name = "strip, Chief Engineer" + index_name = "strip, Chief Engineer" path = /obj/item/clothing/accessory/head_strip/ce donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/donor/strip/t4ce - display_name = "strip, Grand Chief Engineer" + index_name = "strip, Grand Chief Engineer" path = /obj/item/clothing/accessory/head_strip/t4ce donator_tier = 4 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/donor/strip/cmo - display_name = "strip, Chief Medical Officer" + index_name = "strip, Chief Medical Officer" path = /obj/item/clothing/accessory/head_strip/cmo donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CMO) /datum/gear/donor/strip/hop - display_name = "strip, Head of Personnel" + index_name = "strip, Head of Personnel" path = /obj/item/clothing/accessory/head_strip/hop donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_HOP) /datum/gear/donor/strip/hos - display_name = "strip, Head of Security" + index_name = "strip, Head of Security" path = /obj/item/clothing/accessory/head_strip/hos donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_HOS) /datum/gear/donor/strip/qm - display_name = "strip, Quartermaster" + index_name = "strip, Quartermaster" path = /obj/item/clothing/accessory/head_strip/qm donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/donor/strip/clown - display_name = "strip, Clown" + index_name = "strip, Clown" path = /obj/item/clothing/accessory/head_strip/clown donator_tier = 2 cost = 1 allowed_roles = list(JOB_TITLE_CLOWN) /datum/gear/donor/strip/bs - display_name = "strip, Blueshield" + index_name = "strip, Blueshield" path = /obj/item/clothing/accessory/head_strip/bs donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/donor/strip/ntr - display_name = "strip, NanoTrasen Representative" + index_name = "strip, NanoTrasen Representative" path = /obj/item/clothing/accessory/head_strip/ntr donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/donor/strip/syndi - display_name = "strip, Syndicate" + index_name = "strip, Syndicate" path = /obj/item/clothing/accessory/head_strip/syndicate donator_tier = 3 cost = 1 /datum/gear/donor/strip/comrad - display_name = "strip, SSSP" + index_name = "strip, SSSP" path = /obj/item/clothing/accessory/head_strip/comrad donator_tier = 3 cost = 1 /datum/gear/donor/strip/federal - display_name = "strip, TSF" + index_name = "strip, TSF" path = /obj/item/clothing/accessory/head_strip/federal donator_tier = 3 cost = 1 +/datum/gear/donor/strip/greytide + index_name = "strip, GreyTide" + path = /obj/item/clothing/accessory/head_strip/greytide + donator_tier = 3 + cost = 1 + /datum/gear/donor/heartglasses - display_name = "heart-shaped glasses, color" + index_name = "heart-shaped glasses, color" path = /obj/item/clothing/glasses/heart donator_tier = 3 cost = 1 @@ -266,7 +272,7 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/donor/heart_meson - display_name = "Heart Meson Glasses" + index_name = "Heart Meson Glasses" path = /obj/item/clothing/glasses/meson/heart donator_tier = 4 cost = 2 @@ -274,7 +280,7 @@ allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ATMOSTECH, JOB_TITLE_MECHANIC, JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER, JOB_TITLE_CAPTAIN, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/donor/heart_science - display_name = "Heart Science Glasses" + index_name = "Heart Science Glasses" path = /obj/item/clothing/glasses/science/heart donator_tier = 4 cost = 2 @@ -282,7 +288,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_SCIENTIST, JOB_TITLE_ROBOTICIST, JOB_TITLE_RD, JOB_TITLE_GENETICIST, JOB_TITLE_CHEMIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/donor/heart_health - display_name = "Heart Medical Glasses" + index_name = "Heart Medical Glasses" path = /obj/item/clothing/glasses/hud/health/heart donator_tier = 4 cost = 2 @@ -290,7 +296,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_CMO, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BLUESHIELD, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_DOCTOR, JOB_TITLE_CORONER) /datum/gear/donor/heart_diagnostic - display_name = "Heart Diagnostic Glasses" + index_name = "Heart Diagnostic Glasses" path = /obj/item/clothing/glasses/hud/diagnostic/heart donator_tier = 4 cost = 2 @@ -298,7 +304,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/donor/heart_security - display_name = "Heart Security Glasses" + index_name = "Heart Security Glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/heart donator_tier = 4 cost = 2 @@ -306,7 +312,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_DETECTIVE, JOB_TITLE_PILOT, JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_BLUESHIELD, JOB_TITLE_JUDGE, JOB_TITLE_OFFICER) /datum/gear/donor/heartsec_read - display_name = "Heart Security Glasses" + index_name = "Heart Security Glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/heart/read_only donator_tier = 4 cost = 2 @@ -314,7 +320,7 @@ allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/donor/heart_hydroponic - display_name = "Heart Hydroponic Glasses" + index_name = "Heart Hydroponic Glasses" path = /obj/item/clothing/glasses/hud/heart donator_tier = 4 cost = 2 @@ -322,7 +328,7 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_BOTANIST) /datum/gear/donor/heart_skills - display_name = "Heart Skills Glasses" + index_name = "Heart Skills Glasses" path = /obj/item/clothing/glasses/hud/skills/heart donator_tier = 4 cost = 2 @@ -330,7 +336,8 @@ allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_BLUESHIELD, JOB_TITLE_HOP) /datum/gear/donor/night_dress - display_name = "night dress, select" + index_name = "night dress, select" + display_name = "night dress" description = "A classic night dress." cost = 1 donator_tier = 3 @@ -346,14 +353,14 @@ gear_tweaks += new /datum/gear_tweak/path(skirts, src) /datum/gear/donor/strip/cheese_badge - display_name = "strip, Great fellow" + index_name = "strip, Great fellow" path = /obj/item/clothing/accessory/head_strip/cheese_badge donator_tier = 4 cost = 1 allowed_roles = list(JOB_TITLE_CAPTAIN, JOB_TITLE_QUARTERMASTER, JOB_TITLE_RD, JOB_TITLE_HOS, JOB_TITLE_HOP, JOB_TITLE_CMO, JOB_TITLE_CHIEF, JOB_TITLE_REPRESENTATIVE, JOB_TITLE_JUDGE) /datum/gear/donor/smile_pin - display_name = "smiling pin" + index_name = "smiling pin" path = /obj/item/clothing/accessory/medal/smile donator_tier = 4 cost = 1 @@ -361,67 +368,67 @@ /datum/gear/donor/backpack_hiking donator_tier = 3 cost = 1 - display_name = "backpack, Fancy Hiking Pack" + index_name = "backpack, Fancy Hiking Pack" path = /obj/item/storage/backpack/fluff/hiking /datum/gear/donor/backpack_brew donator_tier = 3 cost = 1 - display_name = "backpack, The brew" + index_name = "backpack, The brew" path = /obj/item/storage/backpack/fluff/thebrew /datum/gear/donor/backpack_cat donator_tier = 3 cost = 1 - display_name = "backpack, CatPack" + index_name = "backpack, CatPack" path = /obj/item/storage/backpack/fluff/ssscratches_back /datum/gear/donor/backpack_voxcaster donator_tier = 3 cost = 1 - display_name = "backpack, Voxcaster" + index_name = "backpack, Voxcaster" path = /obj/item/storage/backpack/fluff/krich_back /datum/gear/donor/backpack_syndi donator_tier = 3 cost = 1 - display_name = "backpack, Military Satchel" + index_name = "backpack, Military Satchel" path = /obj/item/storage/backpack/fluff/syndiesatchel /datum/gear/donor/spacecloak donator_tier = 3 cost = 1 - display_name = "Space cloak" + index_name = "Space cloak" path = /obj/item/clothing/neck/cloak/spacecloak /datum/gear/donor/golden_wheelchair donator_tier = 4 cost = 1 - display_name = "Golden wheelchair paintkit" + index_name = "Golden wheelchair paintkit" path = /obj/item/fluff/rapid_wheelchair_kit /datum/gear/donor/hazardbelt - display_name = "hazard vest alt" + index_name = "hazard vest alt" path = /obj/item/clothing/suit/storage/hazardvest/beltdonor donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER) /datum/gear/donor/atmosbelt - display_name = "hazard vest alt (atmos)" + index_name = "hazard vest alt (atmos)" path = /obj/item/clothing/suit/storage/hazardvest/beltdonor/atmos donator_tier = 3 cost = 1 allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/donor/beaver - display_name = "Beaver Plushie" + index_name = "Beaver Plushie" path = /obj/item/toy/plushie/beaver donator_tier = 3 cost = 1 /datum/gear/donor/earring_NT - display_name = "Earrings NT" + index_name = "Earrings NT" path = /obj/item/clothing/ears/earrings/Nt donator_tier = 3 cost = 1 @@ -429,47 +436,47 @@ /datum/gear/donor/hijab donator_tier = 1 cost = 1 - display_name = "hijab" + index_name = "hijab" path = /obj/item/clothing/suit/hooded/hijab /datum/gear/donor/steampunkdress donator_tier = 1 cost = 1 - display_name = "victorian blue-white dress" + index_name = "victorian blue-white dress" path = /obj/item/clothing/under/steampunkdress /datum/gear/donor/plaidhoodie_green donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, green" + index_name = "Plaid hoodie, green" path = /obj/item/clothing/suit/hoodie/plaidhoodie_green /datum/gear/donor/plaidhoodie_white donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, white" + index_name = "Plaid hoodie, white" path = /obj/item/clothing/suit/hoodie/plaidhoodie_white /datum/gear/donor/plaidhoodie_red donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, red" + index_name = "Plaid hoodie, red" path = /obj/item/clothing/suit/hoodie/plaidhoodie_red /datum/gear/donor/plaidhoodie_yellow donator_tier = 1 cost = 1 - display_name = "Plaid hoodie, yellow" + index_name = "Plaid hoodie, yellow" path = /obj/item/clothing/suit/hoodie/plaidhoodie_yellow /datum/gear/donor/blackcoat donator_tier = 2 cost = 2 - display_name = "Black Coat" + index_name = "Black Coat" path = /obj/item/clothing/suit/blackcoat /datum/gear/donor/pda_beer - display_name = "PDA case \"BEER\"" + index_name = "PDA case \"BEER\"" path = /obj/item/pda_case/beer donator_tier = 1 cost = 1 @@ -477,24 +484,24 @@ /datum/gear/donor/maid donator_tier = 2 cost = 1 - display_name = "Short maid costume" + index_name = "Short maid costume" path = /obj/item/clothing/under/maid/short /datum/gear/donor/rdplushie donator_tier = 3 cost = 1 - display_name = "RD doll" + index_name = "RD doll" path = /obj/item/toy/plushie/rdplushie /datum/gear/donor/gsbplushie donator_tier = 3 cost = 1 - display_name = "GSBussy doll" + index_name = "GSBussy doll" path = /obj/item/toy/plushie/gsbplushie /datum/gear/donor/backpack_shitsec donator_tier = 3 cost = 1 - display_name = "backpack of justice" + index_name = "backpack of justice" path = /obj/item/storage/backpack/justice allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_general.dm b/code/modules/client/preference/loadout/loadout_general.dm index 774e92169fe..2143c72aeff 100644 --- a/code/modules/client/preference/loadout/loadout_general.dm +++ b/code/modules/client/preference/loadout/loadout_general.dm @@ -1,39 +1,40 @@ /datum/gear/dice - display_name = "a d20" + index_name = "a d20" path = /obj/item/dice/d20 /datum/gear/uplift - display_name = "a pack of Uplifts" + index_name = "a pack of Uplifts" path = /obj/item/storage/fancy/cigarettes/cigpack_uplift /datum/gear/robust - display_name = "a pack of Robusts" + index_name = "a pack of Robusts" path = /obj/item/storage/fancy/cigarettes/cigpack_robust /datum/gear/carp - display_name = "a pack of Carps" + index_name = "a pack of Carps" path = /obj/item/storage/fancy/cigarettes/cigpack_carp /datum/gear/midori - display_name = "a pack of Midoris" + index_name = "a pack of Midoris" path = /obj/item/storage/fancy/cigarettes/cigpack_midori /datum/gear/smokingpipe - display_name = "smoking pipe" + index_name = "smoking pipe" path = /obj/item/clothing/mask/cigarette/pipe cost = 2 /datum/gear/robustpipe - display_name = "robust smoking pipe" + index_name = "robust smoking pipe" path = /obj/item/clothing/mask/cigarette/pipe/oldpipe cost = 2 /datum/gear/lighter - display_name = "a cheap lighter" + index_name = "a cheap lighter" path = /obj/item/lighter /datum/gear/earrings - display_name = "earrings, select" + index_name = "earrings, select" + display_name = "earrings" path = /obj/item/clothing/ears/earrings /datum/gear/earrings/New() @@ -44,62 +45,63 @@ gear_tweaks += new /datum/gear_tweak/path(earrings, src) /datum/gear/matches - display_name = "a box of matches" + index_name = "a box of matches" path = /obj/item/storage/box/matches /datum/gear/candlebox - display_name = "a box candles" + index_name = "a box candles" description = "For setting the mood or for occult rituals." path = /obj/item/storage/fancy/candle_box/full /datum/gear/camera - display_name = "a camera" + index_name = "a camera" path = /obj/item/camera /datum/gear/sechud - display_name = "a classic security HUD" + index_name = "a classic security HUD" path = /obj/item/clothing/glasses/hud/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE) /datum/gear/read_only_sechud - display_name = "a classic security HUD (read-only)" + index_name = "a classic security HUD (read-only)" path = /obj/item/clothing/glasses/hud/security/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/cryaonbox - display_name = "a box of crayons" + index_name = "a box of crayons" path = /obj/item/storage/fancy/crayons /datum/gear/cane - display_name = "a walking cane" + index_name = "a walking cane" path = /obj/item/cane /datum/gear/cards - display_name = "a deck of standard cards" + index_name = "a deck of standard cards" path = /obj/item/deck/cards /datum/gear/doublecards - display_name = "a double deck of standard cards" + index_name = "a double deck of standard cards" path = /obj/item/deck/cards/doublecards /datum/gear/tarot - display_name = "a deck of tarot cards" + index_name = "a deck of tarot cards" path = /obj/item/deck/tarot /datum/gear/headphones - display_name = "a pair of headphones" + index_name = "a pair of headphones" path = /obj/item/clothing/ears/headphones /datum/gear/fannypack - display_name = "a fannypack" + index_name = "a fannypack" path = /obj/item/storage/belt/fannypack /datum/gear/wallet - display_name = "a wallet(leather)" + index_name = "a wallet(leather)" path = /obj/item/storage/wallet /datum/gear/wallet/color - display_name = "a wallet, select" + index_name = "a wallet, select" + display_name = "a wallet" path = /obj/item/storage/wallet/color/blue /datum/gear/wallet/color/New() @@ -114,7 +116,8 @@ gear_tweaks += new /datum/gear_tweak/path(wallets, src) /datum/gear/bandana - display_name = "bandana, select" + index_name = "bandana, select" + display_name = "bandana" path = /obj/item/clothing/mask/bandana/black /datum/gear/bandana/New() @@ -131,16 +134,17 @@ gear_tweaks += new /datum/gear_tweak/path(bands, src) /datum/gear/piano_synth - display_name ="synthesizer" + index_name ="synthesizer" path = /obj/item/instrument/piano_synth cost = 2 /datum/gear/tts - display_name ="TTS device" + index_name ="TTS device" path = /obj/item/ttsdevice /datum/gear/lipstick - display_name = "lipstick, select" + index_name = "lipstick, select" + display_name = "lipstick" path = /obj/item/lipstick /datum/gear/lipstick/New() @@ -159,18 +163,18 @@ ////////////////////// /datum/gear/mug - display_name = "random coffee mug" + index_name = "random coffee mug" description = "A randomly colored coffee mug. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/mug /datum/gear/novelty_mug - display_name = "novelty coffee mug" + index_name = "novelty coffee mug" description = "A random novelty coffee mug. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/mug/novelty cost = 2 /datum/gear/mug/flask - display_name = "flask" + index_name = "flask" description = "A flask for drink transportation. You'll need to supply your own beverage though." path = /obj/item/reagent_containers/food/drinks/flask/barflask @@ -179,30 +183,30 @@ subtype_cost_overlap = FALSE /datum/gear/mug/department/eng - display_name = "engineer coffee mug" + index_name = "engineer coffee mug" description = "An engineer's coffee mug, emblazoned in the colors of the Engineering department." allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) path = /obj/item/reagent_containers/food/drinks/mug/eng /datum/gear/mug/department/med - display_name = "doctor coffee mug" + index_name = "doctor coffee mug" description = "A doctor's coffee mug, emblazoned in the colors of the Medical department." allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_CORONER) path = /obj/item/reagent_containers/food/drinks/mug/med /datum/gear/mug/department/sci - display_name = "scientist coffee mug" + index_name = "scientist coffee mug" description = "A scientist's coffee mug, emblazoned in the colors of the Science department." allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST) path = /obj/item/reagent_containers/food/drinks/mug/sci /datum/gear/mug/department/sec - display_name = "officer coffee mug" + index_name = "officer coffee mug" description = "An officer's coffee mug, emblazoned in the colors of the Security department." allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_BRIGDOC, JOB_TITLE_PILOT, JOB_TITLE_LAWYER) path = /obj/item/reagent_containers/food/drinks/mug/sec /datum/gear/mug/department/serv - display_name = "crewmember coffee mug" + index_name = "crewmember coffee mug" description = "A crewmember's coffee mug, emblazoned in the colors of the Service department." path = /obj/item/reagent_containers/food/drinks/mug/serv diff --git a/code/modules/client/preference/loadout/loadout_glasses.dm b/code/modules/client/preference/loadout/loadout_glasses.dm index 7cb1fd8037b..a11f2d9918d 100644 --- a/code/modules/client/preference/loadout/loadout_glasses.dm +++ b/code/modules/client/preference/loadout/loadout_glasses.dm @@ -4,15 +4,15 @@ sort_category = "Glasses" /datum/gear/glasses/sunglasses - display_name = "cheap sunglasses" + index_name = "cheap sunglasses" path = /obj/item/clothing/glasses/sunglasses_fake /datum/gear/glasses/eyepatch - display_name = "Eyepatch" + index_name = "Eyepatch" path = /obj/item/clothing/glasses/eyepatch /datum/gear/glasses/blindfold - display_name = "Blindfold" + index_name = "Blindfold" path = /obj/item/clothing/glasses/sunglasses/blindfold /datum/gear/glasses/blindfold/New() @@ -20,7 +20,7 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/glasses/blindfold_fake - display_name = "Fake blindfold" + index_name = "Fake blindfold" path = /obj/item/clothing/glasses/sunglasses/blindfold_fake /datum/gear/glasses/blindfold_fake/New() @@ -28,49 +28,49 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/glasses/hipster - display_name = "Hipster glasses" + index_name = "Hipster glasses" path = /obj/item/clothing/glasses/regular/hipster /datum/gear/glasses/monocle - display_name = "Monocle" + index_name = "Monocle" path = /obj/item/clothing/glasses/monocle /datum/gear/glasses/prescription - display_name = "Prescription glasses" + index_name = "Prescription glasses" path = /obj/item/clothing/glasses/regular /datum/gear/glasses/sectacticool - display_name = "Security tactical glasses" + index_name = "Security tactical glasses" path = /obj/item/clothing/glasses/hud/security/sunglasses/tacticool allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/glasses/medhudpatch - display_name = "Medical HUD eyepatch" + index_name = "Medical HUD eyepatch" path = /obj/item/clothing/glasses/hud/health/patch allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER) /datum/gear/glasses/sechudpatch - display_name = "Security HUD eyepatch" + index_name = "Security HUD eyepatch" path = /obj/item/clothing/glasses/hud/security/patch allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE, JOB_TITLE_DETECTIVE) /datum/gear/glasses/sechudpatch/read_only - display_name = "Security HUD eyepatch (read only)" + index_name = "Security HUD eyepatch (read only)" path = /obj/item/clothing/glasses/hud/security/patch/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/glasses/hydrohudpatch - display_name = "Hydroponic HUD eyepatch" + index_name = "Hydroponic HUD eyepatch" path = /obj/item/clothing/glasses/hud/hydroponic/patch allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/glasses/diaghudpatch - display_name = "Diagnostic HUD eyepatch" + index_name = "Diagnostic HUD eyepatch" path = /obj/item/clothing/glasses/hud/diagnostic/patch allowed_roles = list(JOB_TITLE_ROBOTICIST, JOB_TITLE_RD) /datum/gear/glasses/skillhudpatch - display_name = "Skills HUD eyepatch" + index_name = "Skills HUD eyepatch" path = /obj/item/clothing/glasses/hud/skills/patch allowed_roles = list(JOB_TITLE_HOP, JOB_TITLE_CAPTAIN) diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index 8b08281e1a8..8d676a0f992 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -4,17 +4,17 @@ sort_category = "Gloves" /datum/gear/gloves/fingerless - display_name = "Fingerless Gloves" + index_name = "Fingerless Gloves" path = /obj/item/clothing/gloves/fingerless /datum/gear/gloves/silverring - display_name = "Silver ring" + index_name = "Silver ring" path = /obj/item/clothing/gloves/ring/silver /datum/gear/gloves/goldring - display_name = "Gold ring" + index_name = "Gold ring" path = /obj/item/clothing/gloves/ring/gold /datum/gear/gloves/brown_short_gloves - display_name = "short leather gloves" + index_name = "short leather gloves" path = /obj/item/clothing/gloves/brown_short_gloves diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index 56318992417..06b5bfc8e62 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -4,7 +4,8 @@ sort_category = "Headwear" /datum/gear/hat/hhat - display_name = "hardhat, select" + index_name = "hardhat, select" + display_name = "hardhat" path = /obj/item/clothing/head/hardhat allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) @@ -16,35 +17,36 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src) /datum/gear/hat/that - display_name = "top hat" + index_name = "top hat" path = /obj/item/clothing/head/that /datum/gear/hat/flatcap - display_name = "flat cap" + index_name = "flat cap" path = /obj/item/clothing/head/flatcap /datum/gear/hat/ushanka - display_name = "ushanka" + index_name = "ushanka" path = /obj/item/clothing/head/ushanka /datum/gear/hat/witch - display_name = "witch hat" + index_name = "witch hat" path = /obj/item/clothing/head/wizard/marisa/fake /datum/gear/hat/piratecaphat - display_name = "pirate captian hat" + index_name = "pirate captian hat" path = /obj/item/clothing/head/pirate /datum/gear/hat/fez - display_name = "fez" + index_name = "fez" path = /obj/item/clothing/head/fez /datum/gear/hat/rasta - display_name = "rasta hat" + index_name = "rasta hat" path = /obj/item/clothing/head/beanie/rasta /datum/gear/hat/fedora - display_name = "fedora, select" + index_name = "fedora, select" + display_name = "fedora" path = /obj/item/clothing/head/fedora /datum/gear/hat/fedora/New() @@ -55,17 +57,18 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/capcsec - display_name = "security corporate cap" + index_name = "security corporate cap" path = /obj/item/clothing/head/soft/sec/corp allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/capsec - display_name = "security cap" + index_name = "security cap" path = /obj/item/clothing/head/soft/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/capred - display_name = "cap, select" + index_name = "cap, select" + display_name = "cap" path = /obj/item/clothing/head/soft/red /datum/gear/hat/capred/New() @@ -82,7 +85,8 @@ /obj/item/clothing/head/soft/solgov,) gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/cowboyhat - display_name = "cowboy hat, select" + index_name = "cowboy hat, select" + display_name = "cowboy hat" path = /obj/item/clothing/head/cowboyhat /datum/gear/hat/cowboyhat/New() @@ -95,7 +99,8 @@ gear_tweaks += new /datum/gear_tweak/path(hats, src, TRUE) /datum/gear/hat/beret - display_name = "beret, select" + index_name = "beret, select" + display_name = "beret" path = /obj/item/clothing/head/beret /datum/gear/hat/beret/New() @@ -111,47 +116,48 @@ subtype_cost_overlap = FALSE /datum/gear/hat/beret_job/sec - display_name = "security beret" + index_name = "security beret" path = /obj/item/clothing/head/beret/sec allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beret_job/sec_black - display_name = "black security beret" + index_name = "black security beret" path = /obj/item/clothing/head/beret/sec/black allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beret_job/marine - display_name = "royal marines commando beret" + index_name = "royal marines commando beret" path = /obj/item/clothing/head/beret/centcom/officer/sparkyninja_beret allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_BLUESHIELD) /datum/gear/hat/beret_job/marine_old - display_name = "marine lieutenant beret" + index_name = "marine lieutenant beret" path = /obj/item/clothing/head/beret/centcom/officer/sigholt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_BLUESHIELD) /datum/gear/hat/beret_job/sci - display_name = "science beret" + index_name = "science beret" path = /obj/item/clothing/head/beret/sci allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_GENETICIST) /datum/gear/hat/beret_job/med - display_name = "medical beret" + index_name = "medical beret" path = /obj/item/clothing/head/beret/med allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER, JOB_TITLE_PARAMEDIC, JOB_TITLE_CHEMIST, JOB_TITLE_GENETICIST, JOB_TITLE_PSYCHIATRIST) /datum/gear/hat/beret_job/eng - display_name = "engineering beret" + index_name = "engineering beret" path = /obj/item/clothing/head/beret/eng allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE) /datum/gear/hat/beret_job/atmos - display_name = "atmospherics beret" + index_name = "atmospherics beret" path = /obj/item/clothing/head/beret/atmos allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/hat/surgicalcap - display_name = "surgical cap, select" + index_name = "surgical cap, select" + display_name = "surgical cap" path = /obj/item/clothing/head/surgery/purple allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN) @@ -163,16 +169,17 @@ gear_tweaks += new /datum/gear_tweak/path(caps, src) /datum/gear/hat/flowerpin - display_name = "hair flower" + index_name = "hair flower" path = /obj/item/clothing/head/hairflower /datum/gear/hat/lwhelmet - display_name = "security lightweight helmet" + index_name = "security lightweight helmet" path = /obj/item/clothing/head/helmet/lightweighthelmet allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/hat/beanie - display_name = "beanie, select" + index_name = "beanie, select" + display_name = "beanie" path = /obj/item/clothing/head/beanie /datum/gear/hat/beanie/New() diff --git a/code/modules/client/preference/loadout/loadout_implants.dm b/code/modules/client/preference/loadout/loadout_implants.dm index ddbdd98a788..a78a4ec0a20 100644 --- a/code/modules/client/preference/loadout/loadout_implants.dm +++ b/code/modules/client/preference/loadout/loadout_implants.dm @@ -9,27 +9,27 @@ //Eye implants /datum/gear/implant/meson - display_name = "Meson Scanner Implant" + index_name = "Meson Scanner Implant" path = /obj/item/organ/internal/cyberimp/eyes/meson allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH, JOB_TITLE_ENGINEER, JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER) /datum/gear/implant/security - display_name = "Security Hud Implant" + index_name = "Security Hud Implant" cost = 3 path = /obj/item/organ/internal/cyberimp/eyes/hud/security allowed_roles = list(JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN, JOB_TITLE_HOS, JOB_TITLE_JUDGE) /datum/gear/implant/medical - display_name = "Medical Hud Implant" + index_name = "Medical Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/medical allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_CHEMIST, JOB_TITLE_DOCTOR, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC, JOB_TITLE_VIROLOGIST) /datum/gear/implant/diagnostic - display_name = "Diagnostical Hud Implant" + index_name = "Diagnostical Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/implant/science - display_name = "Science Hud Implant" + index_name = "Science Hud Implant" path = /obj/item/organ/internal/cyberimp/eyes/hud/science allowed_roles = list(JOB_TITLE_CHEMIST, JOB_TITLE_SCIENTIST, JOB_TITLE_RD, JOB_TITLE_GENETICIST) diff --git a/code/modules/client/preference/loadout/loadout_neck.dm b/code/modules/client/preference/loadout/loadout_neck.dm index e43ba657a5d..cd45dc31907 100644 --- a/code/modules/client/preference/loadout/loadout_neck.dm +++ b/code/modules/client/preference/loadout/loadout_neck.dm @@ -5,7 +5,7 @@ //Mantles /datum/gear/neck/mantle - display_name = "mantle, color" + index_name = "mantle, color" path = /obj/item/clothing/neck/mantle /datum/gear/neck/mantle/New() @@ -13,15 +13,15 @@ gear_tweaks += new /datum/gear_tweak/color(parent = src) /datum/gear/neck/mantle/old_scarf - display_name = "old scarf" + index_name = "old scarf" path = /obj/item/clothing/neck/mantle/old /datum/gear/neck/mantle/regal_shawl - display_name = "regal shawl" + index_name = "regal shawl" path = /obj/item/clothing/neck/mantle/regal /datum/gear/neck/mantle/cowboy_mantle - display_name = "old wrappings" + index_name = "old wrappings" path = /obj/item/clothing/neck/mantle/cowboy /datum/gear/neck/mantle/job @@ -29,38 +29,38 @@ subtype_cost_overlap = FALSE /datum/gear/neck/mantle/job/captain - display_name = "mantle, captain" + index_name = "mantle, captain" path = /obj/item/clothing/neck/mantle/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/neck/mantle/job/chief_engineer - display_name = "mantle, chief engineer" + index_name = "mantle, chief engineer" path = /obj/item/clothing/neck/mantle/chief_engineer allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/mantle/job/chief_medical_officer - display_name = "mantle, chief medical officer" + index_name = "mantle, chief medical officer" path = /obj/item/clothing/neck/mantle/chief_medical_officer allowed_roles = list(JOB_TITLE_CMO) /datum/gear/neck/mantle/job/head_of_security - display_name = "mantle, head of security" + index_name = "mantle, head of security" path = /obj/item/clothing/neck/mantle/head_of_security allowed_roles = list(JOB_TITLE_HOS) /datum/gear/neck/mantle/job/head_of_personnel - display_name = "mantle, head of personnel" + index_name = "mantle, head of personnel" path = /obj/item/clothing/neck/mantle/head_of_personnel allowed_roles = list(JOB_TITLE_HOP) /datum/gear/neck/mantle/job/research_director - display_name = "mantle, research director" + index_name = "mantle, research director" path = /obj/item/clothing/neck/mantle/research_director allowed_roles = list(JOB_TITLE_RD) //Cloaks /datum/gear/neck/cloak - display_name = "cloak, color" + index_name = "cloak, color" path = /obj/item/clothing/neck/cloak/grey /datum/gear/neck/cloak/New() @@ -72,68 +72,69 @@ subtype_cost_overlap = FALSE /datum/gear/neck/cloak/job/healer - display_name = "cloak, healer" + index_name = "cloak, healer" path = /obj/item/clothing/neck/cloak/healer allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PARAMEDIC, JOB_TITLE_BRIGDOC) /datum/gear/neck/cloak/job/captain - display_name = "cloak, captain" + index_name = "cloak, captain" path = /obj/item/clothing/neck/cloak/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/neck/cloak/job/nanotrasen_representative - display_name = "cloak, nanotrasen representative" + index_name = "cloak, nanotrasen representative" path = /obj/item/clothing/neck/cloak/nanotrasen_representative allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/neck/cloak/job/blueshield - display_name = "cloak, blueshield" + index_name = "cloak, blueshield" path = /obj/item/clothing/neck/cloak/blueshield allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/neck/cloak/job/chief_engineer - display_name = "cloak, chief engineer" + index_name = "cloak, chief engineer" path = /obj/item/clothing/neck/cloak/chief_engineer allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/cloak/job/chief_engineer/white - display_name = "cloak, chief engineer, white" + index_name = "cloak, chief engineer, white" path = /obj/item/clothing/neck/cloak/chief_engineer/white allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/neck/cloak/job/chief_medical_officer - display_name = "cloak, chief medical officer" + index_name = "cloak, chief medical officer" path = /obj/item/clothing/neck/cloak/chief_medical_officer allowed_roles = list(JOB_TITLE_CMO) /datum/gear/neck/cloak/job/head_of_security - display_name = "cloak, head of security" + index_name = "cloak, head of security" path = /obj/item/clothing/neck/cloak/head_of_security allowed_roles = list(JOB_TITLE_HOS) /datum/gear/neck/cloaksecurity - display_name = "cloak, security officer" + index_name = "cloak, security officer" path = /obj/item/clothing/neck/cloak/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_PILOT) /datum/gear/neck/cloak/job/head_of_personnel - display_name = "cloak, head of personnel" + index_name = "cloak, head of personnel" path = /obj/item/clothing/neck/cloak/head_of_personnel allowed_roles = list(JOB_TITLE_HOP) /datum/gear/neck/cloak/job/research_director - display_name = "cloak, research director" + index_name = "cloak, research director" path = /obj/item/clothing/neck/cloak/research_director allowed_roles = list(JOB_TITLE_RD) /datum/gear/neck/cloak/job/quartermaster - display_name = "cloak, quartermaster" + index_name = "cloak, quartermaster" path = /obj/item/clothing/neck/cloak/quartermaster allowed_roles = list(JOB_TITLE_QUARTERMASTER) //Ponchos /datum/gear/neck/poncho - display_name = "poncho, select" + index_name = "poncho, select" + display_name = "poncho" path = /obj/item/clothing/neck/poncho /datum/gear/neck/poncho/New() @@ -152,7 +153,7 @@ gear_tweaks += new /datum/gear_tweak/path(ponchos, src, TRUE) /datum/gear/neck/poncho/security - display_name = "poncho, corporate" + index_name = "poncho, corporate" path = /obj/item/clothing/neck/poncho/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_OFFICER, JOB_TITLE_WARDEN, JOB_TITLE_PILOT) diff --git a/code/modules/client/preference/loadout/loadout_plushie.dm b/code/modules/client/preference/loadout/loadout_plushie.dm index e90ab2146e2..e1ede04e043 100644 --- a/code/modules/client/preference/loadout/loadout_plushie.dm +++ b/code/modules/client/preference/loadout/loadout_plushie.dm @@ -4,47 +4,48 @@ cost = 1 /datum/gear/plushie/rock - display_name = "a pet rock" + index_name = "a pet rock" path = /obj/item/toy/pet_rock /datum/gear/plushie/redfoxplushie - display_name = "a red fox plushie" + index_name = "a red fox plushie" path = /obj/item/toy/plushie/red_fox /datum/gear/plushie/blackcatplushie - display_name = "a black cat plushie" + index_name = "a black cat plushie" path = /obj/item/toy/plushie/black_cat /datum/gear/plushie/voxplushie - display_name = "a vox plushie" + index_name = "a vox plushie" path = /obj/item/toy/plushie/voxplushie /datum/gear/plushie/lizardplushie - display_name = "a lizard plushie" + index_name = "a lizard plushie" path = /obj/item/toy/plushie/lizardplushie /datum/gear/plushie/deerplushie - display_name = "a deer plushie" + index_name = "a deer plushie" path = /obj/item/toy/plushie/deer /datum/gear/plushie/carpplushie - display_name = "a carp plushie" + index_name = "a carp plushie" path = /obj/item/toy/carpplushie /datum/gear/plushie/nianplushie - display_name = "Nian plushie" + index_name = "Nian plushie" path = /obj/item/toy/plushie/nianplushie /datum/gear/plushie/bubblegumplushie - display_name = "Bubblegum plushie" + index_name = "Bubblegum plushie" path = /obj/item/toy/plushie/bubblegumplushie /datum/gear/plushie/greyplushie - display_name = "Grey Plushie" + index_name = "Grey Plushie" path = /obj/item/toy/plushie/greyplushie /datum/gear/plushie/plasmamanplushie - display_name = "Plasmaman Plushie, select" + index_name = "Plasmaman Plushie, select" + display_name = "Plasmaman Plushie" path = /obj/item/toy/plushie/plasmamanplushie /datum/gear/plushie/plasmamanplushie/New() @@ -65,61 +66,61 @@ gear_tweaks += new /datum/gear_tweak/path(plasmamans, src, TRUE) /datum/gear/plushie/shardplushie - display_name = "Shard Plushie" + index_name = "Shard Plushie" path = /obj/item/toy/plushie/shardplushie /datum/gear/plushie/akulaplushie - display_name = "Akula Plushie" + index_name = "Akula Plushie" path = /obj/item/toy/plushie/blahaj/twohanded cost = 2 /datum/gear/plushie/hampter - display_name = "Hampter" + index_name = "Hampter" path = /obj/item/toy/plushie/hampter cost = 1 /datum/gear/plushie/hampter_assistant - display_name = "Hampter, Assitant" + index_name = "Hampter, Assitant" path = /obj/item/toy/plushie/hampter/asisstant cost = 1 /datum/gear/plushie/hampter_security - display_name = "Hampter, Security" + index_name = "Hampter, Security" path = /obj/item/toy/plushie/hampter/security cost = 1 /datum/gear/plushie/hampter_medic - display_name = "Hampter, Doctor" + index_name = "Hampter, Doctor" path = /obj/item/toy/plushie/hampter/medic cost = 1 /datum/gear/plushie/hampter_janitor - display_name = "Hampter, Janitor" + index_name = "Hampter, Janitor" path = /obj/item/toy/plushie/hampter/janitor cost = 1 /datum/gear/plushie/hampter_captain - display_name = "Hampter, Captain" + index_name = "Hampter, Captain" path = /obj/item/toy/plushie/hampter/captain cost = 1 /datum/gear/plushie/hampter_old_captain - display_name = "Hampter, Old Captain" + index_name = "Hampter, Old Captain" path = /obj/item/toy/plushie/hampter/captain/old cost = 1 /datum/gear/plushie/hampter_syndi - display_name = "Hampter, Syndi" + index_name = "Hampter, Syndi" path = /obj/item/toy/plushie/hampter/syndi cost = 1 /datum/gear/plushie/hampter_death_squad - display_name = "Hampter, Grandpa" + index_name = "Hampter, Grandpa" path = /obj/item/toy/plushie/hampter/death_squad cost = 1 /datum/gear/plushie/hampter_ert_squad - display_name = "Hampter, ERT" + index_name = "Hampter, ERT" path = /obj/item/toy/plushie/hampter/ert_squad cost = 1 diff --git a/code/modules/client/preference/loadout/loadout_racial.dm b/code/modules/client/preference/loadout/loadout_racial.dm index 9bfa31cf463..a8e12060c17 100644 --- a/code/modules/client/preference/loadout/loadout_racial.dm +++ b/code/modules/client/preference/loadout/loadout_racial.dm @@ -9,7 +9,7 @@ return FALSE if(!LAZYLEN(whitelisted_species)) // why are we here? allowed, but - stack_trace("Item with no racial list in loadout racial items: [display_name].") + stack_trace("Item with no racial list in loadout racial items: [index_name].") return TRUE if(!species_name) // skip @@ -19,7 +19,7 @@ return TRUE if(cl && !silent) - to_chat(cl, span_warning("Ваш вид не подходит для того, чтобы использовать \"[display_name]\"!")) + to_chat(cl, span_warning("Ваш вид не подходит для того, чтобы использовать \"[index_name]\"!")) return FALSE @@ -28,8 +28,10 @@ return "\[Species: [english_list(whitelisted_species)]\] " + // TAJARAN // + /datum/gear/racial/taj - display_name = "embroidered veil" + index_name = "embroidered veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races." path = /obj/item/clothing/glasses/tajblind slot = ITEM_SLOT_EYES @@ -41,57 +43,75 @@ cost = 2 /datum/gear/racial/taj/job/bot - display_name = "veil, blooming" + index_name = "veil, blooming" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built botanical HUD." path = /obj/item/clothing/glasses/hud/hydroponic/tajblind allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/racial/taj/job/sec - display_name = "veil, sleek" + index_name = "veil, sleek" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built security HUD." path = /obj/item/clothing/glasses/hud/security/sunglasses/tajblind allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_JUDGE) /datum/gear/racial/taj/job/iaa - display_name = "veil, sleek(read-only)" + index_name = "veil, sleek(read-only)" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built security HUD." path = /obj/item/clothing/glasses/hud/security/sunglasses/tajblind/read_only allowed_roles = list(JOB_TITLE_LAWYER) /datum/gear/racial/taj/job/med - display_name = "veil, lightweight" + index_name = "veil, lightweight" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built medical HUD." path = /obj/item/clothing/glasses/hud/health/tajblind allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC, JOB_TITLE_CORONER) /datum/gear/racial/taj/job/sci - display_name = "veil, hi-tech" + index_name = "veil, hi-tech" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built science goggles" path = /obj/item/clothing/glasses/tajblind/sci allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT, JOB_TITLE_ROBOTICIST, JOB_TITLE_GENETICIST, JOB_TITLE_CHEMIST) /datum/gear/racial/taj/job/eng - display_name = "veil, industrial" + index_name = "veil, industrial" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built optical meson scanners and welding shields." path = /obj/item/clothing/glasses/tajblind/eng allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC, JOB_TITLE_ATMOSTECH) /datum/gear/racial/taj/job/cargo - display_name = "veil, khaki" + index_name = "veil, khaki" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built optical meson scanners." path = /obj/item/clothing/glasses/tajblind/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/racial/taj/job/diag - display_name = "veil, diagnostic" + index_name = "veil, diagnostic" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built diagnostic HUD." path = /obj/item/clothing/glasses/hud/diagnostic/tajblind allowed_roles = list(JOB_TITLE_ROBOTICIST, JOB_TITLE_RD) /datum/gear/racial/taj/job/skills - display_name = "veil, skills" + index_name = "veil, skills" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built skills HUD." path = /obj/item/clothing/glasses/hud/skills/tajblind allowed_roles = list(JOB_TITLE_HOP, JOB_TITLE_CAPTAIN) +// GREY // + +/datum/gear/racial/language_chip + index_name = "selected language chip" + description = "Крошечный чип-переводчик с индикатором, содержащий в себе один из языков. Разработан греями, устанавливается в импланты-переводчики." + path = /obj/item/translator_chip/sol + whitelisted_species = list(SPECIES_GREY) + + +/datum/gear/racial/language_chip/New() + . = ..() + + var/list/available_chips = list() + for(var/obj/item/translator_chip/chip as anything in subtypesof(/obj/item/translator_chip)) + available_chips[chip.stored_language_rus] = chip + + gear_tweaks += new /datum/gear_tweak/path(available_chips, src) + diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index 8245e9371b7..9bec05ed938 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -4,31 +4,32 @@ sort_category = "Shoes" /datum/gear/shoes/sandals - display_name = "sandals, wooden" + index_name = "sandals, wooden" path = /obj/item/clothing/shoes/sandal /datum/gear/shoes/winterboots - display_name = "winter boots" + index_name = "winter boots" path = /obj/item/clothing/shoes/winterboots /datum/gear/shoes/workboots - display_name = "work boots" + index_name = "work boots" path = /obj/item/clothing/shoes/workboots /datum/gear/shoes/leather - display_name = "leather shoes" + index_name = "leather shoes" path = /obj/item/clothing/shoes/leather /datum/gear/shoes/fancysandals - display_name = "sandals, fancy" + index_name = "sandals, fancy" path = /obj/item/clothing/shoes/sandal/fancy /datum/gear/shoes/dressshoes - display_name = "dress shoes" + index_name = "dress shoes" path = /obj/item/clothing/shoes/centcom /datum/gear/shoes/cowboyboots - display_name = "cowboy boots, select" + index_name = "cowboy boots, select" + display_name = "cowboy boots" path = /obj/item/clothing/shoes/cowboy /datum/gear/shoes/cowboyboots/New() @@ -40,19 +41,20 @@ gear_tweaks += new /datum/gear_tweak/path(boots, src) /datum/gear/shoes/jackboots - display_name = "jackboots" + index_name = "jackboots" path = /obj/item/clothing/shoes/jackboots /datum/gear/shoes/jacksandals - display_name = "jacksandals" + index_name = "jacksandals" path = /obj/item/clothing/shoes/jackboots/jacksandals /datum/gear/shoes/laceup - display_name = "laceup shoes" + index_name = "laceup shoes" path = /obj/item/clothing/shoes/laceup /datum/gear/shoes/shoes - display_name = "shoes, select" + index_name = "shoes, select" + display_name = "shoes" path = /obj/item/clothing/shoes/black /datum/gear/shoes/shoes/New() @@ -63,15 +65,15 @@ gear_tweaks += new /datum/gear_tweak/path(boots, src, TRUE) /datum/gear/shoes/jackcross - display_name = "jackcross" + index_name = "jackcross" path = /obj/item/clothing/shoes/jackboots/cross /datum/gear/shoes/leather_boots - display_name = "high leather boots" + index_name = "high leather boots" path = /obj/item/clothing/shoes/leather_boots /datum/gear/shoes/footwraps - display_name = "cloth footwraps, color" + index_name = "cloth footwraps, color" path = /obj/item/clothing/shoes/footwraps /datum/gear/shoes/footwraps/New() diff --git a/code/modules/client/preference/loadout/loadout_suit.dm b/code/modules/client/preference/loadout/loadout_suit.dm index 32f9586498f..bae2d9940c7 100644 --- a/code/modules/client/preference/loadout/loadout_suit.dm +++ b/code/modules/client/preference/loadout/loadout_suit.dm @@ -8,7 +8,7 @@ subtype_path = /datum/gear/suit/coat /datum/gear/suit/coat/grey - display_name = "winter coat" + index_name = "winter coat" path = /obj/item/clothing/suit/hooded/wintercoat /datum/gear/suit/coat/job @@ -16,109 +16,110 @@ subtype_cost_overlap = FALSE /datum/gear/suit/coat/job/sec - display_name = "winter coat, security" + index_name = "winter coat, security" path = /obj/item/clothing/suit/hooded/wintercoat/security allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT, JOB_TITLE_BRIGDOC) /datum/gear/suit/coat/job/hos - display_name = "winter coat, head of security" + index_name = "winter coat, head of security" path = /obj/item/clothing/suit/hooded/wintercoat/security/hos allowed_roles = list(JOB_TITLE_HOS) /datum/gear/suit/coat/job/captain - display_name = "winter coat, captain" + index_name = "winter coat, captain" path = /obj/item/clothing/suit/hooded/wintercoat/captain allowed_roles = list(JOB_TITLE_CAPTAIN) /datum/gear/suit/coat/job/med - display_name = "winter coat, medical" + index_name = "winter coat, medical" path = /obj/item/clothing/suit/hooded/wintercoat/medical allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_CHEMIST, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_VIROLOGIST, JOB_TITLE_BRIGDOC , JOB_TITLE_CORONER) /datum/gear/suit/coat/job/cmo - display_name = "winter coat, chief medical officer" + index_name = "winter coat, chief medical officer" path = /obj/item/clothing/suit/hooded/wintercoat/medical/cmo allowed_roles = list(JOB_TITLE_CMO) /datum/gear/suit/coat/job/sci - display_name = "winter coat, science" + index_name = "winter coat, science" path = /obj/item/clothing/suit/hooded/wintercoat/medical/science allowed_roles = list(JOB_TITLE_SCIENTIST, JOB_TITLE_RD, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/suit/coat/job/rd - display_name = "winter coat, research director" + index_name = "winter coat, research director" path = /obj/item/clothing/suit/hooded/wintercoat/medical/science/rd allowed_roles = list(JOB_TITLE_RD) /datum/gear/suit/coat/job/engi - display_name = "winter coat, engineering" + index_name = "winter coat, engineering" path = /obj/item/clothing/suit/hooded/wintercoat/engineering allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC) /datum/gear/suit/coat/job/atmos - display_name = "winter coat, atmospherics" + index_name = "winter coat, atmospherics" path = /obj/item/clothing/suit/hooded/wintercoat/engineering/atmos allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/suit/coat/job/ce - display_name = "winter coat, chief engineer" + index_name = "winter coat, chief engineer" path = /obj/item/clothing/suit/hooded/wintercoat/engineering/ce allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/suit/coat/job/hydro - display_name = "winter coat, hydroponics" + index_name = "winter coat, hydroponics" path = /obj/item/clothing/suit/hooded/wintercoat/hydro allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/suit/coat/job/cargo - display_name = "winter coat, cargo" + index_name = "winter coat, cargo" path = /obj/item/clothing/suit/hooded/wintercoat/cargo allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/suit/coat/job/qm - display_name = "winter coat, quartermaster" + index_name = "winter coat, quartermaster" path = /obj/item/clothing/suit/hooded/wintercoat/cargo/qm allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/suit/coat/job/miner - display_name = "winter coat, miner" + index_name = "winter coat, miner" path = /obj/item/clothing/suit/hooded/wintercoat/miner allowed_roles = list(JOB_TITLE_MINER) /datum/gear/suit/coat/job/hop - display_name = "winter coat, head of personnel" + index_name = "winter coat, head of personnel" path = /obj/item/clothing/suit/hooded/wintercoat/hop allowed_roles = list(JOB_TITLE_HOP) //LABCOATS /datum/gear/suit/labcoat_emt - display_name = "labcoat, paramedic" + index_name = "labcoat, paramedic" path = /obj/item/clothing/suit/storage/labcoat/emt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_PARAMEDIC) //JACKETS /datum/gear/suit/leather_jacket - display_name = "leather jacket" + index_name = "leather jacket" path = /obj/item/clothing/suit/jacket/leather /datum/gear/suit/motojacket - display_name = "leather motorcycle jacket" + index_name = "leather motorcycle jacket" path = /obj/item/clothing/suit/jacket/motojacket /datum/gear/suit/br_tcoat - display_name = "trenchcoat, brown" + index_name = "trenchcoat, brown" path = /obj/item/clothing/suit/storage/browntrenchcoat /datum/gear/suit/bl_tcoat - display_name = "trenchcoat, black" + index_name = "trenchcoat, black" path = /obj/item/clothing/suit/storage/blacktrenchcoat /datum/gear/suit/bomber_jacket - display_name = "bomber jacket" + index_name = "bomber jacket" path = /obj/item/clothing/suit/jacket /datum/gear/suit/miljacket - display_name = "military jacket, select" + index_name = "military jacket, select" + display_name = "military jacket" path = /obj/item/clothing/suit/jacket/miljacket /datum/gear/suit/miljacket/New() @@ -131,21 +132,21 @@ gear_tweaks += new /datum/gear_tweak/path(jackets, src) /datum/gear/suit/secjacket - display_name = "security jacket" + index_name = "security jacket" path = /obj/item/clothing/suit/armor/secjacket allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/coat/russian - display_name = "russian coat" + index_name = "russian coat" path = /obj/item/clothing/suit/russiancoat /datum/gear/suit/secbomber - display_name = "security bomber" + index_name = "security bomber" path = /obj/item/clothing/suit/jacket/pilot allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/sec_rps - display_name = "security belt-shoulder system" + index_name = "security belt-shoulder system" path = /obj/item/clothing/suit/armor/vest/sec_rps allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) @@ -154,94 +155,95 @@ subtype_path = /datum/gear/suit/suragi_jacket /datum/gear/suit/suragi_jacket/civ - display_name = "Suragi Jacket" + index_name = "Suragi Jacket" path = /obj/item/clothing/suit/storage/suragi_jacket/civ /datum/gear/suit/suragi_jacket/sec - display_name = "Suragi Jacket - Security" + index_name = "Suragi Jacket - Security" path = /obj/item/clothing/suit/storage/suragi_jacket/sec allowed_roles = list(JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/suit/suragi_jacket/cargo - display_name = "Suragi Jacket - Cargo" + index_name = "Suragi Jacket - Cargo" path = /obj/item/clothing/suit/storage/suragi_jacket/cargo allowed_roles = list(JOB_TITLE_CARGOTECH) /datum/gear/suit/suragi_jacket/atmos - display_name = "Suragi Jacket - Atmospherics" + index_name = "Suragi Jacket - Atmospherics" path = /obj/item/clothing/suit/storage/suragi_jacket/atmos allowed_roles = list(JOB_TITLE_ATMOSTECH) /datum/gear/suit/suragi_jacket/eng - display_name = "Suragi Jacket - Engineering" + index_name = "Suragi Jacket - Engineering" path = /obj/item/clothing/suit/storage/suragi_jacket/eng allowed_roles = list(JOB_TITLE_ENGINEER, JOB_TITLE_ENGINEER_TRAINEE, JOB_TITLE_MECHANIC) /datum/gear/suit/suragi_jacket/botany - display_name = "Suragi Jacket - Hydroponics" + index_name = "Suragi Jacket - Hydroponics" path = /obj/item/clothing/suit/storage/suragi_jacket/botany allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/suit/suragi_jacket/medic - display_name = "Suragi Jacket - Medical" + index_name = "Suragi Jacket - Medical" path = /obj/item/clothing/suit/storage/suragi_jacket/medic allowed_roles = list(JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_CORONER) /datum/gear/suit/suragi_jacket/medsec - display_name = "Suragi Jacket - Medical Security" + index_name = "Suragi Jacket - Medical Security" path = /obj/item/clothing/suit/storage/suragi_jacket/medsec allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/suit/suragi_jacket/virus - display_name = "Suragi Jacket - Virology" + index_name = "Suragi Jacket - Virology" path = /obj/item/clothing/suit/storage/suragi_jacket/virus allowed_roles = list(JOB_TITLE_VIROLOGIST) /datum/gear/suit/suragi_jacket/chem - display_name = "Suragi Jacket - Chemistry" + index_name = "Suragi Jacket - Chemistry" path = /obj/item/clothing/suit/storage/suragi_jacket/chem allowed_roles = list(JOB_TITLE_CHEMIST) /datum/gear/suit/suragi_jacket/genetics - display_name = "Suragi Jacket - Genetics" + index_name = "Suragi Jacket - Genetics" path = /obj/item/clothing/suit/storage/suragi_jacket/genetics allowed_roles = list(JOB_TITLE_GENETICIST) /datum/gear/suit/suragi_jacket/robot - display_name = "Suragi Jacket - Roboticist" + index_name = "Suragi Jacket - Roboticist" path = /obj/item/clothing/suit/storage/suragi_jacket/robot allowed_roles = list(JOB_TITLE_ROBOTICIST) /datum/gear/suit/suragi_jacket/sci - display_name = "Suragi Jacket - Science" + index_name = "Suragi Jacket - Science" path = /obj/item/clothing/suit/storage/suragi_jacket/sci allowed_roles = list(JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/suit/suragi_jacket/janitor - display_name = "Suragi Jacket - Janitor" + index_name = "Suragi Jacket - Janitor" path = /obj/item/clothing/suit/storage/suragi_jacket/janitor allowed_roles = list(JOB_TITLE_JANITOR) /datum/gear/suit/ianshirt - display_name = "Ian Shirt" + index_name = "Ian Shirt" path = /obj/item/clothing/suit/ianshirt /datum/gear/suit/hoodie - display_name = "hoodie, select" + index_name = "hoodie, select" + display_name = "hoodie" path = /obj/item/clothing/suit/hooded/hoodie /datum/gear/suit/hoodie/New() @@ -259,7 +261,8 @@ //SUITS! /datum/gear/suit/blacksuit - display_name = "suit jacket, select" + index_name = "suit jacket, select" + display_name = "suit jacket" path = /obj/item/clothing/suit/storage/lawyer/blackjacket /datum/gear/suit/blacksuit/New() @@ -272,14 +275,14 @@ //Robes! /datum/gear/suit/witch - display_name = "witch robes" + index_name = "witch robes" path = /obj/item/clothing/suit/wizrobe/marisa/fake //Suspenders /datum/gear/suit/suspenders - display_name = "suspenders, color" + index_name = "suspenders, color" path = /obj/item/clothing/suit/suspenders /datum/gear/suit/suspenders/New() diff --git a/code/modules/client/preference/loadout/loadout_tgui.dm b/code/modules/client/preference/loadout/loadout_tgui.dm new file mode 100644 index 00000000000..977ebac09a6 --- /dev/null +++ b/code/modules/client/preference/loadout/loadout_tgui.dm @@ -0,0 +1,61 @@ +GLOBAL_LIST_EMPTY(gear_tgui_info) + +/datum/ui_module/loadout + name = "Loadout" + +/datum/ui_module/loadout/ui_state(mob/user) + return GLOB.always_state + +/datum/ui_module/loadout/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Loadout", name) + ui.set_autoupdate(FALSE) + ui.open() + +/datum/ui_module/loadout/ui_data(mob/user) + var/list/data = list() + data["gear_slots"] = user?.client?.prefs.build_loadout() + data["selected_gears"] = user?.client?.prefs?.tgui_loadout_gear + return data + +/datum/ui_module/loadout/ui_static_data(mob/user) + var/list/data = list() + data["gears"] = GLOB.gear_tgui_info + data["max_gear_slots"] = user?.client?.prefs?.max_gear_slots + data["user_tier"] = user?.client?.donator_level + return data + +/datum/ui_module/loadout/ui_act(action, list/params) + if(..()) + return + . = TRUE + + var/mob/user = usr + var/datum/preferences/prefs = user.client.prefs + switch(action) + if("toggle_gear") + var/datum/gear/gear = GLOB.gear_datums[params["gear"]] + if(gear && ("[gear.index_name]" in prefs.loadout_gear)) + prefs.loadout_gear -= "[gear.index_name]" + return TRUE + + if(gear.donator_tier && user.client.donator_level < gear.donator_tier) + to_chat(user, "That gear is only available at a higher donation tier than you are on.") + return FALSE + + user.client.prefs.build_loadout(gear) + return TRUE + + if("set_tweak") + if(!(params["gear"] in prefs.loadout_gear)) + return FALSE + + var/datum/gear/gear = GLOB.gear_datums[params["gear"]] + var/datum/gear_tweak/tweak = locate(text2path(params["tweak"])) in gear.gear_tweaks + prefs.set_tweak_metadata(gear, tweak, tweak.get_metadata(user, prefs.get_tweak_metadata(gear, tweak))) + return TRUE + + if("clear_loadout") + prefs.loadout_gear.Cut() + return TRUE diff --git a/code/modules/client/preference/loadout/loadout_uniform.dm b/code/modules/client/preference/loadout/loadout_uniform.dm index 8e3120d644d..7458254ec43 100644 --- a/code/modules/client/preference/loadout/loadout_uniform.dm +++ b/code/modules/client/preference/loadout/loadout_uniform.dm @@ -10,7 +10,8 @@ //there's a lot more colors than I thought there were @_@ /datum/gear/uniform/suit/jumpsuit - display_name = "jumpsuit, select" + index_name = "jumpsuit, select" + display_name = "jumpsuit" path = /obj/item/clothing/under/color/grey /datum/gear/uniform/suit/jumpsuit/New() @@ -39,27 +40,28 @@ gear_tweaks += new /datum/gear_tweak/path(suits, src, TRUE) /datum/gear/uniform/suit/soviet - display_name = "USSP uniform" + index_name = "USSP uniform" path = /obj/item/clothing/under/soviet /datum/gear/uniform/suit/federal - display_name = "Solar Federation uniform" + index_name = "Solar Federation uniform" path = /obj/item/clothing/under/solgov/civ /datum/gear/uniform/suit/kilt - display_name = "a kilt" + index_name = "a kilt" path = /obj/item/clothing/under/kilt /datum/gear/uniform/suit/executive - display_name = "executive suit" + index_name = "executive suit" path = /obj/item/clothing/under/suit_jacket/really_black /datum/gear/uniform/suit/amish_suit - display_name = "amish suit" + index_name = "amish suit" path = /obj/item/clothing/under/sl_suit /datum/gear/uniform/chaps - display_name = "chaps, select" + index_name = "chaps, select" + display_name = "chaps" path = /obj/item/clothing/under/red_chaps /datum/gear/uniform/chaps/New() @@ -74,11 +76,11 @@ subtype_path = /datum/gear/uniform/skirt /datum/gear/uniform/skirt/syndi - display_name = "skirt, tactical" + index_name = "skirt, tactical" path = /obj/item/clothing/under/syndicate/tacticool/skirt /datum/gear/uniform/skirt/dyeable - display_name = "dyeable skirt, color" + index_name = "dyeable skirt, color" path = /obj/item/clothing/under/colour/skirt @@ -88,7 +90,8 @@ /datum/gear/uniform/skirt/plaid - display_name = "plaid skirt, select" + index_name = "plaid skirt, select" + display_name = "plaid skirt" path = /obj/item/clothing/under/dress/plaid_blue /datum/gear/uniform/skirt/plaid/New() @@ -99,11 +102,11 @@ gear_tweaks += new /datum/gear_tweak/path(skirts, src, TRUE) /datum/gear/uniform/skirt/redeveninggown - display_name = "red evening gown" + index_name = "red evening gown" path = /obj/item/clothing/under/redeveninggown /datum/gear/uniform/skirt/black - display_name = "skirt, black" + index_name = "skirt, black" path = /obj/item/clothing/under/blackskirt /datum/gear/uniform/skirt/job @@ -111,137 +114,132 @@ subtype_cost_overlap = FALSE /datum/gear/uniform/skirt/job/ce - display_name = "skirt, ce" + index_name = "skirt, ce" path = /obj/item/clothing/under/rank/chief_engineer/skirt allowed_roles = list(JOB_TITLE_CHIEF) /datum/gear/uniform/skirt/job/atmos - display_name = "skirt, atmos" + index_name = "skirt, atmos" path = /obj/item/clothing/under/rank/atmospheric_technician/skirt allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ATMOSTECH) /datum/gear/uniform/skirt/job/eng - display_name = "skirt, engineer" + index_name = "skirt, engineer" path = /obj/item/clothing/under/rank/engineer/skirt allowed_roles = list(JOB_TITLE_CHIEF, JOB_TITLE_ENGINEER) /datum/gear/uniform/skirt/job/roboticist - display_name = "skirt, roboticist" + index_name = "skirt, roboticist" path = /obj/item/clothing/under/rank/roboticist/skirt allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_ROBOTICIST) /datum/gear/uniform/skirt/job/cmo - display_name = "skirt, cmo" + index_name = "skirt, cmo" path = /obj/item/clothing/under/rank/chief_medical_officer/skirt allowed_roles = list(JOB_TITLE_CMO) /datum/gear/uniform/skirt/job/paramedic - display_name = "skirt, paramedic" + index_name = "skirt, paramedic" path = /obj/item/clothing/under/rank/medical/paramedic/skirt allowed_roles = list(JOB_TITLE_PARAMEDIC) /datum/gear/uniform/skirt/job/chem - display_name = "skirt, chemist" + index_name = "skirt, chemist" path = /obj/item/clothing/under/rank/chemist/skirt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_CHEMIST) /datum/gear/uniform/skirt/job/viro - display_name = "skirt, virologist" + index_name = "skirt, virologist" path = /obj/item/clothing/under/rank/virologist/skirt allowed_roles = list(JOB_TITLE_VIROLOGIST) /datum/gear/uniform/skirt/job/med - display_name = "skirt, medical" + index_name = "skirt, medical" path = /obj/item/clothing/under/rank/medical/skirt allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN, JOB_TITLE_PSYCHIATRIST, JOB_TITLE_PARAMEDIC, JOB_TITLE_CORONER) /datum/gear/uniform/skirt/job/phys - display_name = "skirt, physician" + index_name = "skirt, physician" path = /obj/item/clothing/under/rank/security/brigphys/skirt allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/uniform/skirt/job/physalt - display_name = "skirt, physician alt" + index_name = "skirt, physician alt" path = /obj/item/clothing/under/rank/security/brigmedical/skirt allowed_roles = list(JOB_TITLE_BRIGDOC) /datum/gear/uniform/skirt/job/hydro - display_name = "skirt, botanist" + index_name = "skirt, botanist" path = /obj/item/clothing/under/rank/hydroponics/skirt allowed_roles = list(JOB_TITLE_BOTANIST) /datum/gear/uniform/skirt/job/sci - display_name = "skirt, scientist" + index_name = "skirt, scientist" path = /obj/item/clothing/under/rank/scientist/skirt allowed_roles = list(JOB_TITLE_RD, JOB_TITLE_SCIENTIST, JOB_TITLE_SCIENTIST_STUDENT) /datum/gear/uniform/skirt/job/cargo - display_name = "skirt, cargo" + index_name = "skirt, cargo" path = /obj/item/clothing/under/rank/cargotech/skirt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/uniform/skirt/job/qm - display_name = "skirt, QM" + index_name = "skirt, QM" path = /obj/item/clothing/under/rank/cargo/skirt allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/uniform/skirt/job/warden - display_name = "skirt, warden" + index_name = "skirt, warden" path = /obj/item/clothing/under/rank/warden/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN) /datum/gear/uniform/skirt/job/security - display_name = "skirt, security" + index_name = "skirt, security" path = /obj/item/clothing/under/rank/security/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/skirt/job/podpilot - display_name = "skirt, podpilot" + index_name = "skirt, podpilot" path = /obj/item/clothing/under/rank/security/pod_pilot/skirt allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_PILOT) /datum/gear/uniform/skirt/job/head_of_security - display_name = "skirt, hos" + index_name = "skirt, hos" path = /obj/item/clothing/under/rank/head_of_security/skirt allowed_roles = list(JOB_TITLE_HOS) /datum/gear/uniform/skirt/job/ntrep - display_name = "skirt, nt rep" + index_name = "skirt, nt rep" path = /obj/item/clothing/under/rank/ntrep/skirt allowed_roles = list(JOB_TITLE_REPRESENTATIVE) /datum/gear/uniform/skirt/job/blueshield - display_name = "skirt, blueshield" + index_name = "skirt, blueshield" path = /obj/item/clothing/under/rank/blueshield/skirt allowed_roles = list(JOB_TITLE_BLUESHIELD) /datum/gear/uniform/skirt/job/librarian - display_name = "skirt, librarian" + index_name = "skirt, librarian" path = /obj/item/clothing/under/suit_jacket/red/skirt allowed_roles = list(JOB_TITLE_LIBRARIAN) /datum/gear/uniform/skirt/job/bartender - display_name = "skirt, bartender" + index_name = "skirt, bartender" path = /obj/item/clothing/under/rank/bartender/skirt allowed_roles = list(JOB_TITLE_BARTENDER) /datum/gear/uniform/skirt/job/chaplain - display_name = "skirt, chaplain" + index_name = "skirt, chaplain" path = /obj/item/clothing/under/rank/chaplain/skirt allowed_roles = list(JOB_TITLE_CHAPLAIN) -/datum/gear/uniform/skirt/job/barber - display_name = "skirt, barber" - path = /obj/item/clothing/under/barber/skirt - allowed_roles = list(JOB_TITLE_BARBER) - /datum/gear/uniform/skirt/job/nanotrasenofficer - display_name = "skirt, NNO" + index_name = "skirt, NNO" path = /obj/item/clothing/under/rank/centcom/officer/skirt allowed_roles = list(JOB_TITLE_CCOFFICER) /datum/gear/uniform/skirt/job/internalaffairs - display_name = "skirt, internalaffairs" + index_name = "skirt, internalaffairs" path = /obj/item/clothing/under/rank/internalaffairs/skirt allowed_roles = list(JOB_TITLE_LAWYER) @@ -249,7 +247,8 @@ subtype_path = /datum/gear/uniform/medical /datum/gear/uniform/medical/scrubs - display_name = "medical scrubs, select" + index_name = "medical scrubs, select" + display_name = "medical scrubs" path = /obj/item/clothing/under/rank/medical/purple allowed_roles = list(JOB_TITLE_CMO, JOB_TITLE_DOCTOR, JOB_TITLE_INTERN) @@ -264,22 +263,22 @@ subtype_path = /datum/gear/uniform/sec /datum/gear/uniform/sec/formal - display_name = "security uniform, formal" + index_name = "security uniform, formal" path = /obj/item/clothing/under/rank/security/formal allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/secorporate - display_name = "security uniform, corporate" + index_name = "security uniform, corporate" path = /obj/item/clothing/under/rank/security/corp allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/dispatch - display_name = "security uniform, dispatch" + index_name = "security uniform, dispatch" path = /obj/item/clothing/under/rank/dispatch allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) /datum/gear/uniform/sec/casual - display_name = "security uniform, casual" + index_name = "security uniform, casual" path = /obj/item/clothing/under/rank/security2 allowed_roles = list(JOB_TITLE_HOS, JOB_TITLE_WARDEN, JOB_TITLE_DETECTIVE, JOB_TITLE_OFFICER, JOB_TITLE_PILOT) @@ -287,22 +286,23 @@ subtype_path = /datum/gear/uniform/cargo /datum/gear/uniform/cargo/qm - display_name = "quartermaster dress" + index_name = "quartermaster dress" path = /obj/item/clothing/under/rank/cargo/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER) /datum/gear/uniform/cargo/tech - display_name = "cargo technician dress" + index_name = "cargo technician dress" path = /obj/item/clothing/under/rank/cargotech/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_CARGOTECH) /datum/gear/uniform/cargo/miner - display_name = "shaft miner sweater" + index_name = "shaft miner sweater" path = /obj/item/clothing/under/rank/miner/alt allowed_roles = list(JOB_TITLE_QUARTERMASTER, JOB_TITLE_MINER) /datum/gear/uniform/shorts - display_name = "shorts, select" + index_name = "shorts, select" + display_name = "shorts" path = /obj/item/clothing/under/shorts/red /datum/gear/uniform/shorts/New() @@ -318,7 +318,8 @@ subtype_path = /datum/gear/uniform/pants /datum/gear/uniform/pants/jeans - display_name = "jeans, select" + index_name = "jeans, select" + display_name = "jeans" path = /obj/item/clothing/under/pants/classicjeans /datum/gear/uniform/pants/jeans/New() @@ -331,7 +332,8 @@ gear_tweaks += new /datum/gear_tweak/path(jeans, src, TRUE) /datum/gear/uniform/pants/pants - display_name = "pants, select" + index_name = "pants, select" + display_name = "pants" path = /obj/item/clothing/under/pants/white /datum/gear/uniform/pants/pants/New() @@ -348,48 +350,48 @@ gear_tweaks += new /datum/gear_tweak/path(pants, src, TRUE) /datum/gear/uniform/suit/tacticool - display_name = "tacticool turtleneck" + index_name = "tacticool turtleneck" description = "A sleek black turtleneck paired with some khakis (WARNING DOES NOT HAVE SUIT SENSORS)" path = /obj/item/clothing/under/syndicate/tacticool /datum/gear/uniform/hawaii - display_name = "hawaiian shirt (red)" + index_name = "hawaiian shirt (red)" description = "Sometimes you just want to shoot the guy who brought the chainsaw to the drug deal" path = /obj/item/clothing/under/redhawaiianshirt /datum/gear/uniform/hawaii/pink - display_name = "hawaiian shirt (pink)" + index_name = "hawaiian shirt (pink)" description = "Sometimes you just want some pink in your life. For what? Who knows" path = /obj/item/clothing/under/pinkhawaiianshirt /datum/gear/uniform/hawaii/blue - display_name = "hawaiian shirt (blue)" + index_name = "hawaiian shirt (blue)" description = "Be careful around water! Some guys in blue shirt like you can't swim" path = /obj/item/clothing/under/bluehawaiianshirt /datum/gear/uniform/hawaii/orange - display_name = "hawaiian shirt (orange)" + index_name = "hawaiian shirt (orange)" description = "Come one step closer and I will knock his teeth out!" path = /obj/item/clothing/under/orangehawaiianshirt /datum/gear/uniform/ussptracksuit_red - display_name = "track suit (red)" + index_name = "track suit (red)" description = "A classic track suit. There is a small tag on the clothes that says \"Made in the USSP\"." path = /obj/item/clothing/under/ussptracksuit_red /datum/gear/uniform/ussptracksuit_blue - display_name = "track suit (blue)" + index_name = "track suit (blue)" description = "A classic track suit. There is a small tag on the clothes that says \"Made in the USSP\"." path = /obj/item/clothing/under/ussptracksuit_blue /datum/gear/uniform/dress50s - display_name = "old Soviet dress" + index_name = "old Soviet dress" path = /obj/item/clothing/under/dress50s /datum/gear/uniform/galifepants - display_name = "check breeches" + index_name = "check breeches" path = /obj/item/clothing/under/pants/galifepants /datum/gear/uniform/sandpants - display_name = "long sand pants" + index_name = "long sand pants" path = /obj/item/clothing/under/pants/sandpants diff --git a/code/modules/client/preference/preference_info.dm b/code/modules/client/preference/preference_info.dm new file mode 100644 index 00000000000..2da8f02b214 --- /dev/null +++ b/code/modules/client/preference/preference_info.dm @@ -0,0 +1,251 @@ +/datum/preference_info + var/name + +/datum/preference_info/proc/get_preference_toggle() + return + +/datum/preference_info/proc/get_examine_text() + return + +/datum/preference_info/ghost_ears + name = "Hearing All Speech as a Ghost" + +/datum/preference_info/ghost_sight + name = "Ghost Emote Viewing" + +/datum/preference_info/ghost_radio + name = "Ghost Radio" + +/datum/preference_info/admin_radio + name = "Admin Radio" + +/datum/preference_info/ai_voice_announcements + name = "AI Voice Announcements" + +/datum/preference_info/admin_pm_sound + name = "Admin PM Sound" + +/datum/preference_info/mentor_pm_sound + name = "Mentor PM Sound" + +/datum/preference_info/deadchat_visibility + name = "Deadchat Visibility" + +/datum/preference_info/end_of_round_scoreboard + name = "End of Round Scoreboard" + +/datum/preference_info/title_music + name = "Lobby Music" + +/datum/preference_info/admin_midis + name = "Admin Midis" + +/datum/preference_info/ooc + name = "OOC Chat" + +/datum/preference_info/looc + name = "LOOC Chat" + +/datum/preference_info/ambience + name = "Ambient Sounds" + +/datum/preference_info/white_noise + name = "White Noise" + +/datum/preference_info/heartbeat_noise + name = "Heartbeat Noise" + +/datum/preference_info/instruments + name = "Instruments" + +/datum/preference_info/disco + name = "Disco Machine Music" + +/datum/preference_info/ghost_pda + name = "Ghost PDA Messages" + +/datum/preference_info/runechat + name = "Runechat" + +/datum/preference_info/ghost_death_notifs + name = "Ghost Death Notifications" + +/datum/preference_info/reverb + name = "Reverb" + +/datum/preference_info/simple_stat_panel + name = "Item Outlines" + +/datum/preference_info/anonmode + name = "Anonymous Mode" + +/datum/preference_info/typing_indicator + name = "Typing Indicator" + +/datum/preference_info/admin_logs + name = "Admin Log Messages" + +/datum/preference_info/mhelp_notification + name = "Mentor Ticket Messages" + +/datum/preference_info/ahelp_notification + name = "Admin Ticket Messages" + +/datum/preference_info/debug_logs + name = "Debug Log Messages" + +/datum/preference_info/mctabs + name = "MC Tab" + +/datum/preference_info/attack_animations + name = "Attack Animations" + +/datum/preference_info/prayers + name = "Prayers" + +/datum/preference_info/prayers_notify + name = "Prayers Notify" + +/datum/preference_info/karma_reminder + name = "End Round Karma Reminder" + +/datum/preference_info/parallax_multiz + name = "Parallax Multi-Z" + +/datum/preference_info/vote_popup + name = "Vote Popup" + +/datum/preference_info/tgui_input + name = "TGUI Input" + +/datum/preference_info/strip_tgui_size + name = "TGUI Strip Menu Size" + +/datum/preference_info/item_description_tips + name = "Item Description Tips" + +/datum/preference_info/take_out_of_the_round_without_obj + name = "Take out from round without objective" + +/datum/preference_info/deadchat_visibility/get_preference_toggle() + return /datum/preference_toggle/toggle_deadchat_visibility + +/datum/preference_info/ghost_ears/get_preference_toggle() + return /datum/preference_toggle/toggle_ghost_ears + +/datum/preference_info/ghost_sight/get_preference_toggle() + return /datum/preference_toggle/toggle_ghost_sight + +/datum/preference_info/ghost_radio/get_preference_toggle() + return /datum/preference_toggle/toggle_ghost_radio + +/datum/preference_info/admin_radio/get_preference_toggle() + return /datum/preference_toggle/toggle_admin_radio + +/datum/preference_info/ai_voice_announcements/get_preference_toggle() + return /datum/preference_toggle/toggle_ai_voice_annoucements + +/datum/preference_info/admin_pm_sound/get_preference_toggle() + return /datum/preference_toggle/toggle_admin_pm_sound + +/datum/preference_info/mentor_pm_sound/get_preference_toggle() + return /datum/preference_toggle/toggle_mentor_pm_sound + +/datum/preference_info/end_of_round_scoreboard/get_preference_toggle() + return /datum/preference_toggle/end_of_round_scoreboard + +/datum/preference_info/title_music/get_preference_toggle() + return /datum/preference_toggle/title_music + +/datum/preference_info/admin_midis/get_preference_toggle() + return /datum/preference_toggle/toggle_admin_midis + +/datum/preference_info/ooc/get_preference_toggle() + return /datum/preference_toggle/toggle_ooc + +/datum/preference_info/looc/get_preference_toggle() + return /datum/preference_toggle/toggle_looc + +/datum/preference_info/ambience/get_preference_toggle() + return /datum/preference_toggle/toggle_ambience + +/datum/preference_info/white_noise/get_preference_toggle() + return /datum/preference_toggle/toggle_white_noise + +/datum/preference_info/heartbeat_noise/get_preference_toggle() + return /datum/preference_toggle/toggle_heartbeat_noise + +/datum/preference_info/instruments/get_preference_toggle() + return /datum/preference_toggle/toggle_instruments + +/datum/preference_info/disco/get_preference_toggle() + return /datum/preference_toggle/toggle_disco + +/datum/preference_info/ghost_pda/get_preference_toggle() + return /datum/preference_toggle/toggle_ghost_pda + +/datum/preference_info/runechat/get_preference_toggle() + return /datum/preference_toggle/toggle_runechat + +/datum/preference_info/ghost_death_notifs/get_preference_toggle() + return /datum/preference_toggle/toggle_ghost_death_notifs + +/datum/preference_info/reverb/get_preference_toggle() + return /datum/preference_toggle/toggle_reverb + +/datum/preference_info/simple_stat_panel/get_preference_toggle() + return /datum/preference_toggle/toggle_simple_stat_panel + +/datum/preference_info/anonmode/get_preference_toggle() + return /datum/preference_toggle/toggle_anonmode + +/datum/preference_info/typing_indicator/get_preference_toggle() + return /datum/preference_toggle/toggle_typing_indicator + +/datum/preference_info/admin_logs/get_preference_toggle() + return /datum/preference_toggle/toggle_admin_logs + +/datum/preference_info/mhelp_notification/get_preference_toggle() + return /datum/preference_toggle/toggle_mhelp_notification + +/datum/preference_info/ahelp_notification/get_preference_toggle() + return /datum/preference_toggle/toggle_ahelp_notification + +/datum/preference_info/debug_logs/get_preference_toggle() + return /datum/preference_toggle/toggle_debug_logs + +/datum/preference_info/mctabs/get_preference_toggle() + return /datum/preference_toggle/toggle_mctabs + +/datum/preference_info/attack_animations/get_preference_toggle() + return /datum/preference_toggle/toggle_attack_animations + +/datum/preference_info/prayers/get_preference_toggle() + return /datum/preference_toggle/toggleprayers + +/datum/preference_info/prayers_notify/get_preference_toggle() + return /datum/preference_toggle/toggle_prayers_notify + +/datum/preference_info/karma_reminder/get_preference_toggle() + return /datum/preference_toggle/toggle_karma_reminder + +/datum/preference_info/parallax_multiz/get_preference_toggle() + return /datum/preference_toggle/toggle_parallax_multiz + +/datum/preference_info/vote_popup/get_preference_toggle() + return /datum/preference_toggle/toggle_vote_popup + +/datum/preference_info/tgui_input/get_preference_toggle() + return /datum/preference_toggle/toggle_tgui_input + +/datum/preference_info/strip_tgui_size/get_preference_toggle() + return /datum/preference_toggle/toggle_strip_tgui_size + +/datum/preference_info/item_descritpion_tips/get_preference_toggle() + return /datum/preference_toggle/toggle_item_descritpion_tips + +/datum/preference_info/take_out_of_the_round_without_obj/get_preference_toggle() + return /datum/preference_toggle/toggle_take_out_of_the_round_without_obj + +/datum/preference_info/take_out_of_the_round_without_obj/get_examine_text() + return "\n
[span_info("Вы можете вывести этого игрока из игры не имея соответствующей цели.")]
" diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 6e9a574847c..327d18661fa 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -74,9 +74,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts #define TAB_CHAR 0 #define TAB_GAME 1 #define TAB_SPEC 2 -#define TAB_GEAR 3 -#define TAB_KEYS 4 -#define TAB_TOGGLES 5 +#define TAB_KEYS 3 +#define TAB_TOGGLES 4 /datum/preferences var/client/parent @@ -242,6 +241,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts //Gear stuff var/list/loadout_gear = list() + var/list/tgui_loadout_gear = list() var/list/choosen_gears = list() var/gear_tab = "General" // Parallax @@ -313,7 +313,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "Character Settings" dat += "Game Preferences" dat += "Special Roles" - dat += "Loadout" dat += "Key Bindings" dat += "General Preferences" dat += "" @@ -357,13 +356,14 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if(SPECIES_VOX) dat += "N2 Tank: [speciesprefs ? "Large N2 Tank" : "Specialized N2 Tank"]
" if(SPECIES_GREY) - dat += "Wingdings: Set in disabilities
" - dat += "Voice Translator: [speciesprefs ? "Yes" : "No"]
" + dat += "Wingdings: [disabilities & DISABILITY_FLAG_WINGDINGS ? "Yes" : "No"]
" + dat += "Install Wingdings Decoder: [speciesprefs ? "Yes" : "No"]
" if(SPECIES_MACNINEPERSON) dat += "Synthetic Shell: Selections
" if(SPECIES_WRYN) dat += "Comb Deafness: [speciesprefs ? "Yes" : "No"]
" - dat += "Secondary Language: [language]
" + if(species != SPECIES_GREY) + dat += "Secondary Language: [language]
" if(S.autohiss_basic_map) dat += "Auto-accent: [autohiss_mode == AUTOHISS_FULL ? "Full" : (autohiss_mode == AUTOHISS_BASIC ? "Basic" : "Off")]
" dat += "Blood Type: [b_type]
" @@ -532,7 +532,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "Undershirt Color: Color [color_square(undershirt_color)]
" if(S.clothing_flags & HAS_SOCKS) dat += "Socks: [socks]
" - dat += "Backpack Type: [backbag]
" + dat += "Backpack Type: [backbag]

" + dat += "Open Loadout
" dat += "" @@ -642,59 +643,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts dat += "

" dat += "" - if(TAB_GEAR) - var/total_cost = 0 - var/list/type_blacklist = list() - if(loadout_gear && loadout_gear.len) - for(var/i = 1, i <= loadout_gear.len, i++) - var/datum/gear/G = GLOB.gear_datums[loadout_gear[i]] - if(G) - if(!G.subtype_cost_overlap) - if(G.subtype_path in type_blacklist) - continue - type_blacklist += G.subtype_path - total_cost += G.cost - - var/fcolor = "#3366CC" - if(total_cost < max_gear_slots) - fcolor = "#E67300" - dat += "" - dat += "" - dat += "" - - var/datum/loadout_category/LC = own_categories[gear_tab] - dat += "" - for(var/gear_name in LC.gear) - var/datum/gear/G = LC.gear[gear_name] - var/datum/gear/ticked = choosen_gears[G.display_name] - dat += "" - dat += "
[total_cost]/[max_gear_slots] loadout points spent. \[Clear Loadout\]
" - - var/firstcat = 1 - var/list/own_categories = GLOB.loadout_categories.Copy() - var/datum/loadout_category/choosen = new("Selected") - choosen.gear = choosen_gears - own_categories[choosen.category] = choosen - for(var/category in own_categories) - if(firstcat) - firstcat = 0 - else - dat += " |" - if(category == gear_tab) - dat += " [category] " - else - dat += " [category] " - dat += "
[LC.category]
[G.display_name]
" - if(ticked) - for(var/datum/gear_tweak/tweak in ticked.gear_tweaks) - dat += "
[tweak.get_contents(get_tweak_metadata(ticked, tweak))]" - dat += "
[G.cost]" - if(G.allowed_roles) - dat += "Restrictions: " - for(var/role in G.allowed_roles) - dat += role + " " - dat += "" - dat += "[G.get_header_tips()][ticked ? ticked.description : G.description]
" if(TAB_KEYS) dat += "
All Key Bindings: " dat += "Reset to Default " @@ -806,10 +754,10 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts /datum/preferences/proc/get_gear_metadata(var/datum/gear/G) - . = loadout_gear[G.display_name] + . = loadout_gear[G.index_name] if(!.) . = list() - loadout_gear[G.display_name] = . + loadout_gear[G.index_name] = . /datum/preferences/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak) var/list/metadata = get_gear_metadata(G) @@ -1191,6 +1139,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts HTML += ShowDisabilityState(user, DISABILITY_FLAG_ALCOHOLE_ADDICT, "Alcohole addict") if(!(S.blacklisted_disabilities & DISABILITY_FLAG_PARAPLEGIA)) HTML += ShowDisabilityState(user, DISABILITY_FLAG_PARAPLEGIA, "Paraplegia") + if(!(S.blacklisted_disabilities & DISABILITY_FLAG_APHASIA)) + HTML += ShowDisabilityState(user, DISABILITY_FLAG_APHASIA, "Aphasia") HTML += {" \[Done\] @@ -1280,6 +1230,60 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts SetChoices(user) return 1 +/** + * Rebuilds the `loadout_gear` list of the [active_character], and returns the total end cost. + * + * Caches and cuts the existing [/datum/character_save/var/loadout_gear] list and remakes it, checking the `subtype_selection_cost` and overall cost validity of each item. + * + * If the item's [/datum/gear/var/subtype_selection_cost] is `FALSE`, any future items with the same [/datum/gear/var/main_typepath] will have their cost skipped. + * If adding the item will take the total cost over the maximum, it won't be added to the list. + * + * Arguments: + * * new_item - A new [/datum/gear] item to be added to the `loadout_gear` list. + */ +/datum/preferences/proc/build_loadout(datum/gear/new_item) + var/total_cost = 0 + var/list/type_blacklist = list() + var/list/loadout_cache = loadout_gear.Copy() + loadout_gear.Cut() + tgui_loadout_gear.Cut() + choosen_gears.Cut() + if(new_item) + loadout_cache += "[new_item.index_name]" + + for(var/item in loadout_cache) + var/datum/gear/gear = GLOB.gear_datums[item] + if(!gear) + continue + var/added_cost = gear.cost + if(!gear.subtype_cost_overlap) // If listings of the same subtype shouldn't have their cost added. + if(gear.path in type_blacklist) + added_cost = 0 + else + type_blacklist += gear.path + if((total_cost + added_cost) > max_gear_slots) + continue // If the final cost is too high, don't add the item. + var/item_cache = loadout_cache[item] + loadout_gear[item] = item_cache ? item_cache : list() + var/tgui_data = list() + for(var/datum/gear_tweak/tweak in gear.gear_tweaks) + var/text_path = "[tweak.type]" + if(!(text_path in item_cache)) + continue + var/params = item_cache[text_path] + var/list/data =tweak?.get_tgui_data(params) + if (!data) + continue + tgui_data[text_path] = data["display_param"] + tgui_data["name"] = data["name"] + tgui_data["icon"] = data["icon"] + tgui_data["icon_file"] = data["icon_file"] + tgui_data["icon_state"] = data["icon_state"] + tgui_loadout_gear[gear] = tgui_data + choosen_gears[item] = gear + total_cost += added_cost + return total_cost + /datum/preferences/proc/ResetJobs() job_support_high = 0 job_support_med = 0 @@ -1491,47 +1495,6 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts SetRecords(user) - if(href_list["preference"] == "gear") - if(href_list["toggle_gear"]) - var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]] - if(TG.display_name in loadout_gear) - loadout_gear -= TG.display_name - choosen_gears -= TG.display_name - else - if(!TG.can_select(cl = user.client, species_name = S.name)) // all gear checks there, no jobs while prefs - return - var/total_cost = 0 - var/list/type_blacklist = list() - for(var/gear_name in loadout_gear) - var/datum/gear/G = GLOB.gear_datums[gear_name] - if(istype(G)) - if(!G.subtype_cost_overlap) - if(G.subtype_path in type_blacklist) - continue - type_blacklist += G.subtype_path - total_cost += G.cost - - if((total_cost + TG.cost) <= max_gear_slots) - loadout_gear += TG.display_name - choosen_gears[TG.display_name] += new TG.type - else if(href_list["gear"] && href_list["tweak"]) - var/datum/gear/gear = choosen_gears[href_list["gear"]] - var/datum/gear_tweak/tweak = locate(href_list["tweak"]) - if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) - return - var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak)) - if(!metadata) - return - set_tweak_metadata(gear, tweak, metadata) - else if(href_list["select_category"]) - gear_tab = href_list["select_category"] - else if(href_list["clear_loadout"]) - loadout_gear.Cut() - choosen_gears.Cut() - - ShowChoices(user) - return - switch(href_list["task"]) if("random") var/datum/robolimb/robohead @@ -1551,7 +1514,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_WRYN, SPECIES_VULPKANIN, SPECIES_VOX)) h_sec_colour = rand_hex_color() if("h_style") - h_style = random_hair_style(gender, species, robohead) + h_style = random_hair_style(gender, S, robohead) if("facial") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_WRYN, SPECIES_VULPKANIN, SPECIES_VOX)) f_colour = rand_hex_color() @@ -1647,7 +1610,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" robohead = GLOB.all_robolimbs[head_model] //grab one of the valid hair styles for the newly chosen species - h_style = random_hair_style(gender, species, robohead) + h_style = random_hair_style(gender, S, robohead) //grab one of the valid facial hair styles for the newly chosen species f_style = random_facial_hair_style(gender, species, robohead) @@ -1713,6 +1676,10 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts autohiss_mode = AUTOHISS_OFF if("speciesprefs") speciesprefs = !speciesprefs //Starts 0, so if someone clicks the button up top there, this won't be 0 anymore. If they click it again, it'll go back to 0. + if("toggle_wingdings") + var/dflag = text2num(DISABILITY_FLAG_WINGDINGS) + if(dflag >= 0) + disabilities ^= text2num(DISABILITY_FLAG_WINGDINGS) if("language") // var/languages_available var/list/new_languages = list("None") @@ -1763,16 +1730,16 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("hair") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX, SPECIES_WRYN)) //Species that have hair. (No HAS_HAIR flag) var/input = "Choose your character's hair colour:" - var/new_hair = input(user, input, "Character Preference", h_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, input, "Character Preference", h_colour) + if(!isnull(new_hair)) h_colour = new_hair if("secondary_hair") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) var/datum/sprite_accessory/hair_style = GLOB.hair_styles_public_list[h_style] if(hair_style.secondary_theme && !hair_style.no_sec_colour) - var/new_hair = input(user, "Choose your character's secondary hair colour:", "Character Preference", h_sec_colour) as color|null - if(new_hair) + var/new_hair = tgui_input_color(user, "Choose your character's secondary hair colour:", "Character Preference", h_sec_colour) + if(!isnull(new_hair)) h_sec_colour = new_hair if("h_style") @@ -1820,8 +1787,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts h_grad_offset_y = clamp(text2num(expl[2]) || 0, -16, 16) if("h_grad_colour") - var/result = input(user, "Choose your character's hair gradient colour:", "Character Preference", h_grad_colour) as color|null - if(result) + var/result = tgui_input_color(user, "Choose your character's hair gradient colour:", "Character Preference", h_grad_colour) + if(!isnull(result)) h_grad_colour = result if("h_grad_alpha") @@ -1833,8 +1800,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("headaccessory") if(S.bodyflags & HAS_HEAD_ACCESSORY) //Species with head accessories. var/input = "Choose the colour of your your character's head accessory:" - var/new_head_accessory = input(user, input, "Character Preference", hacc_colour) as color|null - if(new_head_accessory) + var/new_head_accessory = tgui_input_color(user, input, "Character Preference", hacc_colour) + if(!isnull(new_head_accessory)) hacc_colour = new_head_accessory if("ha_style") @@ -1913,8 +1880,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_head_colour") if(S.bodyflags & HAS_HEAD_MARKINGS) //Species with head markings. var/input = "Choose the colour of your your character's head markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["head"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["head"]) + if(!isnull(new_markings)) m_colours["head"] = new_markings if("m_style_body") @@ -1939,8 +1906,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_body_colour") if(S.bodyflags & HAS_BODY_MARKINGS) //Species with body markings/tattoos. var/input = "Choose the colour of your your character's body markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["body"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["body"]) + if(!isnull(new_markings)) m_colours["body"] = new_markings if("m_style_tail") @@ -1969,8 +1936,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("m_tail_colour") if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. var/input = "Choose the colour of your your character's tail markings:" - var/new_markings = input(user, input, "Character Preference", m_colours["tail"]) as color|null - if(new_markings) + var/new_markings = tgui_input_color(user, input, "Character Preference", m_colours["tail"]) + if(!isnull(new_markings)) m_colours["tail"] = new_markings if("body_accessory") @@ -1995,16 +1962,16 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("facial") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) //Species that have facial hair. (No HAS_HAIR_FACIAL flag) - var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference", f_colour) as color|null - if(new_facial) + var/new_facial = tgui_input_color(user, "Choose your character's facial-hair colour:", "Character Preference", f_colour) + if(!isnull(new_facial)) f_colour = new_facial if("secondary_facial") if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_VULPKANIN, SPECIES_VOX)) var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] if(facial_hair_style.secondary_theme && !facial_hair_style.no_sec_colour) - var/new_facial = input(user, "Choose your character's secondary facial-hair colour:", "Character Preference", f_sec_colour) as color|null - if(new_facial) + var/new_facial = tgui_input_color(user, "Choose your character's secondary facial-hair colour:", "Character Preference", f_sec_colour) + if(!isnull(new_facial)) f_sec_colour = new_facial if("f_style") @@ -2054,8 +2021,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts underwear = new_underwear if("underwear_color") - var/new_uwear_color = input(user, "Choose your character's underwear colour:", "Character Preference", underwear_color) as color|null - if(new_uwear_color) + var/new_uwear_color = tgui_input_color(user, "Choose your character's underwear colour:", "Character Preference", underwear_color) + if(!isnull(new_uwear_color)) underwear_color = new_uwear_color if("undershirt") @@ -2074,8 +2041,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts undershirt = new_undershirt if("undershirt_color") - var/new_ushirt_color = input(user, "Choose your character's undershirt colour:", "Character Preference", undershirt_color) as color|null - if(new_ushirt_color) + var/new_ushirt_color = tgui_input_color(user, "Choose your character's undershirt colour:", "Character Preference", undershirt_color) + if(!isnull(new_ushirt_color)) undershirt_color = new_ushirt_color if("socks") @@ -2094,8 +2061,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts socks = new_socks if("eyes") - var/new_eyes = input(user, "Choose your character's eye colour:", "Character Preference", e_colour) as color|null - if(new_eyes) + var/new_eyes = tgui_input_color(user, "Choose your character's eye colour:", "Character Preference", e_colour) + if(!isnull(new_eyes)) e_colour = new_eyes if("s_tone") @@ -2121,13 +2088,13 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if("skin") if((S.bodyflags & HAS_SKIN_COLOR) || ((S.bodyflags & HAS_BODYACC_COLOR) && GLOB.body_accessory_by_species[species]) || check_rights(R_ADMIN, 0, user)) - var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", s_colour) as color|null - if(new_skin) + var/new_skin = tgui_input_color(user, "Choose your character's skin colour: ", "Character Preference", s_colour) + if(!isnull(new_skin)) s_colour = new_skin if("ooccolor") - var/new_ooccolor = input(user, "Choose your OOC colour:", "Game Preference", ooccolor) as color|null - if(new_ooccolor) + var/new_ooccolor = tgui_input_color(user, "Choose your OOC colour:", "Game Preference", ooccolor) + if(!isnull(new_ooccolor)) ooccolor = new_ooccolor if("bag") @@ -2135,6 +2102,11 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if(new_backbag) backbag = new_backbag + if("loadout") + var/datum/ui_module/loadout/loadout = new() + loadout.ui_interact(user) + return FALSE + if("nt_relation") var/new_relation = tgui_input_list(user, "Choose your relation to NT. Note that this represents what others can find out about your character by researching your background, not what your character actually thinks.", "Character Preference", list("Loyal", "Supportive", "Neutral", "Skeptical", "Opposed")) if(new_relation) @@ -2397,7 +2369,7 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" robohead = GLOB.all_robolimbs[head_model] - h_style = random_hair_style(gender, species, robohead) + h_style = random_hair_style(gender, S, robohead) f_style = random_facial_hair_style(gender, species, robohead) m_styles["body"] = random_marking_style("body", species, gender = src.gender) @@ -2489,8 +2461,8 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts toggles2 ^= PREFTOGGLE_2_AFKWATCH if("UIcolor") - var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null - if(!UI_style_color_new) return + var/UI_style_color_new = tgui_input_color(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) + if(isnull(UI_style_color_new)) return UI_style_color = UI_style_color_new if(ishuman(usr)) //mid-round preference changes, for aesthetics @@ -2959,6 +2931,9 @@ GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts if((disabilities & DISABILITY_FLAG_PARAPLEGIA) && !(new_species.blacklisted_disabilities & DISABILITY_FLAG_PARAPLEGIA)) character.force_gene_block(GLOB.paraplegiablock, TRUE, TRUE) + if((disabilities & DISABILITY_FLAG_APHASIA) && !(new_species.blacklisted_disabilities & DISABILITY_FLAG_APHASIA)) + character.force_gene_block(GLOB.aphasiablock, TRUE, TRUE) + character.dna.species.handle_dna(character) if(character.dna.dirtySE) diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index d047654588f..4bbd63e44ca 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -434,8 +434,8 @@ blackbox_message = "Set Own OOC" /datum/preference_toggle/special_toggle/set_ooc_color/set_toggles(client/user) - var/new_ooccolor = input(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) as color|null - if(new_ooccolor) + var/new_ooccolor = tgui_input_color(usr, "Please select your OOC color.", "OOC color", user.prefs.ooccolor) + if(!isnull(new_ooccolor)) user.prefs.ooccolor = new_ooccolor to_chat(usr, "Your OOC color has been set to [new_ooccolor].") else diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index de7194f995e..0dc22fed05d 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -489,3 +489,39 @@ SKILLS /obj/item/clothing/glasses/hud/skills/tajblind/attack_self(mob/user) toggle_veil(user) + +/obj/item/clothing/glasses/hud/blueshield + name = "multi-mode HUD glasses" + desc = "Солнечные очки с многорежимным проекционным дисплеем." + ru_names = list( + NOMINATIVE = "много-режимные HUD-очки", + GENITIVE = "много-режимных HUD-очков", + DATIVE = "много-режимным HUD-очкам", + ACCUSATIVE = "много-режимные HUD-очки", + INSTRUMENTAL = "много-режимными HUD-очками", + PREPOSITIONAL = "много-режимных HUD-очках" + ) + actions_types = list(/datum/action/item_action/switch_hud) + icon_state = "sunhudmed" + origin_tech = "magnets=4;combat=4;engineering=4;biotech=4" + see_in_dark = 1 + flash_protect = FLASH_PROTECTION_FLASH + tint = 1 + HUDType = DATA_HUD_MEDICAL_ADVANCED + +/obj/item/clothing/glasses/hud/blueshield/attack_self(mob/user) + if(HUDType) + var/datum/atom_hud/H = GLOB.huds[HUDType] + H.remove_hud_from(user) + switch(HUDType) + if(DATA_HUD_MEDICAL_ADVANCED) + HUDType = DATA_HUD_SECURITY_BASIC + examine_extensions = EXAMINE_HUD_SKILLS + if(DATA_HUD_SECURITY_ADVANCED) + HUDType = DATA_HUD_MEDICAL_ADVANCED + examine_extensions = EXAMINE_HUD_MEDICAL + else + HUDType = DATA_HUD_SECURITY_ADVANCED + examine_extensions = EXAMINE_HUD_SECURITY_READ | EXAMINE_HUD_SECURITY_WRITE + balloon_alert(user, "режим переключён") + return diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index d3a911963fc..0aa3eb6a812 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -145,7 +145,7 @@ desc = "Pair of gloves with some protection" icon_state = "armored_gloves" item_state = "armored_gloves" - armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + armor = list("melee" = 5, "bullet" = 25, "laser" = 10, "energy" = 5, "bomb" = 5, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 75) can_be_cut = FALSE sprite_sheets = list( SPECIES_VOX = 'icons/mob/clothing/species/vox/gloves.dmi', @@ -294,4 +294,4 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT strip_delay = 60 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) + armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 30, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 75, "acid" = 75) diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index ac3749345c2..13fd4ae3c03 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -35,8 +35,8 @@ transfer_prints = FALSE /obj/item/clothing/gloves/combat - desc = "These tactical gloves are both insulated and offer protection from heat sources." name = "combat gloves" + desc = "These tactical gloves are both insulated and offer melee protection." icon_state = "combat" item_state = "swat_gl" siemens_coefficient = 0 @@ -47,7 +47,22 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + armor = list("melee" = 25, "bullet" = 5, "laser" = 5, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 70) + +/obj/item/clothing/gloves/combat/riot + name = "riot gloves" + desc = "These riot gloves are both insulated and offer melee protection." + icon_state = "riotgloves" + item_state = "riotgloves" + sprite_sheets = list( + SPECIES_VOX = 'icons/mob/clothing/species/vox/gloves.dmi', + SPECIES_DRASK = 'icons/mob/clothing/species/drask/gloves.dmi', + SPECIES_MONKEY = 'icons/mob/clothing/species/monkey/gloves.dmi', + SPECIES_FARWA = 'icons/mob/clothing/species/monkey/gloves.dmi', + SPECIES_WOLPIN = 'icons/mob/clothing/species/monkey/gloves.dmi', + SPECIES_NEARA = 'icons/mob/clothing/species/monkey/gloves.dmi', + SPECIES_STOK = 'icons/mob/clothing/species/monkey/gloves.dmi' + ) /obj/item/clothing/gloves/bracer name = "bone bracers" @@ -386,7 +401,7 @@ name = "SWAT gloves" icon_state = "swat_gloves" item_state = "nt_swat_gl" - armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) + armor = list("melee" = 15, "bullet" = 15, "laser" = 15, "energy" = 15, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 75) sprite_sheets = list( SPECIES_VOX = 'icons/mob/clothing/species/vox/gloves.dmi', SPECIES_DRASK = 'icons/mob/clothing/species/drask/gloves.dmi', diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index 56db1c4aadf..792386969be 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -126,8 +126,9 @@ dog_fashion = null sprite_sheets = list( SPECIES_VOX = 'icons/mob/clothing/species/vox/helmet.dmi', + SPECIES_GREY = 'icons/mob/clothing/species/grey/helmet.dmi', SPECIES_VULPKANIN = 'icons/mob/clothing/species/vulpkanin/helmet.dmi' - ) + ) /obj/item/clothing/head/helmet/riot/knight name = "medieval helmet" diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index e17f94aa6a6..1195c8a3a2f 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -16,6 +16,26 @@ pickup_sound = 'sound/items/handling/boots_pickup.ogg' drop_sound = 'sound/items/handling/boots_drop.ogg' +/obj/item/clothing/shoes/combat/riot + name = "riot boots" + desc = "High speed, low drag riot boots." + can_cut_open = FALSE + icon_state = "riotboots" + item_state = "riotboots" + sprite_sheets = list( + SPECIES_VOX = 'icons/mob/clothing/species/vox/shoes.dmi', + SPECIES_DRASK = 'icons/mob/clothing/species/drask/shoes.dmi', + SPECIES_MONKEY = 'icons/mob/clothing/species/monkey/shoes.dmi', + SPECIES_FARWA = 'icons/mob/clothing/species/monkey/shoes.dmi', + SPECIES_WOLPIN = 'icons/mob/clothing/species/monkey/shoes.dmi', + SPECIES_NEARA = 'icons/mob/clothing/species/monkey/shoes.dmi', + SPECIES_STOK = 'icons/mob/clothing/species/monkey/shoes.dmi', + SPECIES_UNATHI = 'icons/mob/clothing/species/unathi/shoes.dmi', + SPECIES_ASHWALKER_BASIC = 'icons/mob/clothing/species/unathi/shoes.dmi', + SPECIES_ASHWALKER_SHAMAN = 'icons/mob/clothing/species/unathi/shoes.dmi', + SPECIES_DRACONOID = 'icons/mob/clothing/species/unathi/shoes.dmi' + ) + /obj/item/clothing/shoes/combat/swat //overpowered boots for death squads name = "\improper SWAT shoes" desc = "High speed, no drag combat boots." @@ -155,7 +175,7 @@ icon_state = "armored_shoes" item_color = "armored_shoes" item_state = "armored_shoes" - armor = list("melee" = 5, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + armor = list("melee" = 5, "bullet" = 25, "laser" = 10, "energy" = 5, "bomb" = 5, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 75) sprite_sheets = list( SPECIES_VOX = 'icons/mob/clothing/species/vox/shoes.dmi', SPECIES_DRASK = 'icons/mob/clothing/species/drask/shoes.dmi', diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index d9379daaec9..e2a8003dcbf 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -1152,6 +1152,21 @@ item_state = "stripe_federal" strip_bubble_icon = "federal" +/obj/item/clothing/accessory/head_strip/greytide + name = "GreyTide strip" + desc = "Плотно сшитая круглая нашивка серого цвета с расположенным в центре противогазом." + ru_names = list( + NOMINATIVE = "нашивка \"GreyTide\"", + GENITIVE = "нашивки \"GreyTide\"", + DATIVE = "нашивке \"GreyTide\"", + ACCUSATIVE = "нашивку \"GreyTide\"", + INSTRUMENTAL = "нашивкой \"GreyTide\"", + PREPOSITIONAL = "нашивке \"GreyTide\"" + ) + icon_state = "greytstrip" + item_state = "greytstrip" + strip_bubble_icon = "greyt" + /obj/item/clothing/accessory/head_strip/lawyers_badge name = "attorney's badge" desc = "Fills you with the conviction of JUSTICE. Lawyers tend to want to show it to everyone they meet." diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index 4dcd10981ba..c97a20f0941 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -684,7 +684,7 @@ /datum/crafting_recipe/bonesword name = "Bone Sword" - result = /obj/item/claymore/bone + result = /obj/item/melee/claymore/bone time = 4 SECONDS reqs = list(/obj/item/stack/sheet/bone = 3, /obj/item/stack/sheet/sinew = 2) @@ -1405,6 +1405,14 @@ /obj/item/toy/crayon/spraycan = 1) category = CAT_MISC +/datum/crafting_recipe/ashedlockerpaint + name = "Ashed customisation kit" + result = /obj/item/paintkit/lockermech_ashed + time = 35 + reqs = list(/obj/item/stack/sheet/cardboard = 5, + /obj/item/toy/crayon/spraycan = 1) + category = CAT_MISC + /datum/crafting_recipe/stacklifter name = "The weight stacklifter" result = /obj/structure/weightmachine/stacklifter @@ -1551,7 +1559,7 @@ /obj/item/stack/sheet/mineral/diamond = 5 ) result = list(/obj/item/pickaxe/diamond) - + /datum/crafting_recipe/drone name = "Inactive Drone" result = list(/obj/item/inactive_drone) diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index cb5b6bf4768..d261b290300 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -111,8 +111,8 @@ /obj/item/fluff/tattoo_gun/elliot_cybernetic_tat/attack_self(mob/user as mob) if(!used) - var/ink_color = input("Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) as color|null - if(ink_color && !(user.incapacitated() || used) ) + var/ink_color = tgui_input_color(usr, "Please select an ink color.", "Tattoo Ink Color", rgb(tattoo_r, tattoo_g, tattoo_b)) + if(!isnull(ink_color) && !(user.incapacitated() || used) ) tattoo_r = color2R(ink_color) tattoo_g = color2G(ink_color) tattoo_b = color2B(ink_color) @@ -138,13 +138,13 @@ to_chat(user, "You use [src] on yourself.") qdel(src) -/obj/item/claymore/fluff // MrBarrelrolll: Maximus Greenwood +/obj/item/melee/claymore/fluff // MrBarrelrolll: Maximus Greenwood name = "Greenwood's Blade" desc = "A replica claymore with strange markings scratched into the blade." force = 5 sharp = 0 -/obj/item/claymore/fluff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = ITEM_ATTACK) +/obj/item/melee/claymore/fluff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = ITEM_ATTACK) return 0 /obj/item/fluff/rsik_katana //Xydonus: Rsik Ugsharki Atan diff --git a/code/modules/economy/robotic_quests/mech_types.dm b/code/modules/economy/robotic_quests/mech_types.dm index edd66d9c5c7..36b019a782f 100644 --- a/code/modules/economy/robotic_quests/mech_types.dm +++ b/code/modules/economy/robotic_quests/mech_types.dm @@ -96,6 +96,7 @@ /obj/item/mecha_parts/mecha_equipment/medical/syringe_gun_upgrade, //You can't put this without syringe gun /obj/item/mecha_parts/mecha_equipment/servo_hydra_actuator, /obj/item/mecha_parts/mecha_equipment/improved_exosuit_control_system, + /obj/item/mecha_parts/mecha_equipment/medical/beamgun, ) /datum/quest_mech/gygax diff --git a/code/modules/events/abductor.dm b/code/modules/events/abductor.dm index 4013f804ee7..a994379ade9 100644 --- a/code/modules/events/abductor.dm +++ b/code/modules/events/abductor.dm @@ -7,8 +7,7 @@ processing = 0 //so it won't fire again in next tick if(!makeAbductorTeam()) message_admins("Abductor event failed to find players. Retrying in 30s.") - spawn(300) - makeAbductorTeam() + addtimer(CALLBACK(src, PROC_REF(makeAbductorTeam)), 30 SECONDS) /datum/event/abductor/proc/get_teams_num() return min(round(num_station_players() / for_players) + 1, game_mode_ref.max_teams) @@ -16,7 +15,7 @@ /datum/event/abductor/proc/makeAbductorTeam() var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Вы хотите занять роль Абдуктора?", ROLE_ABDUCTOR, TRUE) - if(length(candidates) < 2) + if(LAZYLEN(candidates) < 2) return FALSE if(SSticker.mode.config_tag == "abduction") @@ -26,7 +25,7 @@ var/num_teams = get_teams_num() for(var/i in 1 to num_teams) - if(length(candidates) < 2) + if(LAZYLEN(candidates) < 2) break var/number = SSticker.mode.abductor_teams + 1 @@ -54,6 +53,7 @@ if(SSticker.mode.config_tag != "abduction") SSticker.mode.abductors |= game_mode_ref.abductors + processing = 1 return TRUE diff --git a/code/modules/events/tear.dm b/code/modules/events/tear.dm index d4f0cd94132..af61941d254 100644 --- a/code/modules/events/tear.dm +++ b/code/modules/events/tear.dm @@ -42,9 +42,9 @@ animation.icon_state = "newtear" animation.icon = 'icons/effects/tear.dmi' animation.master = src - spawn(15) - if(animation) - qdel(animation) + + if(animation) + addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, animation), 1.5 SECONDS) addtimer(CALLBACK(src, PROC_REF(spew_critters)), rand(30, 120)) diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 597d12cc4bc..64c00810f33 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -724,7 +724,7 @@ GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/projectile, /obj/ite /obj/item/clothing/shoes/chameleon/noslip, /obj/item/card/id/syndicate,\ /obj/item/clothing/mask/chameleon, /obj/item/clothing/glasses/thermal,\ /obj/item/chameleon, /obj/item/card/emag,\ - /obj/item/storage/toolbox/syndicate, /obj/item/aiModule,\ + /obj/item/storage/toolbox/syndicate, /obj/item/ai_module,\ /obj/item/radio/headset/syndicate, /obj/item/grenade/plastic/c4,\ /obj/item/powersink, /obj/item/storage/box/syndie_kit,\ /obj/item/toy/syndicateballoon, /obj/item/gun/energy/laser/captain,\ @@ -781,7 +781,7 @@ GLOBAL_LIST_INIT(non_fakeattack_weapons, list(/obj/item/gun/projectile, /obj/ite if(person) //Basic talk var/image/speech_overlay = image('icons/mob/talk.dmi', person, "h0", layer = ABOVE_MOB_LAYER) SET_PLANE_EXPLICIT(speech_overlay, ABOVE_GAME_PLANE, src) - target.hear_say(message_to_multilingual(pick(speak_messages), safepick(person.languages)), speaker = person) + target.hear_say(message_to_multilingual(pick(speak_messages), safepick(person.languages)), speaker = person, is_whisper = TRUE) if(target.client) target.client.images |= speech_overlay sleep(30) diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm index 1ecca32f013..81a2184ae92 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm @@ -147,6 +147,11 @@ to_chat(user, span_warning("You have no idea how to cook with [I].")) return ATTACK_CHAIN_PROCEED|ATTACK_CHAIN_NO_AFTERATTACK +/obj/machinery/kitchen_machine/examine(mob/user) + . = ..() + if(in_range(src, user)) + . += "Alt-click to activate it.
Ctrl-Shift-click to dispose content.
" + /obj/machinery/kitchen_machine/AltClick(mob/living/carbon/human/human) if(!istype(human) || !human.Adjacent(src)) return diff --git a/code/modules/hallucination/_hallucination.dm b/code/modules/hallucination/_hallucination.dm new file mode 100644 index 00000000000..c59b1af61a2 --- /dev/null +++ b/code/modules/hallucination/_hallucination.dm @@ -0,0 +1,123 @@ +/** + * Simple effect that holds an image + * to be shown to one or multiple clients only. + * + * Pass a list of mobs in initialize() that corresponds to all mobs that can see it. + */ +/obj/effect/client_image_holder + invisibility = INVISIBILITY_OBSERVER + anchored = TRUE + + /// A list of mobs which can see us. + var/list/mob/who_sees_us + /// The created image, what we look like. + var/image/shown_image + /// The icon file the image uses. If null, we have no image + var/image_icon + /// The icon state the image uses + var/image_state + /// The x pixel offset of the image + var/image_pixel_x = 0 + /// The y pixel offset of the image + var/image_pixel_y = 0 + /// Optional, the color of the image + var/image_color + /// The layer of the image + var/image_layer = MOB_LAYER + /// The plane of the image + var/image_plane = GAME_PLANE + +/obj/effect/client_image_holder/Initialize(mapload, list/mobs_which_see_us) + . = ..() + if(isnull(mobs_which_see_us)) + stack_trace("Client image holder was created with no mobs to see it.") + return INITIALIZE_HINT_QDEL + + shown_image = generate_image() + + if(!islist(mobs_which_see_us)) + mobs_which_see_us = list(mobs_which_see_us) + + who_sees_us = list() + for(var/mob/seer as anything in mobs_which_see_us) + RegisterSignal(seer, COMSIG_MOB_LOGIN, PROC_REF(show_image_to)) + RegisterSignal(seer, COMSIG_QDELETING, PROC_REF(remove_seer)) + who_sees_us += seer + show_image_to(seer) + +/obj/effect/client_image_holder/Destroy(force) + if(shown_image) + for(var/mob/seer as anything in who_sees_us) + remove_seer(seer) + shown_image = null + + who_sees_us.Cut() // probably not needed but who knows + return ..() + +/obj/effect/client_image_holder/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + . = ..() + if(QDELETED(src) || same_z_layer) + return + SET_PLANE(shown_image, PLANE_TO_TRUE(shown_image.plane), new_turf) + +/// Signal proc to clean up references if people who see us are deleted. +/obj/effect/client_image_holder/proc/remove_seer(mob/source) + SIGNAL_HANDLER + + UnregisterSignal(source, list(COMSIG_MOB_LOGIN, COMSIG_QDELETING)) + hide_image_from(source) + who_sees_us -= source + + // No reason to exist, anymore + if(!QDELETED(src) && !length(who_sees_us)) + qdel(src) + +/// Generates the image which we take on. +/obj/effect/client_image_holder/proc/generate_image() + var/image/created = image(image_icon, src, image_state, image_layer, dir = src.dir) + SET_PLANE_EXPLICIT(created, image_plane, src) + created.pixel_x = image_pixel_x + created.pixel_y = image_pixel_y + if(image_color) + created.color = image_color + return created + +/// Shows the image we generated to the passed mob +/obj/effect/client_image_holder/proc/show_image_to(mob/show_to) + SIGNAL_HANDLER + + show_to.client?.images |= shown_image + +/// Hides the image we generated from the passed mob +/obj/effect/client_image_holder/proc/hide_image_from(mob/hide_from) + SIGNAL_HANDLER + + hide_from.client?.images -= shown_image + +/// Simple helper for refreshing / showing the image to everyone in our list. +/obj/effect/client_image_holder/proc/regenerate_image() + for(var/mob/seer as anything in who_sees_us) + hide_image_from(seer) + + shown_image = generate_image() + + for(var/mob/seer as anything in who_sees_us) + show_image_to(seer) + +// Whenever we perform icon updates, regenerate our image +/obj/effect/client_image_holder/update_icon(updates = ALL) + . = ..() + regenerate_image() + +// If we move for some reason, regenerate our image +/obj/effect/client_image_holder/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + . = ..() + if(!loc) + return + regenerate_image() + +/obj/effect/client_image_holder/singularity_pull() + return + +/obj/effect/client_image_holder/singularity_act() + return diff --git a/code/modules/library/codex_gigas.dm b/code/modules/library/codex_gigas.dm index ef54b64f626..e97cd7646fc 100644 --- a/code/modules/library/codex_gigas.dm +++ b/code/modules/library/codex_gigas.dm @@ -7,50 +7,71 @@ author = "Forces beyond your comprehension" unique = TRUE title = "The codex gigas" - var/inUse = 0 - + var/inUse = FALSE /obj/item/book/codex_gigas/attack_self(mob/user) if(!user.has_vision()) return + if(inUse) to_chat(user,"Someone else is reading it.") return + if(!user.is_literate()) - to_chat(user,"You don't know how to read.") + to_chat(user, span_notice("You don't know how to read.")) + return + + if(!ishuman(user)) + return + + var/mob/living/carbon/human/human = user + + if(locate(/datum/objective/sintouched/acedia) in human.mind?.objectives) + to_chat(user, span_notice("None of this matters, why are you reading this? You put the [title] down.")) + return + + inUse = TRUE + + var/devilName = copytext(sanitize(input(user, "What infernal being do you wish to research?", "Codex Gigas", null) as text), 1, MAX_MESSAGE_LEN) + var/speed = 30 SECONDS + var/correctness = 85 + var/willpower = 95 + + if(human.job in list(JOB_TITLE_LIBRARIAN)) // the librarian is both faster, and more accurate than normal crew members at research + speed = 4.5 SECONDS + correctness = 100 + willpower = 100 + + if(human.job in list(JOB_TITLE_CHAPLAIN)) // the librarian is both faster, and more accurate than normal crew members at research + speed = 30 SECONDS + correctness = 100 + + if(human.job in list(JOB_TITLE_CAPTAIN, JOB_TITLE_OFFICER, JOB_TITLE_HOS, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN)) + willpower = 99 + + if(human.job in list(JOB_TITLE_CLOWN)) // WHO GAVE THE CLOWN A DEMONOMICON? BAD THINGS WILL HAPPEN! + willpower = 25 + + correctness -= human.getBrainLoss() *0.5 //Brain damage makes researching hard. + speed += human.getBrainLoss() * 0.3 SECONDS + user.visible_message("[user] opens [title] and begins reading intently.") + + if(!do_after(human, speed, human, DEFAULT_DOAFTER_IGNORE | DA_IGNORE_HELD_ITEM)) return - if(ishuman(user)) - var/mob/living/carbon/human/U = user - if(U.check_acedia()) - to_chat(user,"None of this matters, why are you reading this? You put the [title] down.") - return - inUse = 1 - var/devilName = copytext(sanitize(input(user, "What infernal being do you wish to research?", "Codex Gigas", null) as text),1,MAX_MESSAGE_LEN) - var/speed = 30 SECONDS - var/correctness = 85 - var/willpower = 95 - if(U.job in list(JOB_TITLE_LIBRARIAN)) // the librarian is both faster, and more accurate than normal crew members at research - speed = 4.5 SECONDS - correctness = 100 - willpower = 100 - if(U.job in list(JOB_TITLE_CHAPLAIN)) // the librarian is both faster, and more accurate than normal crew members at research - speed = 30 SECONDS - correctness = 100 - if(U.job in list(JOB_TITLE_CAPTAIN, JOB_TITLE_OFFICER, JOB_TITLE_HOS, JOB_TITLE_DETECTIVE, JOB_TITLE_WARDEN)) - willpower = 99 - if(U.job in list(JOB_TITLE_CLOWN)) // WHO GAVE THE CLOWN A DEMONOMICON? BAD THINGS WILL HAPPEN! - willpower = 25 - correctness -= U.getBrainLoss() *0.5 //Brain damage makes researching hard. - speed += U.getBrainLoss() * 0.3 SECONDS - user.visible_message("[user] opens [title] and begins reading intently.") - if(do_after(U, speed, U, DEFAULT_DOAFTER_IGNORE|DA_IGNORE_HELD_ITEM)) - var/usedName = devilName - if(!prob(correctness)) - usedName += "x" - var/datum/devilinfo/devil = devilInfo(usedName, 0) - user << browse("Information on [devilName]


[GLOB.lawlorify[LORE][devil.ban]]
[GLOB.lawlorify[LORE][devil.bane]]
[GLOB.lawlorify[LORE][devil.obligation]]
[GLOB.lawlorify[LORE][devil.banish]]", "window=book") - inUse = 0 - sleep(10) - if(!prob(willpower)) - U.influenceSin() - onclose(user, "book") + + var/usedName = devilName + + if(!prob(correctness)) + usedName += "x" + + var/datum/antagonist/devil/devil = devilInfo(usedName) + user << browse("Information on [devilName]


[devil.info.ban.desc]
[devil.info.bane.desc]
[devil.info.obligation.desc]
[devil.info.banish.desc]", "window=book") + + inUse = FALSE + addtimer(CALLBACK(src, PROC_REF(close), human, willpower), 10 SECONDS) + +/obj/item/book/codex_gigas/proc/close(mob/living/carbon/human/human, willpower) + if(!prob(willpower)) + human.mind?.add_antag_datum(/datum/antagonist/sintouched) + + onclose(human, "book") diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm index 1391ac128b3..5a6b8ccf444 100644 --- a/code/modules/martial_arts/martial.dm +++ b/code/modules/martial_arts/martial.dm @@ -417,7 +417,15 @@ /obj/item/CQC_manual name = "old manual" - desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat." + desc = "Небольшая книжка чёрного цвета. Это подробное руководство по тактике рукопашного боя." + ru_names = list( + NOMINATIVE = "старое руководство", + GENITIVE = "старого руководства", + DATIVE = "старому руководству", + ACCUSATIVE = "старое руководство", + INSTRUMENTAL = "старым руководством", + PREPOSITIONAL = "старом руководстве" + ) icon = 'icons/obj/library.dmi' icon_state = "cqcmanual" @@ -427,27 +435,35 @@ if(user.mind) //Prevents changelings and vampires from being able to learn it if(ischangeling(user)) - to_chat(user, "We try multiple times, but we simply cannot grasp the basics of CQC!") + to_chat(user, span_warning("Как бы мы не пытались, у нас не получается понять даже основы CQC!")) return else if(isvampire(user)) //Vampires - to_chat(user, "Your blood lust distracts you from the basics of CQC!") + to_chat(user, span_warning("Ваша жажда крови отвлекает вас от изучения CQC!")) return else if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "The mere thought of combat, let alone CQC, makes your head spin!") + to_chat(user, span_warning("От одной мысли о драке, не говоря уже о CQC, ваша голова идёт кругом!")) return - to_chat(user, span_boldannounceic("You remember the basics of CQC.")) + to_chat(user, span_boldannounceic("Вы быстро пробегаетесь глазами по страницам книги, запоминая основы CQC.")) var/datum/martial_art/cqc/CQC = new(null) CQC.teach(user) user.temporarily_remove_item_from_inventory(src) - visible_message("[src] beeps ominously, and a moment later it bursts up in flames.") + visible_message(span_warning("[declent_ru(NOMINATIVE)] зловеще пищит, после чего вспыхивает ярким пламенем!")) new /obj/effect/decal/cleanable/ash(get_turf(src)) qdel(src) /obj/item/CQC_manual/chef name = "CQC Upgrade implant" - desc = "Gives you to remember what you always forget" + desc = "Небольшой шприц, содержащий в себе имплант. Даёт вам запомнить то, что вы всегда забываете." + ru_names = list( + NOMINATIVE = "имплант улучшения CQC", + GENITIVE = "импланта улучшения CQC", + DATIVE = "импланту улучшения CQC", + ACCUSATIVE = "имплант улучшения CQC", + INSTRUMENTAL = "имплантом улучшения CQC", + PREPOSITIONAL = "импланте улучшения CQC" + ) icon = 'icons/obj/items.dmi' icon_state = "implanter1" item_state = "syringe_0" @@ -455,24 +471,41 @@ /obj/item/CQC_manual/chef/attack_self(mob/living/carbon/human/user) if(!istype(user)) return - if(user.mind && user.mind.assigned_role == JOB_TITLE_CHEF) - to_chat(user, span_boldannounceic(">You completely memorise the basics of CQC.")) - var/datum/martial_art/cqc/CQC = new(null) - CQC.teach(user) - user.temporarily_remove_item_from_inventory(src) - visible_message("[src] beeps ominously, and a moment later it blow up.") - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - else - to_chat(user, "You implant yourself, but nanobots can't find their target. You feel sharp pain in head!") + + if(!(user.mind && user.mind.assigned_role == JOB_TITLE_CHEF)) + to_chat(user, span_notice("Вы имплантируете себя, но наноботы не могут найти свою цель. Вы чувствуете острую головную боль!")) if(isliving(user)) var/mob/living/L = user L.apply_damages(burn = 20, brain = 20, spread_damage = TRUE) - user.temporarily_remove_item_from_inventory(src) - visible_message("[src] beeps ominously, and a moment later it blow up!") - playsound(get_turf(src),'sound/effects/explosion2.ogg', 100, 1) - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) + use_implant(user) + return + + if(ischangeling(user)) + to_chat(user, span_warning("Мы имплантируем себя, но наноботы не успевают достичь своей цели и разрушаются.")) + use_implant(user) + return + + if(isvampire(user)) + to_chat(user, span_warning("Вы имплантируете себя, но ваша кровь разрушает наноботов быстрее, чем они достигают своей цели.")) + use_implant(user) + return + + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, span_warning("От одной мысли о драке, не говоря уже о CQC, голова идёт кругом! Вы не решаетесь вколоть в себя имплант.")) + return + + to_chat(user, span_boldannounceic("Вы полностью запоминаете основы CQC.")) + var/datum/martial_art/cqc/CQC = new(null) + CQC.teach(user) + use_implant(user) + +/obj/item/CQC_manual/chef/proc/use_implant(mob/living/carbon/human/user) + user.temporarily_remove_item_from_inventory(src) + visible_message(span_warning("[declent_ru(NOMINATIVE)] зловеще пищит, после чего взрывается!")) + playsound(get_turf(src),'sound/effects/explosion2.ogg', 100, TRUE) + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) + /obj/item/mr_chang_technique name = "«Aggressive Marketing Technique»" diff --git a/code/modules/mini_games/thunderdome/gamemodes/gamemode.dm b/code/modules/mini_games/thunderdome/gamemodes/gamemode.dm index 12e097e4459..03de50171b3 100644 --- a/code/modules/mini_games/thunderdome/gamemodes/gamemode.dm +++ b/code/modules/mini_games/thunderdome/gamemodes/gamemode.dm @@ -60,7 +60,7 @@ /obj/item/twohanded/spear/bonespear/chitinspear = 1, /obj/item/twohanded/garrote = 1, /obj/item/melee/rapier/syndie = 1, - /obj/item/claymore/bone = 1, + /obj/item/melee/claymore/bone = 1, /obj/item/gun/magic/staff/spellblade = 1, /obj/item/spellbook/oneuse/goliath_dash = 1, ) @@ -75,6 +75,7 @@ random_items_count = 3 item_pool = list( /obj/item/gun/energy/immolator/multi = 2, + /obj/item/gun/energy/gun/minigun = 1, /obj/item/gun/projectile/automatic/mini_uzi = 2, /obj/item/gun/projectile/automatic/pistol/deagle = 2, /obj/item/gun/projectile/automatic/wt550 = 2, @@ -139,6 +140,7 @@ random_items_count = 3 item_pool = list( /obj/item/gun/energy/immolator/multi = 1, + /obj/item/gun/energy/gun/minigun = 1, /obj/item/gun/projectile/automatic/mini_uzi = 1, /obj/item/gun/projectile/automatic/pistol/deagle = 1, /obj/item/gun/projectile/automatic/wt550 = 1, @@ -225,7 +227,7 @@ /obj/item/twohanded/spear/bonespear/chitinspear = 1, /obj/item/twohanded/garrote = 1, /obj/item/melee/rapier/syndie = 1, - /obj/item/claymore/bone = 1, + /obj/item/melee/claymore/bone = 1, /obj/item/gun/magic/staff/spellblade = 1, /obj/item/spellbook/oneuse/goliath_dash = 1, /obj/item/spellbook/oneuse/forcewall = 1, diff --git a/code/modules/mini_games/thunderdome/thunderdome_battle.dm b/code/modules/mini_games/thunderdome/thunderdome_battle.dm index 8d233c1c83c..0821efba0dc 100644 --- a/code/modules/mini_games/thunderdome/thunderdome_battle.dm +++ b/code/modules/mini_games/thunderdome/thunderdome_battle.dm @@ -5,7 +5,7 @@ #define ARENA_COOLDOWN 5 MINUTES //After which time thunderdome will be once again allowed to use #define CQC_ARENA_RADIUS 6 //how much tiles away from a center players will spawn #define RANGED_ARENA_RADIUS 10 -#define VOTING_POLL_TIME 30 SECONDS +#define VOTING_POLL_TIME 10 SECONDS #define MAX_PLAYERS_COUNT 16 #define MIN_PLAYERS_COUNT 2 #define SPAWN_COEFFICENT 0.85 //how many (polled * spawn_coefficent) players will go brawling diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 8d08d07bb8e..70070c1d37b 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -115,7 +115,7 @@ if(91) new /obj/item/soulstone/anybody(src) if(92) - new /obj/item/katana(src) + new /obj/item/melee/katana(src) if(93) new /obj/item/dnainjector/xraymut(src) if(94) diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 3650601de72..1b15289a9c6 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -1,7 +1,15 @@ /****************Explorer's Suit and Mask****************/ /obj/item/clothing/suit/hooded/explorer name = "explorer suit" - desc = "An armoured suit for exploring harsh environments." + desc = "Бронированный костюм, созданный для исследования и работы в суровых условиях." + ru_names = list( + NOMINATIVE = "костюм исследователя", + GENITIVE = "костюма исследователя", + DATIVE = "костюму исследователя", + ACCUSATIVE = "костюм исследователя", + INSTRUMENTAL = "костюмом исследователя", + PREPOSITIONAL = "костюме исследователя" + ) icon_state = "explorer" item_state = "explorer" item_color = "explorer" @@ -34,7 +42,15 @@ /obj/item/clothing/head/hooded/explorer name = "explorer hood" - desc = "An armoured hood for exploring harsh environments." + desc = "Бронированный капюшон, созданный для исследования и работы в суровых условиях." + ru_names = list( + NOMINATIVE = "капюшон исследователя", + GENITIVE = "капюшона исследователя", + DATIVE = "капюшону исследователя", + ACCUSATIVE = "капюшон исследователя", + INSTRUMENTAL = "капюшоном исследователя", + PREPOSITIONAL = "капюшоне исследователя" + ) icon_state = "explorer" item_state = "explorer" body_parts_covered = HEAD @@ -65,7 +81,15 @@ /obj/item/clothing/suit/space/hostile_environment name = "H.E.C.K. suit" - desc = "Hostile Environment Cross-Kinetic Suit: A suit designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + desc = "Экспериментальный Кинетический Защитный Обшитый Костюм: костюм, специально созданный для защиты от широкого спектра опасностей Лаваленда. Прошлому его владельцу этого, видимо, не хватило." + ru_names = list( + NOMINATIVE = "Э.К.З.О. костюм", + GENITIVE = "Э.К.З.О. костюма ", + DATIVE = "Э.К.З.О. костюму", + ACCUSATIVE = "Э.К.З.О. костюм", + INSTRUMENTAL = "Э.К.З.О. костюмом", + PREPOSITIONAL = "Э.К.З.О. костюме" + ) icon_state = "hostile_env" item_state = "hostile_env" max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT @@ -103,13 +127,21 @@ if(istype(C) && prob(2)) //cursed by bubblegum if(prob(15)) new /obj/effect/hallucination/delusion(C.loc, C, force_kind = "demon", duration = 100, skip_nearby = 0) - to_chat(C, "[pick("I AM IMMORTAL.","KILL THEM ALL!","I SEE YOU.","WE ARE THE SAME!","DEATH CANNOT HOLD ME.")]") + to_chat(C, span_colossus("[pick("МЕНЯ НЕ УБИТЬ.", "НАЧНИ ТУТ РЕЗНЮ!", "Я ТЕБЯ ВИЖУ.", "МЫ ОДНО ЦЕЛОЕ!", "СМЕРТИ МЕНЯ НЕ СДЕРЖАТЬ.", "УСТРОЙ КРОВАВУЮ БАНЮ!")]")) else - to_chat(C, "[pick("You hear faint whispers.","You smell ash.","You feel hot.","You hear a roar in the distance.")]") + to_chat(C, span_warning("[pick("Вы слышите тихий шепот.", "Вы чуете пепел.", "Вам жарко.", "Вы слышите рёв вдали.")]")) /obj/item/clothing/head/helmet/space/hostile_environment name = "H.E.C.K. helmet" - desc = "Hostile Environiment Cross-Kinetic Helmet: A helmet designed to withstand the wide variety of hazards from Lavaland. It wasn't enough for its last owner." + desc = "Экспериментальный Кинетический Защитный Обшитый Шлем: шлем, специально созданный для защиты от широкого спектра опасностей Лаваленда. Прошлому его владельцу этого, видимо, не хватило." + ru_names = list( + NOMINATIVE = "Э.К.З.О. шлем", + GENITIVE = "Э.К.З.О. шлема ", + DATIVE = "Э.К.З.О. шлему", + ACCUSATIVE = "Э.К.З.О. шлем", + INSTRUMENTAL = "Э.К.З.О. шлемом", + PREPOSITIONAL = "Э.К.З.О. шлеме" + ) icon_state = "hostile_env" item_state = "hostile_env" w_class = WEIGHT_CLASS_NORMAL @@ -141,7 +173,15 @@ /obj/item/clothing/head/helmet/space/hardsuit/champion name = "champion's helmet" - desc = "Peering into the eyes of the helmet is enough to seal damnation." + desc = "Лишь одного взгляда в глаза этого шлема хватит, чтобы посеять ужас." + ru_names = list( + NOMINATIVE = "чемпионский шлем", + GENITIVE = "чемпионского шлема", + DATIVE = "чемпионскому шлему", + ACCUSATIVE = "чемпионский шлем", + INSTRUMENTAL = "чемпионским шлемом", + PREPOSITIONAL = "чемпионском шлеме" + ) icon_state = "hardsuit0-berserker" item_color = "berserker" max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT @@ -160,7 +200,15 @@ /obj/item/clothing/suit/space/hardsuit/champion name = "champion's hardsuit" - desc = "Voices echo from the hardsuit, driving the user insane." + desc = "Изнутри этой брони эхом проносятся голоса, медленно сводя с ума своего носителя." + ru_names = list( + NOMINATIVE = "чемпионская броня", + GENITIVE = "чемпионской брони", + DATIVE = "чемпионской броне", + ACCUSATIVE = "чемпионскую броню", + INSTRUMENTAL = "чемпионской бронёй", + PREPOSITIONAL = "чемпионской броне" + ) icon_state = "hardsuit-berserker" max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT slowdown = 0.25 // you are wearing a POWERFUL energy suit, after all @@ -180,13 +228,29 @@ /obj/item/clothing/head/helmet/space/hardsuit/champion/templar name = "dark templar's helmet" - desc = "Through darkness we see the light" + desc = "Сквозь тьму мы видим свет." + ru_names = list( + NOMINATIVE = "шлем Чёрного Храмовника", + GENITIVE = "шлема Чёрного Храмовника", + DATIVE = "шлему Чёрного Храмовника", + ACCUSATIVE = "шлем Чёрного Храмовника", + INSTRUMENTAL = "шлемом Чёрного Храмовника", + PREPOSITIONAL = "шлеме Чёрного Храмовника" + ) icon_state = "hardsuit0-templar" item_color = "templar" /obj/item/clothing/suit/space/hardsuit/champion/templar name = "dark templar's hardsuit" - desc = "No Pity! No Remorse! No Fear!" + desc = "Без жалости! Без сожалений! Без страха!" + ru_names = list( + NOMINATIVE = "доспехи Чёрного Храмовника", + GENITIVE = "доспехов Чёрного Храмовника", + DATIVE = "доспехам Чёрного Храмовника", + ACCUSATIVE = "доспехи Чёрного Храмовника", + INSTRUMENTAL = "доспехами Чёрного Храмовника", + PREPOSITIONAL = "доспехах Чёрного Храмовника" + ) icon_state = "darktemplar-follower0" item_color = "darktemplar-follower0" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/champion/templar @@ -194,32 +258,72 @@ /obj/item/clothing/head/helmet/space/hardsuit/champion/templar/premium name = "high dark templar's helmet" - desc = "The galaxy is the Emperor's.." + desc = "Галактика принадлежит Императору..." + ru_names = list( + NOMINATIVE = "шлем высшего Чёрного Храмовника", + GENITIVE = "шлема высшего Чёрного Храмовника", + DATIVE = "шлему высшего Чёрного Храмовника", + ACCUSATIVE = "шлем высшего Чёрного Храмовника", + INSTRUMENTAL = "шлемом высшего Чёрного Храмовника", + PREPOSITIONAL = "шлеме высшего Чёрного Храмовника" + ) icon_state = "hardsuit0-hightemplar" item_color = "hightemplar" /obj/item/clothing/suit/space/hardsuit/champion/templar/premium name = "high dark templar's hardsuit" - desc = "..And anyone or anything who challenges that claim is an enemy who must be destroyed." + desc = "...и любой, кто оспаривает это — враг, которого необходимо уничтожить." + ru_names = list( + NOMINATIVE = "доспехи высшего Чёрного Храмовника", + GENITIVE = "доспехов высшего Чёрного Храмовника", + DATIVE = "доспехам высшего Чёрного Храмовника", + ACCUSATIVE = "доспехи высшего Чёрного Храмовника", + INSTRUMENTAL = "доспехами высшего Чёрного Храмовника", + PREPOSITIONAL = "доспехах высшего Чёрного Храмовника" + ) icon_state = "darktemplar-chaplain0" item_color = "darktemplar-chaplain0" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/champion/templar/premium /obj/item/clothing/head/helmet/space/hardsuit/champion/inquisitor name = "inquisitor's helmet" - desc = "A helmet worn by those who deal with paranormal threats for a living." + desc = "Шлем, носимый теми, кто зарабатывает на хлеб борьбой с паранормальным." + ru_names = list( + NOMINATIVE = "шлем инквизитора", + GENITIVE = "шлема инквизитора", + DATIVE = "шлему инквизитора", + ACCUSATIVE = "шлем инквизитора", + INSTRUMENTAL = "шлемом инквизитора", + PREPOSITIONAL = "шлеме инквизитора" + ) icon_state = "hardsuit0-inquisitor" item_color = "inquisitor" /obj/item/clothing/suit/space/hardsuit/champion/inquisitor name = "inquisitor's hardsuit" - desc = "Powerful wards are built into this hardsuit, protecting the user from all manner of paranormal threats." + desc = "На этот скафандр наложены мощные охранные чары, защищающие владельца от паранормальных угроз любого характера." + ru_names = list( + NOMINATIVE = "скафандр инквизитора", + GENITIVE = "скафандра инквизитора", + DATIVE = "скафандру инквизитора", + ACCUSATIVE = "скафандр инквизитора", + INSTRUMENTAL = "скафандром инквизитора", + PREPOSITIONAL = "скафандре инквизитора" + ) icon_state = "hardsuit-inquisitor" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/champion/inquisitor /obj/item/clothing/suit/hooded/pathfinder name = "pathfinder cloak" - desc = "A thick cloak woven from sinew and hides, designed to protect its wearer from hazardous weather." + desc = "Тяжёлая мантия, сшитая из сухожилий и шкур, предназначенная для защиты носителя от опасной погоды." + ru_names = list( + NOMINATIVE = "мантия первопроходца", + GENITIVE = "мантии первопроходца", + DATIVE = "мантии первопроходца", + ACCUSATIVE = "мантию первопроходца", + INSTRUMENTAL = "мантией первопроходца", + PREPOSITIONAL = "мантии первопроходца" + ) allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival, /obj/item/twohanded/kinetic_crusher, /obj/item/hierophant_club, /obj/item/twohanded/fireaxe/boneaxe) icon_state = "pathcloak" item_state = "pathcloak" @@ -247,7 +351,15 @@ /obj/item/clothing/head/hooded/pathfinder name = "pathfinder kasa" - desc = "A helmet crafted from bones and sinew meant to protect its wearer from hazardous weather." + desc = "Головной убор, созданный из костей и связок, предназначенный для защиты носителя от опасной погоды." + ru_names = list( + NOMINATIVE = "каса первопроходца", + GENITIVE = "касы первопроходца", + DATIVE = "касе первопроходца", + ACCUSATIVE = "касу первопроходца", + INSTRUMENTAL = "касой первопроходца", + PREPOSITIONAL = "касе первопроходца" + ) icon_state = "pathhead" item_state = "pathhead" body_parts_covered = HEAD diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index 822765b8858..3f48c11605f 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -4,8 +4,16 @@ icon_state = "crusher" item_state = "crusher0" name = "proto-kinetic crusher" - desc = "An early design of the proto-kinetic accelerator, it is little more than a combination of various mining tools cobbled together, forming a high-tech club. \ - While it is an effective mining tool, it did little to aid any but the most skilled and/or suicidal miners against local fauna." + desc = "Ранний дизайн прото-кинетического акселератора, лишь немногим отличающийся от кучи различных шахтёрских инструментов, прибитых друг к другу, формирующих высокотехнологичный топор. \ + Хоть это и является эффективным шахтёрским инструментом, для борьбы с местной фауной его могут использовать либо самые опытные, либо самые сумасшедшие шахтёры." + ru_names = list( + NOMINATIVE = "прото-кинетический крушитель", + GENITIVE = "прото-кинетического крушителя", + DATIVE = "прото-кинетическому крушителю", + ACCUSATIVE = "прото-кинетический крушитель", + INSTRUMENTAL = "прото-кинетическим крушителем", + PREPOSITIONAL = "прото-кинетическом крушителе" + ) force = 0 //You can't hit stuff unless wielded w_class = WEIGHT_CLASS_BULKY slot_flags = ITEM_SLOT_BACK @@ -37,11 +45,11 @@ /obj/item/twohanded/kinetic_crusher/examine(mob/living/user) . = ..() - . += "Mark a large creature with the destabilizing force, then hit them in melee to do [force + detonation_damage] damage." - . += "Does [force + detonation_damage + backstab_bonus] damage if the target is backstabbed, instead of [force + detonation_damage]." + . += span_notice("Отметьте существо дестабилизирующим полем, затем нанесите удар в ближнем бою, чтобы нанести [force + detonation_damage] единиц[declension_ru(force + detonation_damage, "у", "ы", "")] урона.") + . += span_notice("Наносит [force + detonation_damage + backstab_bonus] единиц[declension_ru(force + detonation_damage + backstab_bonus, "у", "ы", "")] урона вместо [force + detonation_damage], если удар был нанесён в спину.") for(var/t in trophies) var/obj/item/crusher_trophy/T = t - . += "It has \a [T] attached, which causes [T.effect_desc()]." + . += span_notice("К нему прикреплён[genderize_ru(T.gender, "", "а", "о", "ы")] [T.declent_ru(NOMINATIVE)], что вызывает следующий эффект: [T.effect_desc()].") /obj/item/twohanded/kinetic_crusher/attackby(obj/item/I, mob/user, params) @@ -69,9 +77,9 @@ /obj/item/twohanded/kinetic_crusher/attack(mob/living/target, mob/living/user, params, def_zone, skip_attack_anim = FALSE) if(!HAS_TRAIT(src, TRAIT_WIELDED)) - var/warn_message = "The [name] is too heavy to use with one hand." + var/warn_message = "[capitalize(declent_ru(NOMINATIVE))] слишком тяжёл, чтобы использовать его одной рукой." if(user.drop_item_ground(src)) - warn_message += " You fumble and drop it." + warn_message += "Вы роняете [declent_ru(ACCUSATIVE)] на землю." to_chat(user, span_warning(warn_message)) return ATTACK_CHAIN_BLOCKED_ALL var/datum/status_effect/crusher_damage/damage_track = target.has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) @@ -104,9 +112,9 @@ if(user.has_status_effect(STATUS_EFFECT_DASH) && user.a_intent == INTENT_HELP) if(user.throw_at(target, range = 3, speed = 3, spin = FALSE, diagonals_first = TRUE)) playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) - user.visible_message("[user] dashes!") + user.visible_message(span_warning("[user] соверша[pluralize_ru(user, "ет", "ют")] рывок!")) else - to_chat(user, "Something prevents you from dashing!") + to_chat(user, span_warning("Что-то не даёт вам совершить рывок!")) user.remove_status_effect(STATUS_EFFECT_DASH) return if(!proximity_flag && charged)//Mark a target, or mine a tile. @@ -214,7 +222,7 @@ var/target_turf = get_turf(target) if(ismineralturf(target_turf)) if(isancientturf(target_turf)) - visible_message("This rock appears to be resistant to all mining tools except pickaxes!") + visible_message(span_notice("Похоже, что эту породу возьмёт только кирка!")) else var/turf/simulated/mineral/M = target_turf new /obj/effect/temp_visual/kinetic_blast(M) @@ -224,7 +232,7 @@ //trophies /obj/item/crusher_trophy name = "tail spike" - desc = "A strange spike with no usage." + desc = "Странный шип без применений." icon = 'icons/obj/lavaland/artefacts.dmi' icon_state = "tail_spike" var/bonus_value = 10 //if it has a bonus effect, this is how much that effect is @@ -232,7 +240,7 @@ /obj/item/crusher_trophy/examine(mob/living/user) . = ..() - . += "Causes [effect_desc()] when attached to a kinetic crusher." + . += span_notice("Когда прикреплено к крушителю, вызывает следующий эффект: [effect_desc()].") /obj/item/crusher_trophy/proc/effect_desc() return "errors" @@ -250,7 +258,7 @@ /obj/item/crusher_trophy/proc/add_to(obj/item/twohanded/kinetic_crusher/crusher, mob/living/user) for(var/obj/item/crusher_trophy/crusher_trophy as anything in crusher.trophies) if(istype(crusher_trophy, denied_type) || istype(src, crusher_trophy.denied_type)) - to_chat(user, span_warning("You cannot attach [src] to [crusher]. Try to remove a few trophies first.")) + balloon_alert(user, "нет места!") return FALSE if(loc == user) if(!user.drop_transfer_item_to_loc(src, crusher)) @@ -258,7 +266,7 @@ else forceMove(crusher) crusher.trophies += src - to_chat(user, span_notice("You have attached [src] to [crusher].")) + balloon_alert(user, "прикреплено") return TRUE /obj/item/crusher_trophy/proc/remove_from(obj/item/twohanded/kinetic_crusher/H, mob/living/user) @@ -277,7 +285,15 @@ //goliath /obj/item/crusher_trophy/goliath_tentacle name = "goliath tentacle" - desc = "A sliced-off goliath tentacle. Suitable as a trophy for a kinetic crusher." + desc = "Отрубленное щупальце голиафа. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "щупальце голиафа", + GENITIVE = "щупальца голиафа", + DATIVE = "щупальцу голиафа", + ACCUSATIVE = "щупальце голиафа", + INSTRUMENTAL = "щупальцем голиафа", + PREPOSITIONAL = "щупальце голиафа" + ) icon_state = "goliath_tentacle" denied_type = /obj/item/crusher_trophy/goliath_tentacle bonus_value = 2 @@ -285,7 +301,7 @@ var/missing_health_desc = 10 /obj/item/crusher_trophy/goliath_tentacle/effect_desc() - return "mark detonation to do [bonus_value] more damage for every [missing_health_desc] health you are missing" + return "детонация метки дестабилизатора наносит на [bonus_value] единиц[declension_ru(bonus_value, "у", "ы", "")] урона больше за каждые [missing_health_desc] единиц[declension_ru(missing_health_desc, "у", "ы", "")] недостающего у вас здоровья" /obj/item/crusher_trophy/goliath_tentacle/on_mark_detonation(mob/living/target, mob/living/user) var/missing_health = user.health - user.maxHealth @@ -297,13 +313,21 @@ //watcher /obj/item/crusher_trophy/watcher_wing name = "watcher wing" - desc = "A wing ripped from a watcher. Suitable as a trophy for a kinetic crusher." + desc = "Оторванное крыло наблюдателя. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "крыло наблюдателя", + GENITIVE = "крыла наблюдателя", + DATIVE = "крылу наблюдателя", + ACCUSATIVE = "крыло наблюдателя", + INSTRUMENTAL = "крылом наблюдателя", + PREPOSITIONAL = "крыле наблюдателя" + ) icon_state = "watcher_wing" denied_type = /obj/item/crusher_trophy/watcher_wing bonus_value = 5 /obj/item/crusher_trophy/watcher_wing/effect_desc() - return "mark detonation to prevent certain creatures from using certain attacks for [bonus_value*0.1] second\s" + return "детонация метки дестабилизатора не позволяет некоторым существам использовать дальнобойные атаки в течении [bonus_value * 0.1] секунд[declension_ru(bonus_value * 0.1, "ы", "", "")]" /obj/item/crusher_trophy/watcher_wing/on_mark_detonation(mob/living/target, mob/living/user) if(ishostile(target)) @@ -317,13 +341,21 @@ //magmawing watcher /obj/item/crusher_trophy/blaster_tubes/magma_wing name = "magmawing watcher wing" - desc = "A still-searing wing from a magmawing watcher. Suitable as a trophy for a kinetic crusher." + desc = "Всё ещё пылающее крыло магмакрылого наблюдателя. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "крыло магмакрылого наблюдателя", + GENITIVE = "крыла магмакрылого наблюдателя", + DATIVE = "крылу магмакрылого наблюдателя", + ACCUSATIVE = "крыло магмакрылого наблюдателя", + INSTRUMENTAL = "крылом магмакрылого наблюдателя", + PREPOSITIONAL = "крыле магмакрылого наблюдателя" + ) icon_state = "magma_wing" gender = NEUTER bonus_value = 5 /obj/item/crusher_trophy/blaster_tubes/magma_wing/effect_desc() - return "mark detonation to make the next destabilizer shot deal [bonus_value] damage" + return "детонация метки дестабилизатора позволяет следующему выстрелу дестабилизатора нанести [bonus_value] единиц[declension_ru(bonus_value, "у", "ы", "")] урона" /obj/item/crusher_trophy/blaster_tubes/magma_wing/on_projectile_fire(obj/item/projectile/destabilizer/marker, mob/living/user) if(deadly_shot) @@ -336,20 +368,36 @@ //icewing watcher /obj/item/crusher_trophy/watcher_wing/ice_wing name = "icewing watcher wing" - desc = "A carefully preserved frozen wing from an icewing watcher. Suitable as a trophy for a kinetic crusher." + desc = "Хрупкое, замороженное крыло ледокрылого наблюдателя. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "крыло ледокрылого наблюдателя", + GENITIVE = "крыла ледокрылого наблюдателя", + DATIVE = "крылу ледокрылого наблюдателя", + ACCUSATIVE = "крыло ледокрылого наблюдателя", + INSTRUMENTAL = "крылом ледокрылого наблюдателя", + PREPOSITIONAL = "крыле ледокрылого наблюдателя" + ) icon_state = "ice_wing" bonus_value = 8 //legion /obj/item/crusher_trophy/legion_skull name = "legion skull" - desc = "A dead and lifeless legion skull. Suitable as a trophy for a kinetic crusher." + desc = "Разбитый, безжизненный череп легиона. Может быть установлен на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "череп легиона", + GENITIVE = "черепа легиона", + DATIVE = "черепу легиона", + ACCUSATIVE = "череп легиона", + INSTRUMENTAL = "черепом легиона", + PREPOSITIONAL = "черепе легиона" + ) icon_state = "legion_skull" denied_type = /obj/item/crusher_trophy/legion_skull bonus_value = 3 /obj/item/crusher_trophy/legion_skull/effect_desc() - return "a kinetic crusher to recharge [bonus_value*0.1] second\s faster" + return "выстрел дестабилизатора перезаряжается на [bonus_value * 0.1] секунд[declension_ru(bonus_value * 0.1, "у", "ы", "")] быстрее" /obj/item/crusher_trophy/legion_skull/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) . = ..() @@ -364,13 +412,21 @@ /// Massive eyed tentacle /obj/item/crusher_trophy/eyed_tentacle name = "Massive eyed tentacle" - desc = "Большое и глазастое щупальце древнего голиафа. Может быть установлено как трофей крашера." + desc = "Большое и глазастое щупальце древнего голиафа. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "огромное щупальце голиафа", + GENITIVE = "огромного щупальца голиафа", + DATIVE = "огромному щупальцу голиафа", + ACCUSATIVE = "огромное щупальце голиафа", + INSTRUMENTAL = "огромным щупальцем голиафа", + PREPOSITIONAL = "огромном щупальце голиафа" + ) icon_state = "ancient_goliath_tentacle" denied_type = /obj/item/crusher_trophy/eyed_tentacle bonus_value = 1 /obj/item/crusher_trophy/eyed_tentacle/effect_desc() - return "causes kinetic crusher to deal 50% more damage if target has more than 90% HP" + return "крушитель наносит на 50% больше урона, если у цели больше 90% здоровья" /obj/item/crusher_trophy/eyed_tentacle/on_melee_hit(mob/living/target, mob/living/user) var/procent = (target.health / target.maxHealth) * 100 @@ -386,13 +442,21 @@ /// Poison fang /obj/item/crusher_trophy/fang name = "Poison fang" - desc = "Уродливый и отравленный коготь. Может быть установлен как трофей крашера." + desc = "Уродливый и отравленный клык. Может быть установлен на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "отравленный клык", + GENITIVE = "отравленного клыка", + DATIVE = "отравленному клыку", + ACCUSATIVE = "отравленный клык", + INSTRUMENTAL = "отравленным клыком", + PREPOSITIONAL = "отравленном клыке" + ) icon_state = "ob_gniga" denied_type = /obj/item/crusher_trophy/fang bonus_value = 1.1 /obj/item/crusher_trophy/fang/effect_desc() - return "causes fauna to get 10% more damage after mark destroyed for 2 seconds" + return "фауна получает на 10% больше урона в течении 2 секунд после детонации метки дестабилизатора" /obj/item/crusher_trophy/fang/on_mark_detonation(mob/living/target, mob/living/user) target.apply_status_effect(STATUS_EFFECT_FANG_EXHAUSTION, bonus_value) @@ -400,13 +464,21 @@ /// Frost gland /obj/item/crusher_trophy/gland name = "Frost gland" - desc = "Замороженная железа. Может быть установлена как трофей крашера." + desc = "Замороженная железа. Может быть установлена на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "морозная железа", + GENITIVE = "морозной железы", + DATIVE = "морозной железе", + ACCUSATIVE = "морозную железу", + INSTRUMENTAL = "морозной железой", + PREPOSITIONAL = "морозной железе" + ) icon_state = "ice_gniga" denied_type = /obj/item/crusher_trophy/gland bonus_value = 0.9 /obj/item/crusher_trophy/gland/effect_desc() - return "causes fauna to deal 10% less damage when marked" + return "фауна наносит на 10% меньше урона, пока на неё установлена метка дестабилизатора" /obj/item/crusher_trophy/gland/on_mark_application(mob/living/simple_animal/target, datum/status_effect/crusher_mark/mark, had_mark) if(had_mark) @@ -428,24 +500,40 @@ //blood-drunk hunter /obj/item/crusher_trophy/miner_eye name = "eye of a blood-drunk hunter" - desc = "Its pupil is collapsed and turned to mush. Suitable as a trophy for a kinetic crusher." + desc = "Человеческий глаз с раздробленным в кашу зрачком. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "глаз кровожадного шахтёра", + GENITIVE = "глаза кровожадного шахтёра", + DATIVE = "глазу кровожадного шахтёра", + ACCUSATIVE = "глаз кровожадного шахтёра", + INSTRUMENTAL = "глазом кровожадного шахтёра", + PREPOSITIONAL = "глазе кровожадного шахтёра" + ) icon_state = "hunter_eye" denied_type = /obj/item/crusher_trophy/miner_eye /obj/item/crusher_trophy/miner_eye/effect_desc() - return "mark detonation to grant stun immunity and 90% damage reduction for 1 second" + return "детонация метки дестабилизатора даёт вам иммунитет к оглушению и уменьшение получаемого урона на 90%, на 1 секунду" /obj/item/crusher_trophy/miner_eye/on_mark_detonation(mob/living/target, mob/living/user) user.apply_status_effect(STATUS_EFFECT_BLOODDRUNK) //ash drake /obj/item/crusher_trophy/tail_spike - desc = "A spike taken from an ash drake's tail. Suitable as a trophy for a kinetic crusher." + desc = "Шип, срезанный с хвоста пепельного дрейка. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "хвостновой шип", + GENITIVE = "хвостового шипа", + DATIVE = "хвостовому шипу", + ACCUSATIVE = "хвостовой шип", + INSTRUMENTAL = "хвостовым шипом", + PREPOSITIONAL = "хвостовом шипе" + ) denied_type = /obj/item/crusher_trophy/tail_spike bonus_value = 5 /obj/item/crusher_trophy/tail_spike/effect_desc() - return "mark detonation to do [bonus_value] damage to nearby creatures and push them back" + return "детонация метки дестабилизатора взрывает врага, нанося [bonus_value] единиц[declension_ru(bonus_value, "у", "ы", "")] урона близлежащим врагам и отталкивая их" /obj/item/crusher_trophy/tail_spike/on_mark_detonation(mob/living/target, mob/living/user) for(var/mob/living/L in oview(2, user)) @@ -463,7 +551,15 @@ //bubblegum /obj/item/crusher_trophy/demon_claws name = "demon claws" - desc = "A set of blood-drenched claws from a massive demon's hand. Suitable as a trophy for a kinetic crusher." + desc = "Набор окровавленных когтей, вырванных с руки огромного демона. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "демонические когти", + GENITIVE = "демонических когтей", + DATIVE = "демоническим когтям", + ACCUSATIVE = "демонические когти", + INSTRUMENTAL = "демоническими когтями", + PREPOSITIONAL = "демонических когтях" + ) icon_state = "demon_claws" gender = PLURAL denied_type = /obj/item/crusher_trophy/demon_claws @@ -471,7 +567,7 @@ var/static/list/damage_heal_order = list(BRUTE, BURN, OXY) /obj/item/crusher_trophy/demon_claws/effect_desc() - return "melee hits to do [bonus_value * 0.2] more damage and heal you for [bonus_value * 0.1], with 5X effect on mark detonation" + return "удары в ближнем бою наносят на [bonus_value * 0.2] единиц[declension_ru(bonus_value * 0.2, "у", "ы", "")] урона больше и лечат вас на [bonus_value * 0.1] единиц[declension_ru(bonus_value * 0.1, "у", "ы", "")] здоровья, с пятерным эффектом при детонации метки" /obj/item/crusher_trophy/demon_claws/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) . = ..() @@ -499,7 +595,15 @@ //colossus /obj/item/crusher_trophy/blaster_tubes name = "blaster tubes" - desc = "The blaster tubes from a colossus's arm. Suitable as a trophy for a kinetic crusher." + desc = "Бластерные трубки, взятые с руки колосса. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "бластерные трубки", + GENITIVE = "бластерных трубок", + DATIVE = "бластерным трубкам", + ACCUSATIVE = "бластерные трубки", + INSTRUMENTAL = "бластерными трубками", + PREPOSITIONAL = "бластерных трубках" + ) icon_state = "blaster_tubes" gender = PLURAL denied_type = /obj/item/crusher_trophy/blaster_tubes @@ -507,7 +611,7 @@ var/deadly_shot = FALSE /obj/item/crusher_trophy/blaster_tubes/effect_desc() - return "mark detonation to make the next destabilizer shot deal [bonus_value] damage but move slower" + return "следующий выстрел дестабилизатора после детонации метки дестабилизатора будет лететь медленнее, но нанесёт [bonus_value] единиц[declension_ru(bonus_value, "у", "ы", "")] урона" /obj/item/crusher_trophy/blaster_tubes/on_projectile_fire(obj/item/projectile/destabilizer/marker, mob/living/user) if(deadly_shot) @@ -528,12 +632,20 @@ //hierophant /obj/item/crusher_trophy/vortex_talisman name = "vortex talisman" - desc = "A glowing trinket that was originally the Hierophant's beacon. Suitable as a trophy for a kinetic crusher." + desc = "Мерцающий талисман, ранее бывший маяком Иерофанта. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "талисман вихря", + GENITIVE = "талисмана вихря", + DATIVE = "талисману вихря", + ACCUSATIVE = "талисман вихря", + INSTRUMENTAL = "талисманом вихря", + PREPOSITIONAL = "талисмане вихря" + ) icon_state = "vortex_talisman" denied_type = /obj/item/crusher_trophy/vortex_talisman /obj/item/crusher_trophy/vortex_talisman/effect_desc() - return "mark detonation to create a homing hierophant chaser" //Wall was way too cheesy and allowed miners to be nearly invincible while dumb mob AI just rubbed its face on the wall. + return "детонация метки дестабилизатора призывает самонаводящуюся гончую Иерофанта" //Wall was way too cheesy and allowed miners to be nearly invincible while dumb mob AI just rubbed its face on the wall. /obj/item/crusher_trophy/vortex_talisman/on_mark_detonation(mob/living/target, mob/living/user) if(isliving(target)) @@ -545,13 +657,21 @@ //vetus /obj/item/crusher_trophy/adaptive_intelligence_core name = "adaptive intelligence core" - desc = "Seems to be one of the cores from a massive robot. Suitable as a trophy for a kinetic crusher." + desc = "Кажется, это одно из ядер огромного робота. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "адаптивное ядро ИИ", + GENITIVE = "адаптивного ядра ИИ", + DATIVE = "адаптивному ядру ИИ", + ACCUSATIVE = "адаптивное ядро ИИ", + INSTRUMENTAL = "адаптивным ядром ИИ", + PREPOSITIONAL = "адаптивном ядре ИИ" + ) icon_state = "adaptive_core" denied_type = /obj/item/crusher_trophy/adaptive_intelligence_core bonus_value = 2 /obj/item/crusher_trophy/adaptive_intelligence_core/effect_desc() - return "melee hits deal [bonus_value] more damage per hit after hitting a target, up to [bonus_value * 10] extra damage to that target" + return "удары в ближнем бою наносят на [bonus_value] единиц[declension_ru(bonus_value, "у", "ы", "")] урона больше после атаки по противнику, с пределом в [bonus_value * 10] единиц[declension_ru(bonus_value, "у", "ы", "")] урона" /obj/item/crusher_trophy/adaptive_intelligence_core/add_to(obj/item/twohanded/kinetic_crusher/H, mob/living/user) . = ..() @@ -567,12 +687,20 @@ /obj/item/crusher_trophy/empowered_legion_skull name = "empowered legion skull" - desc = "A powerful looking skull with glowing red eyes." + desc = "Устрашающий череп с горящими красными глазами. Может быть установлено на крушитель в качестве трофея." + ru_names = list( + NOMINATIVE = "усиленный череп легиона", + GENITIVE = "усиленного черепа легиона", + DATIVE = "усиленному черепу легиона", + ACCUSATIVE = "усиленный череп легиона", + INSTRUMENTAL = "усиленным черепом легиона", + PREPOSITIONAL = "усиленном черепе легиона" + ) icon_state = "ashen_skull" denied_type = /obj/item/crusher_trophy/empowered_legion_skull /obj/item/crusher_trophy/empowered_legion_skull/effect_desc() - return "mark detonation grants the ability to dash a short distance on help intent" + return "детонация метки дестабилизатора позволяет вам сделать рывок на небольшую дистанцию, если выбрано намерение помощи" /obj/item/crusher_trophy/empowered_legion_skull/on_mark_detonation(mob/living/target, mob/living/user) user.apply_status_effect(STATUS_EFFECT_DASH) @@ -583,7 +711,15 @@ icon_state = "magmite_crusher" item_state = "magmite_crusher0" name = "magmite proto-kinetic crusher" - desc = "An early design of the proto-kinetic accelerator, it is now a combination of various mining tools infused with magmite, forming a high-tech club, increasing its capacity as a mining tool." + desc = "Ранний дизайн прото-кинетического акселератора, теперь являющийся кучей различных шахтёрских иструментов приваренных друг к другу плазменным магмитом, формирующих высокотехнологичный топор. Магмит улучшает шахтёрские возможности крушителя." + ru_names = list( + NOMINATIVE = "магмитовый прото-кинетический крушитель", + GENITIVE = "магмитового прото-кинетического крушителя", + DATIVE = "магмитовому прото-кинетическому крушителю", + ACCUSATIVE = "магмитовый прото-кинетический крушитель", + INSTRUMENTAL = "магмитовым прото-кинетическим крушителем", + PREPOSITIONAL = "магмитовом прото-кинетическом крушителе" + ) destab = /obj/item/projectile/destabilizer/mega upgraded = TRUE @@ -595,7 +731,7 @@ var/target_turf = get_turf(target) if(ismineralturf(target_turf)) if(isancientturf(target_turf)) - visible_message("This rock appears to be resistant to all mining tools except pickaxes!") + visible_message(span_notice("Похоже, что эту породу возьмёт только кирка!")) forcedodge = 0 else var/turf/simulated/mineral/M = target_turf @@ -611,9 +747,17 @@ icon_state = "magmite_crusher" item_state = "magmite_crusher0" name = "unfinished proto-kinetic crusher" - desc = "An early design of the proto-kinetic accelerator, it is now a combination of various mining tools infused with magmite, forming a new design, but there is not enough magmite to upgrade it's destabilizer." + desc = "Ранний дизайн прото-кинетического акселератора, теперь являющийся кучей различных шахтёрских иструментов приваренных друг к другу плазменным магмитом. Судя по всему, магмитовых деталей на улучшение его дестабилизатора было недостаточно." + ru_names = list( + NOMINATIVE = "незавершенный прото-кинетический крушитель", + GENITIVE = "незавершенного прото-кинетического крушителя", + DATIVE = "незавершенному прото-кинетическому крушителю", + ACCUSATIVE = "незавершенный прото-кинетический крушитель", + INSTRUMENTAL = "незавершенным прото-кинетическим крушителем", + PREPOSITIONAL = "незавершенном прото-кинетическом крушителе" + ) upgraded = TRUE /obj/item/twohanded/kinetic_crusher/almost/examine(mob/living/user) . = ..() - . += "Perhaps you could use another magmite upgrade part to fully upgrade your crusher." + . += span_notice("Возможно, вы можете применить еще немного магмитовых деталей, чтобы полностью улучшить ваш крушитель.") diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm index a36867067e5..f33eebb2084 100644 --- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm +++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm @@ -185,6 +185,8 @@ name = "staff of lava" desc = "The power of fire and rocks in your hands!" icon_state = "lavastaff" + lefthand_file = 'icons/mob/inhands/staff_lefthand.dmi' + righthand_file = 'icons/mob/inhands/staff_righthand.dmi' item_state = "lavastaff" icon = 'icons/obj/weapons/magic.dmi' slot_flags = ITEM_SLOT_BACK diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index 1cc12eb483a..65fecf1434e 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -1,104 +1,213 @@ -/**********************Mint**************************/ - +#define COIN_COST MINERAL_MATERIAL_AMOUNT * 0.2 /obj/machinery/mineral/mint name = "coin press" + desc = "Массивная, слегка шумящая машина с тяжелыми стальными прессами. \ + Она используется для чеканки монет из различных матриалов. \ + На корпусе расположена панель управления с настройками для выбора металла и нанесения уникальных штампов." + + ru_names = list( + NOMINATIVE = "монетный пресс", + GENITIVE = "монетного пресса", + DATIVE = "монетному прессу", + ACCUSATIVE = "монетный пресс", + INSTRUMENTAL = "монетным прессом", + PREPOSITIONAL = "монетном прессе", + ) + icon = 'icons/obj/economy.dmi' - icon_state = "coinpress0" + icon_state = "coin_press" density = TRUE anchored = TRUE - var/newCoins = 0 //how many coins the machine made in it's last load - var/processing = FALSE - var/chosen = MAT_METAL //which material will be used to make coins - var/coinsToProduce = 10 - speed_process = TRUE -/obj/machinery/mineral/mint/New() - ..() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_PLASMA, MAT_SILVER, MAT_GOLD, MAT_URANIUM, MAT_DIAMOND, MAT_BANANIUM, MAT_TRANQUILLITE), MINERAL_MATERIAL_AMOUNT * 50, FALSE, /obj/item/stack) + /// How many coins did the machine make in total. + var/total_coins = 0 + /// Is it creating coins now? + var/active = FALSE + /// Which material will be used to make coins or for ejecting. + var/chosen_material + /// Inserted money bag. + var/obj/item/storage/bag/money/money_bag -/obj/machinery/mineral/mint/process() - var/turf/T = get_step(src, input_dir) - if(!T) - return +/obj/machinery/mineral/mint/Initialize(mapload) + . = ..() + var/static/list/coin_materials = list() + if(!length(coin_materials)) + for(var/datum/material/coin_mat as anything in subtypesof(/datum/material)) + var/obj/item/coin/coin_type = coin_mat.coin_type + if(!coin_type) + continue + coin_materials += coin_mat.id + + AddComponent(/datum/component/material_container, coin_materials, MINERAL_MATERIAL_AMOUNT * 50, FALSE, /obj/item/stack, _after_insert = CALLBACK(src, PROC_REF(material_insert))) + chosen_material = pick(coin_materials[1]) + +/obj/machinery/mineral/mint/Destroy() var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - for(var/obj/item/stack/sheet/O in T) - materials.insert_stack(O, O.amount) + materials.retrieve_all() + return ..() + +/obj/machinery/mineral/mint/update_icon_state() + if(active) + icon_state = "coin_press-active" + else + icon_state = "coin_press" + +/obj/machinery/mineral/mint/wrench_act(mob/user, obj/item/I) + default_unfasten_wrench(user, I, time = 4 SECONDS) + return TRUE /obj/machinery/mineral/mint/attack_hand(mob/user) - if(..()) - return - var/dat = {"Coin Press
"} + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/mineral/mint/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/mineral/mint/ui_state(mob/user) + return GLOB.default_state + +/obj/machinery/mineral/mint/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CoinMint") + ui.set_autoupdate(FALSE) + ui.open() + +/obj/machinery/mineral/mint/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/materials) + ) + +/obj/machinery/mineral/mint/ui_data(mob/user) + var/list/data = list() + + data["active"] = active + data["chosenMaterial"] = chosen_material + data["totalCoins"] = total_coins + data["moneyBag"] = !!money_bag + + if(money_bag) + data["moneyBagContent"] = length(money_bag.contents) + data["moneyBagMaxContent"] = money_bag.storage_slots var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + data["totalMaterials"] = materials.total_amount + data["maxMaterials"] = materials.max_amount + + var/list/material_list = list() for(var/mat_id in materials.materials) - var/datum/material/M = materials.materials[mat_id] - if(!M.amount && chosen != mat_id) - continue - dat += "
[M.name] amount: [M.amount] cm3 " - if(chosen == mat_id) - dat += "Chosen" - else - dat += "Choose" - - var/datum/material/M = materials.materials[chosen] - - dat += "

Will produce [coinsToProduce] [lowertext(M.name)] coins if enough materials are available.
" - dat += "-10 " - dat += "-5 " - dat += "-1 " - dat += "+1 " - dat += "+5 " - dat += "+10 " - - dat += "

In total this machine produced [newCoins] coins." - dat += "
Make coins" - user << browse(dat, "window=mint") - -/obj/machinery/mineral/mint/Topic(href, href_list) + var/datum/material/material = materials.materials[mat_id] + material_list += list(list( + "name" = material.name, + "amount" = material.amount / MINERAL_MATERIAL_AMOUNT, + "id" = material.id + )) + data["materials"] = material_list + + return data + +/obj/machinery/mineral/mint/ui_act(action, params, datum/tgui/ui) if(..()) return - usr.set_machine(src) - add_fingerprint(usr) - if(processing == 1) - to_chat(usr, "The machine is processing.") + . = TRUE + + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + switch(action) + if("selectMaterial") + if(!materials.materials[params["material"]]) + return + chosen_material = params["material"] + if("activate") + if(active) + active = FALSE + else + try_make_coins() + update_icon(UPDATE_ICON_STATE) + if("ejectMat") + var/datum/material/material = materials.materials[chosen_material] + if(material.amount < MINERAL_MATERIAL_AMOUNT) + to_chat(usr, span_warning("Недостаточно [material.name] для извлечения!")) + return + var/num_sheets = tgui_input_number(usr, "Сколько кусков вы хотите извлечь?", "Извлечь [material.name]", max_value = round(material.amount / MINERAL_MATERIAL_AMOUNT), min_value = 1) + if(isnull(num_sheets)) + return + materials.retrieve_sheets(num_sheets, chosen_material) + if("ejectBag") + eject_bag(usr) + +/obj/machinery/mineral/mint/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/storage/bag/money)) + if(money_bag) + to_chat(user, span_notice("Внутри уже есть [money_bag.declent_ru(NOMINATIVE)]!")) + balloon_alert(usr, "место уже занято!") + return ATTACK_CHAIN_PROCEED + if(!user.drop_from_active_hand()) + return ATTACK_CHAIN_PROCEED + to_chat(user, span_notice("Вы помещаете [I.declent_ru(ACCUSATIVE)] в [declent_ru(ACCUSATIVE)].")) + balloon_alert(usr, "мешок помещен") + I.forceMove(src) + money_bag = I + SStgui.update_uis(src) + return ATTACK_CHAIN_PROCEED_SUCCESS + + return ..() + +/obj/machinery/mineral/mint/process() + if(!active) + return + if(length(money_bag.contents) >= money_bag.storage_slots) + active = FALSE + visible_message(span_notice("[capitalize(declent_ru(NOMINATIVE))] прекращает производство, чтобы избежать переполнения.")) + balloon_alert_to_viewers("мешок переполнен") + update_icon(UPDATE_ICON_STATE) + SStgui.update_uis(src) + return + + var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) + var/datum/material/material = materials.materials[chosen_material] + if(!materials.can_use_amount(COIN_COST, chosen_material)) + active = FALSE + visible_message(span_notice("[capitalize(declent_ru(NOMINATIVE))] прекращает производство из-за нехватки материала.")) + balloon_alert_to_viewers("материал кончился") + update_icon(UPDATE_ICON_STATE) + SStgui.update_uis(src) return + + materials.use_amount_type(COIN_COST, chosen_material) + new material.coin_type(money_bag) + total_coins++ + SStgui.update_uis(src) + +/obj/machinery/mineral/mint/proc/try_make_coins(mob/user) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) - if(href_list["choose"]) - if(materials.materials[href_list["choose"]]) - chosen = href_list["choose"] - if(href_list["chooseAmt"]) - coinsToProduce = clamp(coinsToProduce + text2num(href_list["chooseAmt"]), 0, 1000) - if(href_list["makeCoins"]) - var/temp_coins = coinsToProduce - processing = TRUE - icon_state = "coinpress1" - var/coin_mat = MINERAL_MATERIAL_AMOUNT * 0.2 - var/datum/material/M = materials.materials[chosen] - if(!M || !M.coin_type) - updateUsrDialog() - return - - while(coinsToProduce > 0 && materials.use_amount_type(coin_mat, chosen)) - create_coins(M.coin_type) - coinsToProduce-- - newCoins++ - updateUsrDialog() - sleep(5) - - icon_state = "coinpress0" - processing = FALSE - coinsToProduce = temp_coins - updateUsrDialog() - -/obj/machinery/mineral/mint/proc/create_coins(P) - var/turf/T = get_step(src,output_dir) - if(T) - var/obj/item/O = new P(src) - var/obj/item/storage/bag/money/M = locate(/obj/item/storage/bag/money, T) - if(!M) - M = new /obj/item/storage/bag/money(src) - unload_mineral(M) - O.forceMove(M) + if(!money_bag) + visible_message(span_warning("[capitalize(declent_ru(NOMINATIVE))] не может работать без денежного мешка!")) + return + if(length(money_bag.contents) == money_bag.storage_slots) + visible_message(span_warning("[capitalize(money_bag.declent_ru(NOMINATIVE))] полон!")) + return + if(!materials.can_use_amount(COIN_COST, chosen_material)) + visible_message(span_warning("Недостаточно выбранного материала для производства!")) + return + active = TRUE + +/obj/machinery/mineral/mint/proc/eject_bag(mob/user) + if(!money_bag || !(user && iscarbon(user) && user.Adjacent(src))) + return + if(active) + active = FALSE + if(user.put_in_hands(money_bag)) + to_chat(user, span_notice("Вы забираете [money_bag.declent_ru(ACCUSATIVE)] из [declent_ru(GENITIVE)].")) + else + var/turf/T = get_step(src, output_dir) + money_bag.forceMove(T) + money_bag = null + SStgui.update_uis(src) + +/obj/machinery/mineral/mint/proc/material_insert() + SStgui.update_uis(src) + +#undef COIN_COST diff --git a/code/modules/mining/money_bag.dm b/code/modules/mining/money_bag.dm index 8a25b21b22d..340f7630394 100644 --- a/code/modules/mining/money_bag.dm +++ b/code/modules/mining/money_bag.dm @@ -2,6 +2,17 @@ /obj/item/storage/bag/money name = "money bag" + desc = "Просторный мешок из плотной ткани, украшенный крупным символом доллара. \ + Идеально подходит для хранения монет или банкнот. " + + ru_names = list( + NOMINATIVE = "денежный мешок", + GENITIVE = "денежного мешка", + DATIVE = "денежному мешку", + ACCUSATIVE = "денежный мешок", + INSTRUMENTAL = "денежным мешком", + PREPOSITIONAL = "денежном мешке", + ) icon_state = "moneybag" item_state = "moneybag" force = 10 @@ -14,6 +25,7 @@ max_combined_w_class = 40 can_hold = list(/obj/item/coin, /obj/item/stack/spacecash) + /obj/item/storage/bag/money/vault/populate_contents() new /obj/item/coin/silver(src) new /obj/item/coin/silver(src) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 04ed72018ef..9966f669e5f 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -237,7 +237,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\ desc = "Extremely explosive if struck with mining equipment, Gibtonite is often used by miners to speed up their work by using it as a mining charge. This material is illegal to possess by unauthorized personnel under space law." icon = 'icons/obj/mining.dmi' icon_state = "Gibtonite ore" - item_state = "Gibtonite ore" + item_state = "gibtonite" w_class = WEIGHT_CLASS_BULKY throw_range = 0 var/primed = FALSE diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index baac7eb2ac8..c6c0b535e1d 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -22,7 +22,7 @@ * updates the Z level for dead players * If they don't have a new z, we'll keep the old one, preventing bugs from ghosting and re-entering, among others */ -/mob/dead/proc/update_z(new_z) +/mob/dead/update_z(new_z) if(registered_z == new_z) return if(registered_z) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 469f5cff6cb..128b427e089 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -21,6 +21,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) light_system = NO_LIGHT_SUPPORT invisibility = INVISIBILITY_OBSERVER pass_flags = PASSEVERYTHING + hud_type = /datum/hud/ghost var/can_reenter_corpse var/bootime = FALSE var/started_as_observer //This variable is set to 1 when you enter the game as an observer. diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index 9afe767d2e9..c0e99de82c6 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -95,11 +95,11 @@ return message return "[verb], \"[message]\"" -/mob/proc/hear_say(list/message_pieces, verb = "says", italics = FALSE, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) +/mob/proc/hear_say(list/message_pieces, verb = "says", italics = FALSE, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE, is_whisper = FALSE) if(!client) return 0 - var/is_whisper = verb == "whispers" + if(isobserver(src) && client.prefs.toggles & PREFTOGGLE_CHAT_GHOSTEARS) if(speaker && !speaker.client && !(speaker in view(src))) diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm index 50f9f03aab0..698b828c46f 100644 --- a/code/modules/mob/language.dm +++ b/code/modules/mob/language.dm @@ -908,7 +908,8 @@ // Language handling. /mob/proc/add_language(language_name) - if(SEND_SIGNAL(src, COMSIG_MOB_LANGUAGE_ADD, language_name) & DISEASE_MOB_LANGUAGE_PROCESSED) + var/result_flags = SEND_SIGNAL(src, COMSIG_LANG_PRE_ACT, language_name) + if(SEND_SIGNAL(src, COMSIG_MOB_LANGUAGE_ADD, language_name, result_flags) & DISEASE_MOB_LANGUAGE_PROCESSED) return TRUE var/datum/language/new_language = GLOB.all_languages[language_name] @@ -926,7 +927,8 @@ /mob/proc/remove_language(language_name) - if(SEND_SIGNAL(src, COMSIG_MOB_LANGUAGE_REMOVE, language_name) & DISEASE_MOB_LANGUAGE_PROCESSED) + var/result_flags = SEND_SIGNAL(src, COMSIG_LANG_PRE_ACT, language_name) + if(SEND_SIGNAL(src, COMSIG_MOB_LANGUAGE_REMOVE, language_name, result_flags) & DISEASE_MOB_LANGUAGE_PROCESSED) return TRUE var/datum/language/rem_language = GLOB.all_languages[language_name] diff --git a/code/modules/mob/living/alpha.dm b/code/modules/mob/living/alpha.dm new file mode 100644 index 00000000000..d50b49746ac --- /dev/null +++ b/code/modules/mob/living/alpha.dm @@ -0,0 +1,35 @@ +/mob/living/proc/alpha_update() + var/result = 1 + for(var/source in alphas) + result *= alphas[source] + + alpha = LIGHTING_PLANE_ALPHA_VISIBLE * result + +/mob/living/proc/alpha_prepare(source) + if(!(source in alphas)) + alphas[source] = 1 + +/mob/living/proc/alpha_finalise(source) + alphas[source] = clamp(alphas[source], 0, 1) + if(alphas[source] == 1 && source != ALPHA_SOURCE_DEFAULT) + alphas.Remove(source) + + alpha_update() + +/mob/living/proc/alpha_add(val, source = ALPHA_SOURCE_DEFAULT) + alpha_prepare(source) + alphas[source] += val + alpha_finalise(source) + +/mob/living/proc/alpha_multiply(val, source = ALPHA_SOURCE_DEFAULT) + alpha_prepare(source) + alphas[source] *= val + alpha_finalise(source) + +/mob/living/proc/alpha_set(val, source = ALPHA_SOURCE_DEFAULT) + alpha_prepare(source) + alphas[source] = val + alpha_finalise(source) + +/mob/living/proc/alpha_get(source = ALPHA_SOURCE_DEFAULT) + return alphas[source] diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index dbfbb236453..d3fd0680bab 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -14,7 +14,12 @@ var/nightvision_enabled = FALSE nightvision = 4 - + + verb_say = "hisses" + verb_ask = "hisses curiously" + verb_exclaim = "roars" + verb_yell = "roars" + var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie var/has_fine_manipulation = FALSE var/move_delay_add = 0 // movement delay to add @@ -97,17 +102,12 @@ return GLOB.all_languages[LANGUAGE_XENOS] /mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "hisses" var/ending = copytext(message, length(message)) - + if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, - verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages + return speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages else - if(ending=="!") - verb = "roars" - else if(ending=="?") - verb = "hisses curiously" - return verb + return ..() /mob/living/carbon/alien/adjustToxLoss( diff --git a/code/modules/mob/living/carbon/alien/death.dm b/code/modules/mob/living/carbon/alien/death.dm index 777c180bd57..9d0605f5dde 100644 --- a/code/modules/mob/living/carbon/alien/death.dm +++ b/code/modules/mob/living/carbon/alien/death.dm @@ -16,7 +16,7 @@ flick("gibbed-a", animation) xgibs(loc) - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() QDEL_IN(animation, 15) QDEL_IN(src, 15) @@ -30,7 +30,7 @@ invisibility = INVISIBILITY_ABSTRACT dust_animation() new /obj/effect/decal/remains/xeno(loc) - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() QDEL_IN(src, 15) return TRUE @@ -42,7 +42,7 @@ animation.master = src flick("dust-a", animation) new /obj/effect/decal/remains/xeno(loc) - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() QDEL_IN(animation, 15) /mob/living/carbon/alien/death(gibbed) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 1026d476882..b34ba7137e1 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -5,6 +5,7 @@ max_grab = GRAB_KILL slowed_by_pull_and_push = FALSE butcher_results = list(/obj/item/reagent_containers/food/snacks/monstermeat/xenomeat= 5, /obj/item/stack/sheet/animalhide/xeno = 1) + hud_type = /datum/hud/alien var/obj/item/r_store = null var/obj/item/l_store = null var/caste = "" diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index 2ff4dc5b667..0e11a27fc79 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -18,6 +18,8 @@ death_message = "с тошнотворным шипением выдыха%(ет,ют)% воздух и пада%(ет,ют)% на пол..." death_sound = null + hud_type = /datum/hud/larva + var/datum/action/innate/hide/alien_larva/hide_action diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 7bf60ec50dc..1b92f72d71c 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -186,7 +186,7 @@ brainmob.container = null//Reset brainmob mmi var. brainmob.forceMove(held_brain) //Throw mob into brain. GLOB.respawnable_list += brainmob - GLOB.alive_mob_list -= brainmob//Get outta here + brainmob.remove_from_alive_mob_list()//Get outta here held_brain.brainmob = brainmob//Set the brain to use the brainmob held_brain.brainmob.cancel_camera() REMOVE_TRAIT(brainmob, TRAIT_NO_SPELLS, UNIQUE_TRAIT_SOURCE(src)) diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index f16f77bb016..d5417f35dca 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -39,6 +39,13 @@ CRASH("Brainmob without container.") forceMove(container) +/mob/living/carbon/brain/update_mouse_pointer() + if (!client) + return + client.mouse_pointer_icon = initial(client.mouse_pointer_icon) + if(!container) + return + /* This will return true if the brain has a container that leaves it less helpless than a naked brain diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm index 18ac00640da..6db3fa00b73 100644 --- a/code/modules/mob/living/carbon/brain/brain_item.dm +++ b/code/modules/mob/living/carbon/brain/brain_item.dm @@ -18,6 +18,8 @@ var/mmi_icon_state = "mmi_full" /// If it's a fake brain without a mob assigned that should still be treated like a real brain. var/decoy_brain = FALSE + /// TRUE giving to a user sci hud and active research scanner + var/smart_mind = FALSE /obj/item/organ/internal/brain/xeno name = "xenomorph brain" @@ -142,6 +144,7 @@ icon = 'icons/mob/slimes.dmi' icon_state = "green slime extract" mmi_icon_state = "slime_mmi" + parent_organ_zone = BODY_ZONE_CHEST /obj/item/organ/internal/brain/golem name = "Runic mind" diff --git a/code/modules/mob/living/carbon/brain/robotic_brain.dm b/code/modules/mob/living/carbon/brain/robotic_brain.dm index 6a5b1baf56e..a3f7db308b6 100644 --- a/code/modules/mob/living/carbon/brain/robotic_brain.dm +++ b/code/modules/mob/living/carbon/brain/robotic_brain.dm @@ -265,7 +265,7 @@ brainmob.dna.species = new /datum/species/machine() // Else it will default to human. And we don't want to clone IRC humans now do we? brainmob.dna.ResetSE() brainmob.dna.ResetUI() - GLOB.dead_mob_list -= brainmob + brainmob.remove_from_dead_mob_list() ..() /obj/item/mmi/robotic_brain/attack_ghost(mob/dead/observer/O) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index b0d89a6afa2..bbf221d5e01 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -28,7 +28,7 @@ if(stat == DEAD) return else - show_message("Блоб атакует!") + show_message(span_userdanger("Блоб атакует!")) adjustBruteLoss(10) @@ -396,16 +396,11 @@ else to_chat(src, span_warning("Ваши глаза начинают изрядно болеть. Это определенно не очень хорошо!")) - if(mind && has_bane(BANE_LIGHT)) - mind.disrupt_spells(-500) return TRUE else if(damage == 0) // just enough protection if(prob(20)) to_chat(src, span_notice("Что-то яркое вспыхнуло на периферии вашего зрения!")) - if(mind && has_bane(BANE_LIGHT)) - mind.disrupt_spells(0) - /mob/living/carbon/proc/create_dna() if(!dna) @@ -962,3 +957,10 @@ so that different stomachs can handle things in different ways VB*/ co2overloadtime = 0 + +/mob/living/carbon/check_smart_brain() + var/obj/item/organ/internal/brain/mobs_brain = get_organ_slot(INTERNAL_ORGAN_BRAIN) + if(mobs_brain?.smart_mind) + return TRUE + + return ..() diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm index 6ba77904998..74a5cc990e2 100644 --- a/code/modules/mob/living/carbon/human/body_accessories.dm +++ b/code/modules/mob/living/carbon/human/body_accessories.dm @@ -120,9 +120,24 @@ GLOBAL_LIST_INIT(body_accessory_by_species, list()) allowed_species = list(SPECIES_VULPKANIN) //Wryn -/datum/body_accessory/tail/wryn +/datum/body_accessory/tail/bee name = "Bee Tail" - icon_state = "wryntail" + icon_state = "beetail" + allowed_species = list(SPECIES_WRYN) + +/datum/body_accessory/tail/roach + name = "Cockroach Tail" + icon_state = "roachtail" + allowed_species = list(SPECIES_WRYN) + +/datum/body_accessory/tail/wasp + name = "Wasp Tail" + icon_state = "wasptail" + allowed_species = list(SPECIES_WRYN) + +/datum/body_accessory/tail/wasper + name = "Wasper Tail" + icon_state = "waspertail" allowed_species = list(SPECIES_WRYN) //Nian diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 6152cecfe17..98b121445d2 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -426,10 +426,6 @@ pose = addtext(pose,".") //Makes sure all emotes end with a period. msg += "\n[p_they(TRUE)] [p_are()] [pose]" - if(client && mind && !mind.offstation_role && user.mind?.special_role) // No ashwalkers, monkeys etc - var/permission_granted = client.prefs.toggles2 & PREFTOGGLE_2_GIB_WITHOUT_OBJECTIVE - msg += "\n
[span_info("Вы[permission_granted ? "" : " [span_warning("НЕ")]"] можете вывести этого игрока из игры не имея соответствующей цели.")]
" - . = list(msg) SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) @@ -477,6 +473,9 @@ if(CIH?.examine_extensions) have_hud_exam |= CIH.examine_extensions + if(H.check_smart_brain()) + have_hud_exam |= EXAMINE_HUD_SCIENCE + return (have_hud_exam & hud_exam) else if(isrobot(M) || isAI(M)) //Stand-in/Stopgap to prevent pAIs from freely altering records, pending a more advanced Records system diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 63f34de85cc..a9d6f326bd4 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -31,6 +31,7 @@ QDEL_LIST(bodyparts) SSmobs.cubemonkeys -= src GLOB.human_list -= src + SEND_SIGNAL(src, COMSIG_HUMAN_DESTROYED) return ..() @@ -335,7 +336,7 @@ if(stat == DEAD) return SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - show_message("The blob attacks you!") + show_message(span_userdanger("The blob attacks you!")) var/dam_zone = list( BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN, @@ -350,8 +351,7 @@ BODY_ZONE_PRECISE_R_FOOT, ) var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) - apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee")) - + apply_damage(5, BRUTE, affecting, run_armor_check(affecting, MELEE)) // Get rank from ID from hands, wear_id, pda, and then from uniform /mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") @@ -1775,46 +1775,6 @@ Eyes need to have significantly high darksight to shine unless the mob has the X if(LAZYIN(mind.curses, "high_rp")) // Probably need to make a new proc to handle curses in case if there will be new ones curse_high_rp() -/mob/living/carbon/human/proc/influenceSin() - if(!mind) - return - - var/datum/objective/sintouched/sin_objective - - switch(rand(1,7))//traditional seven deadly sins... except lust. - if(1) // acedia - add_game_logs("[src] was influenced by the sin of Acedia.", src) - sin_objective = new /datum/objective/sintouched/acedia - if(2) // Gluttony - add_game_logs("[src] was influenced by the sin of gluttony.", src) - sin_objective = new /datum/objective/sintouched/gluttony - if(3) // Greed - add_game_logs("[src] was influenced by the sin of greed.", src) - sin_objective = new /datum/objective/sintouched/greed - if(4) // sloth - add_game_logs("[src] was influenced by the sin of sloth.", src) - sin_objective = new /datum/objective/sintouched/sloth - if(5) // Wrath - add_game_logs("[src] was influenced by the sin of wrath.", src) - sin_objective = new /datum/objective/sintouched/wrath - if(6) // Envy - add_game_logs("[src] was influenced by the sin of envy.", src) - sin_objective = new /datum/objective/sintouched/envy - if(7) // Pride - add_game_logs("[src] was influenced by the sin of pride.", src) - sin_objective = new /datum/objective/sintouched/pride - - sin_objective.init_sin(src) - LAZYADD(SSticker.mode.sintouched, mind) - LAZYADD(mind.objectives, sin_objective) - - var/obj_count = 1 - to_chat(src, span_notice("Your current objectives:")) - - for(var/datum/objective/objective in mind.objectives) - to_chat(src, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - /mob/living/carbon/human/is_literate() return getBrainLoss() < 100 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 7f463abfa0c..24a0cf757e6 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -510,24 +510,23 @@ emp_act if(weapon_sharp && prob(getarmor(user.zone_selected, MELEE))) weapon_sharp = FALSE - var/cached_force = I.force * check_weakness(I, user) // this can destroy some species (damn nucleo-bombers), so from now on we cannot count on its existance - var/apply_damage_result = apply_damage(cached_force, I.damtype, affecting, armor, weapon_sharp, I) + var/apply_damage_result = apply_damage(I.force, I.damtype, affecting, armor, weapon_sharp, I) var/IM_ALIVE = !QDELETED(src) var/list/all_objectives = user.mind?.get_all_objectives() if(all_objectives) for(var/datum/objective/pain_hunter/objective in all_objectives) if(mind == objective.target) - objective.take_damage(cached_force, I.damtype) + objective.take_damage(I.force, I.damtype) if(!IM_ALIVE) return . var/bloody = FALSE - if(apply_damage_result && I.damtype == BRUTE && prob(25 + cached_force * 2)) + if(apply_damage_result && I.damtype == BRUTE && prob(25 + I.force * 2)) I.add_mob_blood(src) //Make the weapon bloody, not the person. - if(prob(cached_force * 2)) //blood spatter! + if(prob(I.force * 2)) //blood spatter! bloody = TRUE add_splatter_floor() if(get_dist(user, src) <= 1) //people with TK won't get smeared with blood @@ -536,14 +535,14 @@ emp_act switch(hit_area) if(BODY_ZONE_HEAD)//Harder to score a stun but if you do it lasts a bit longer if(apply_damage_result && stat == CONSCIOUS && armor < 50) - if(prob(cached_force)) + if(prob(I.force)) visible_message( span_combatdanger("[src] has been knocked down!"), span_combatuserdanger("[src] has been knocked down!"), ) apply_effect(4 SECONDS, KNOCKDOWN, armor) AdjustConfused(30 SECONDS) - if(mind?.special_role == SPECIAL_ROLE_REV && prob(cached_force + ((100 - health)/2)) && src != user && I.damtype == BRUTE) + if(mind?.special_role == SPECIAL_ROLE_REV && prob(I.force + ((100 - health)/2)) && src != user && I.damtype == BRUTE) SSticker.mode.remove_revolutionary(mind) if(bloody)//Apply blood @@ -558,7 +557,7 @@ emp_act update_inv_glasses() if(BODY_ZONE_CHEST)//Easier to score a stun but lasts less time - if(apply_damage_result && stat == CONSCIOUS && prob(cached_force + 10)) + if(apply_damage_result && stat == CONSCIOUS && prob(I.force + 10)) visible_message( span_combatdanger("[src] has been knocked down!"), span_combatuserdanger("[src] has been knocked down!"), @@ -573,7 +572,7 @@ emp_act w_uniform.add_mob_blood(src) update_inv_w_uniform() - if(apply_damage_result && (cached_force > 10 || (cached_force >= 5 && prob(33)))) + if(apply_damage_result && (I.force > 10 || (I.force >= 5 && prob(33)))) forcesay(GLOB.hit_appends) //forcesay checks stat already . |= dna.species.spec_proceed_attack_results(I, src, user, affecting) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index b4059f4a60f..040145b9b64 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -15,6 +15,7 @@ num_hands = 0 //Populated on init through list/bodyparts usable_hands = 0 //Populated on init through list/bodyparts status_flags = parent_type::status_flags|CANSTAMCRIT + hud_type = /datum/hud/human //Marking colour and style var/list/m_colours = DEFAULT_MARKING_COLOURS //All colours set to #000000. var/list/m_styles = DEFAULT_MARKING_STYLES //All markings set to None. diff --git a/code/modules/mob/living/carbon/human/human_emote.dm b/code/modules/mob/living/carbon/human/human_emote.dm index 4475de8d31b..d28d47e79f8 100644 --- a/code/modules/mob/living/carbon/human/human_emote.dm +++ b/code/modules/mob/living/carbon/human/human_emote.dm @@ -527,6 +527,7 @@ message_param = EMOTE_PARAM_USE_POSTFIX emote_type = EMOTE_AUDIBLE vary = TRUE + only_unintentional = TRUE audio_cooldown = 1 MINUTES cooldown = 10 SECONDS species_type_blacklist_typecache = list(/datum/species/machine) diff --git a/code/modules/mob/living/carbon/human/human_interaction.dm b/code/modules/mob/living/carbon/human/human_interaction.dm index f731c0d8912..4a858d8abf8 100644 --- a/code/modules/mob/living/carbon/human/human_interaction.dm +++ b/code/modules/mob/living/carbon/human/human_interaction.dm @@ -1,241 +1,225 @@ /mob/living/carbon/human/Topic(href, href_list) ///////Interactions!!/////// - if(href_list["interaction"]) - if(usr.incapacitated() || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) - return - - //CONDITIONS - var/mob/living/carbon/human/H = usr - var/mob/living/carbon/human/P = H.partner - if (!(P in view(H.loc))) - return - var/obj/item/organ/external/temp = H.bodyparts_by_name[BODY_ZONE_PRECISE_R_HAND] - var/hashands = (temp?.is_usable()) - if (!hashands) - temp = H.bodyparts_by_name[BODY_ZONE_PRECISE_L_HAND] - hashands = (temp?.is_usable()) - temp = P.bodyparts_by_name[BODY_ZONE_PRECISE_R_HAND] - var/hashands_p = (temp?.is_usable()) - if (!hashands_p) - temp = P.bodyparts_by_name[BODY_ZONE_PRECISE_L_HAND] - hashands = (temp?.is_usable()) - var/mouthfree = !((H.head && (H.head.flags_cover & HEADCOVERSMOUTH)) || (H.wear_mask && (H.wear_mask.flags_cover & MASKCOVERSMOUTH))) - var/mouthfree_p = !((P.head && (P.head.flags_cover & HEADCOVERSMOUTH)) || (P.wear_mask && (P.wear_mask.flags_cover & MASKCOVERSMOUTH))) - - if(world.time <= H.last_interract + 1 SECONDS) - return - else - H.last_interract = world.time - - if (href_list["interaction"] == "bow") - H.custom_emote(message = "кланя[pluralize_ru(H.gender,"ет","ют")]ся [P].") - if (istype(P.loc, /obj/structure/closet) && P.loc == H.loc) - P.custom_emote(message = "кланя[pluralize_ru(H.gender,"ет","ют")]ся [P].") - - else if (href_list["interaction"] == "pet") - if(((!istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands && H.Adjacent(P)) - H.custom_emote(message = "[pick("глад[pluralize_ru(H.gender,"ит","ят")]", "поглажива[pluralize_ru(H.gender,"ет","ют")]")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "[pick("глад[pluralize_ru(H.gender,"ит","ят")]", "поглажива[pluralize_ru(H.gender,"ет","ют")]")] [P].") - - else if (href_list["interaction"] == "scratch") - if(((!istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands && H.Adjacent(P)) - if(H.zone_selected == BODY_ZONE_HEAD && !((P.dna.species.name == SPECIES_MACNINEPERSON) || (P.dna.species.name == SPECIES_GREY) || (P.dna.species.name == SPECIES_UNATHI))) - H.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender,"ет","ут")] за ухом", "чеш[pluralize_ru(H.gender,"ет","ут")] голову")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender,"ет","ут")] за ухом", "чеш[pluralize_ru(H.gender,"ет","ут")] голову")] [P].") - else - H.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender,"ет","ут")]")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender,"ет","ут")]")] [P].") - - else if (href_list["interaction"] == "give") - if(H.Adjacent(P)) - if (((!istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - H.give(P) - - else if (href_list["interaction"] == "kiss") - if( ((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc))) - H.custom_emote(message = "целу[pluralize_ru(H.gender,"ет","ют")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "целу[pluralize_ru(H.gender,"ет","ют")] [P].") - else if (mouthfree) - H.custom_emote(message = "посыла[pluralize_ru(H.gender,"ет","ют")] [P] воздушный поцелуй.") - - else if (href_list["interaction"] == "lick") - if( ((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && mouthfree && mouthfree_p) - if (prob(90)) - H.custom_emote(message = "лизнул[genderize_ru(H.gender,"","а","о","и")] [P] в щеку.") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "лизнул[genderize_ru(H.gender,"","а","о","и")] [P] в щеку.") - else - H.custom_emote(message = "особо тщательно лизнул[genderize_ru(H.gender,"","а","о","и")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "особо тщательно лизнул[genderize_ru(H.gender,"","а","о","и")] [P].") - - else if (href_list["interaction"] == "hug") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - H.custom_emote(message = "обнима[pluralize_ru(H.gender,"ет","ют")] [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "обнима[pluralize_ru(H.gender,"ет","ют")] [P].") - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - - else if (href_list["interaction"] == "cheer") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - H.custom_emote(message = "похлопыва[pluralize_ru(H.gender,"ет","ют")] [P] по плечу.") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "похлопыва[pluralize_ru(H.gender,"ет","ют")] [P] по плечу.") - - else if (href_list["interaction"] == "five") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - H.custom_emote(message = "да[pluralize_ru(H.gender,"ёт","ют")] [P] пять.") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "да[pluralize_ru(H.gender,"ёт","ют")] [P] пять.") - playsound(loc, 'sound/effects/snap.ogg', 25, 1, -1) - - else if (href_list["interaction"] == "handshake") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands && hashands_p) - H.custom_emote(message = "жм[pluralize_ru(H.gender,"ёт","ут")] руку [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "жм[pluralize_ru(H.gender,"ёт","ут")] руку [P].") - - else if (href_list["interaction"] == "bow_affably") - H.custom_emote(message = "приветливо кивнул[genderize_ru(H.gender,"","а","о","и")] в сторону [P].") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "приветливо кивнул[genderize_ru(H.gender,"","а","о","и")] в сторону [P].") - - else if (href_list["interaction"] == "wave") - if (!(H.Adjacent(P)) && hashands) - H.custom_emote(message = "приветливо маш[pluralize_ru(H.gender,"ет","ут")] в сторону [P].") + if(!href_list["interaction"]) + return ..() + + if(usr.incapacitated() || HAS_TRAIT(usr, TRAIT_HANDS_BLOCKED)) + return + + //CONDITIONS + var/mob/living/carbon/human/H = usr + var/mob/living/carbon/human/P = H.partner + if(!(P in view(H.loc))) + return + + if(world.time <= H.last_interract + 1 SECONDS) + return + + H.last_interract = world.time + + switch(href_list["interaction"]) + if("bow") + H.custom_emote(message = "кланя[pluralize_ru(H.gender, "ет", "ют")]ся [P].") + + if("pet") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + H.custom_emote(message = "[pick("глад[pluralize_ru(H.gender, "ит", "ят")]", "поглажива[pluralize_ru(H.gender, "ет", "ют")]")] [P].") + + if("scratch") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + if(H.zone_selected != BODY_ZONE_HEAD || ismachineperson(P) || isunathi(P) || isgrey(P)) + H.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender, "ет", "ут")]")] [P].") + else - H.custom_emote(message = "приветливо маш[pluralize_ru(H.gender,"ет","ут")] в сторону [P].") - - - else if (href_list["interaction"] == "slap") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - switch(H.zone_selected) - if(BODY_ZONE_HEAD) - H.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] пощечину!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] пощечину!") - playsound(loc, 'sound/effects/snap.ogg', 50, 1, -1) - var/obj/item/organ/external/head/head = P.get_organ(BODY_ZONE_HEAD) - if(head?.brute_dam < 5) - P.apply_damage(1, def_zone = head) - H.do_attack_animation(P) - - if(BODY_ZONE_PRECISE_GROIN) - H.custom_emote(message = "шлёпа[pluralize_ru(H.gender,"ет","ют")] [P] по заднице!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "шлёпа[pluralize_ru(H.gender,"ет","ют")] [P] по заднице!") - playsound(loc, 'sound/effects/snap.ogg', 50, 1, -1) - var/obj/item/organ/external/groin/groin = P.get_organ(BODY_ZONE_PRECISE_GROIN) - if(groin?.brute_dam < 5) - P.apply_damage(1, def_zone = groin) - H.do_attack_animation(P) - - if(BODY_ZONE_PRECISE_MOUTH) - H.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] по губе!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] по губе!") - playsound(loc, 'sound/effects/snap.ogg', 50, 1, -1) - H.do_attack_animation(P) - - else if (href_list["interaction"] == "fuckyou") - if(hashands) - H.custom_emote(message = "показыва[pluralize_ru(H.gender,"ет","ют")] [P] средний палец!") - if (istype(P.loc, /obj/structure/closet) && P.loc == H.loc) - P.custom_emote(message = "показыва[pluralize_ru(H.gender,"ет","ют")] [P] средний палец!") - - else if (href_list["interaction"] == "knock") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands) - H.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] подзатыльник!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "да[pluralize_ru(H.gender,"ет","ют")] [P] подзатыльник!") - playsound(loc, 'sound/weapons/throwtap.ogg', 50, 1, -1) - var/obj/item/organ/external/head/head = P.get_organ(BODY_ZONE_HEAD) - if(head?.brute_dam < 3) - P.apply_damage(1, def_zone = head) - H.do_attack_animation(P) - - else if (href_list["interaction"] == "spit") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && mouthfree) - H.custom_emote(message = "плю[pluralize_ru(H.gender,"ёт","ют")] в [P]!") - if(prob(20)) - P.AdjustEyeBlurry(3 SECONDS) - if(istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "плю[pluralize_ru(H.gender,"ёт","ют")] в [P]!") - - else if (href_list["interaction"] == "threaten") - if(hashands) - H.custom_emote(message = "гроз[pluralize_ru(H.gender,"ит","ят")] [P] кулаком!") - if (istype(P.loc, /obj/structure/closet) && H.loc == P.loc) - P.custom_emote(message = "гроз[pluralize_ru(H.gender,"ит","ят")] [P] кулаком!") - - else if (href_list["interaction"] == "tongue") - if(mouthfree) - H.custom_emote(message = "показыва[pluralize_ru(H.gender,"ет","ют")] [P] язык!") - if (istype(P.loc, /obj/structure/closet) && H.loc == P.loc) - P.custom_emote(message = "показыва[pluralize_ru(H.gender,"ет","ют")] [P] язык!") - - else if (href_list["interaction"] == "pullwing") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands && !HAS_TRAIT(H, TRAIT_HANDS_BLOCKED)) - if(!P.bodyparts_by_name[BODY_ZONE_WING]) - H.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за крылья КОТОРЫХ НЕТ!!!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за крылья КОТОРЫХ НЕТ!!!") - return - if (prob(30)) - var/obj/item/organ/external/wing/wing = P.get_organ(BODY_ZONE_WING) - if ((wing.brute_dam == wing.max_damage || wing.is_dead() || wing.has_fracture()) && prob(20)) - H.custom_emote(message = "отрыва[pluralize_ru(H.gender,"ет","ют")] [P] крылья!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "отрыва[pluralize_ru(H.gender,"ет","ют")] [P] крылья!") - wing.droplimb() - return - H.custom_emote(message = "дёрга[pluralize_ru(H.gender,"ет","ют")] [P] за крылья!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "дёрга[pluralize_ru(H.gender,"ет","ют")] [P] за крылья!") - if(wing.brute_dam < 10) - P.apply_damage(1, def_zone = wing) + H.custom_emote(message = "[pick("чеш[pluralize_ru(H.gender, "ет", "ут")] за ухом", "чеш[pluralize_ru(H.gender, "ет", "ут")] голову")] [P].") + + if("give") + if(!P.Adjacent(H.loc)) + return + + H.give(P) + + if("kiss") + if(!get_location_accessible(H, BODY_ZONE_PRECISE_MOUTH)) + return + + if(!P.Adjacent(H.loc)) + H.custom_emote(message = "посыла[pluralize_ru(H.gender, "ет", "ют")] [P] воздушный поцелуй.") + + else if(get_location_accessible(P, BODY_ZONE_PRECISE_MOUTH)) + H.custom_emote(message = "целу[pluralize_ru(H.gender, "ет", "ют")] [P].") + + if("lick") + if(!P.Adjacent(H.loc) || !get_location_accessible(H, BODY_ZONE_PRECISE_MOUTH) || !get_location_accessible(P, BODY_ZONE_PRECISE_MOUTH)) + return + + if(prob(90)) + H.custom_emote(message = "лизнул[genderize_ru(H.gender, "", "а", "о", "и")] [P] в щеку.") + + else + H.custom_emote(message = "особо тщательно лизнул[genderize_ru(H.gender, "", "а", "о", "и")] [P].") + + if("hug") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + H.custom_emote(message = "обнима[pluralize_ru(H.gender, "ет", "ют")] [P].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) + + if("cheer") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + H.custom_emote(message = "похлопыва[pluralize_ru(H.gender, "ет", "ют")] [P] по плечу.") + + if("five") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + H.custom_emote(message = "да[pluralize_ru(H.gender, "ёт", "ют")] [P] пять.") + playsound(loc, 'sound/effects/snap.ogg', 25, TRUE, -1) + + if("handshake") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || HAS_TRAIT(P, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + H.custom_emote(message = "жм[pluralize_ru(H.gender, "ёт", "ут")] руку [P].") + + if("bow_affably") + H.custom_emote(message = "приветливо кивнул[genderize_ru(H.gender, "", "а", "о", "и")] в сторону [P].") + + if("wave") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED)) + return + + H.custom_emote(message = "приветливо маш[pluralize_ru(H.gender, "ет", "ут")] в сторону [P].") + + if("slap") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + var/obj/item/organ/external/targeted_organ = P.get_organ(H.zone_selected) + if(!targeted_organ) + return + + switch(H.zone_selected) + if(BODY_ZONE_HEAD) + H.custom_emote(message = span_danger("да[pluralize_ru(H.gender, "ет", "ют")] [P] пощечину!")) + + if(BODY_ZONE_PRECISE_GROIN) + H.custom_emote(message = span_danger("шлёпа[pluralize_ru(H.gender, "ет", "ют")] [P] по заднице!")) + + if(BODY_ZONE_PRECISE_MOUTH) + H.custom_emote(message = span_danger("да[pluralize_ru(H.gender, "ет", "ют")] [P] по губе!")) + else - H.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за крылья!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за крылья!") - - else if (href_list["interaction"] == "pull") - if(((H.Adjacent(P) && !istype(P.loc, /obj/structure/closet)) || (H.loc == P.loc)) && hashands && !HAS_TRAIT(H, TRAIT_HANDS_BLOCKED)) - if(!P.bodyparts_by_name[BODY_ZONE_TAIL]) - H.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за хвост КОТОРОГО НЕТ!!!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за хвост КОТОРОГО НЕТ!!!") return - var/obj/item/organ/internal/cyberimp/tail/blade/implant = P.get_organ_slot(INTERNAL_ORGAN_TAIL_DEVICE) - if(istype(implant) && implant.activated) // KEEP YOUR HANDS AWAY FROM ME! - H.custom_emote(message = span_danger("пыта[pluralize_ru(H.gender,"ет","ют")]ся дёрнуть [P] за хвост, но резко одёргива[pluralize_ru(H.gender,"ет","ют")] руки!")) - if(H.has_pain()) - H.emote("scream") - H.apply_damage(5, implant.damage_type, BODY_ZONE_PRECISE_R_HAND) - H.apply_damage(5, implant.damage_type, BODY_ZONE_PRECISE_L_HAND) - return + if(targeted_organ.brute_dam < 5) + P.apply_damage(1, def_zone = targeted_organ) - if (prob(30)) - var/obj/item/organ/external/tail/tail = P.get_organ(BODY_ZONE_TAIL) - if ((tail.brute_dam == tail.max_damage || tail.is_dead() || tail.has_fracture()) && prob(20)) - H.custom_emote(message = "отрыва[pluralize_ru(H.gender,"ет","ют")] [P] хвост!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "отрыва[pluralize_ru(H.gender,"ет","ют")] [P] хвост!") - tail.droplimb() - return - H.custom_emote(message = "дёрга[pluralize_ru(H.gender,"ет","ют")] [P] за хвост!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "дёрга[pluralize_ru(H.gender,"ет","ют")] [P] за хвост!") - if(tail.brute_dam < 10) - P.apply_damage(1, def_zone = tail) - else - H.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за хвост!") - if (istype(P.loc, /obj/structure/closet)) - P.custom_emote(message = "пыта[pluralize_ru(H.gender,"ет","ют")]ся поймать [P] за хвост!") - return - ..() + playsound(loc, 'sound/effects/snap.ogg', 50, TRUE, -1) + H.do_attack_animation(P) + + + if("fuckyou") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED)) + return + + H.custom_emote(message = span_danger("показыва[pluralize_ru(H.gender, "ет", "ют")] [P] средний палец!")) + + if("knock") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + var/obj/item/organ/external/head/head = P.get_organ(BODY_ZONE_HEAD) + if(!head) + return + + if(head.brute_dam < 5) + P.apply_damage(1, def_zone = head) + + H.custom_emote(message = span_danger("да[pluralize_ru(H.gender, "ет", "ют")] [P] подзатыльник!")) + playsound(loc, 'sound/weapons/throwtap.ogg', 50, TRUE, -1) + H.do_attack_animation(P) + + if("spit") + if(!P.Adjacent(H.loc) || !get_location_accessible(H, BODY_ZONE_PRECISE_MOUTH)) + return + + H.custom_emote(message = span_danger("плю[pluralize_ru(H.gender, "ёт", "ют")] в [P]!")) + + if(prob(20)) + P.AdjustEyeBlurry(3 SECONDS) + + if("threaten") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED)) + return + + H.custom_emote(message = span_danger("гроз[pluralize_ru(H.gender, "ит", "ят")] [P] кулаком!")) + + if("tongue") + if(!get_location_accessible(H, BODY_ZONE_PRECISE_MOUTH)) + return + + H.custom_emote(message = span_danger("показыва[pluralize_ru(H.gender, "ет", "ют")] [P] язык!")) + + if("pullwing") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + var/obj/item/organ/external/wing/wing = P.get_organ(BODY_ZONE_WING) + if(!wing) + H.custom_emote(message = "пыта[pluralize_ru(H.gender, "ет", "ют")]ся поймать [P] за крылья, [span_danger("КОТОРЫХ НЕТ!!!")]") + return + + if(!prob(30)) + H.custom_emote(message = "пыта[pluralize_ru(H.gender, "ет", "ют")]ся поймать [P] за крылья!") + return + + if((wing.is_dead() || wing.has_fracture()) && prob(20)) + H.custom_emote(message = span_danger("отрыва[pluralize_ru(H.gender, "ет", "ют")] [P] крылья!")) + wing.droplimb() + return + + if(wing.brute_dam < 10) + P.apply_damage(1, def_zone = wing) + + H.custom_emote(message = span_danger("дёрга[pluralize_ru(H.gender, "ет", "ют")] [P] за крылья!")) + + if("pull") + if(HAS_TRAIT(H, TRAIT_HANDS_BLOCKED) || !P.Adjacent(H.loc)) + return + + var/obj/item/organ/external/tail/tail = P.get_organ(BODY_ZONE_TAIL) + if(!tail) + H.custom_emote(message = "пыта[pluralize_ru(H.gender, "ет", "ют")]ся поймать [P] за хвост, [span_danger("КОТОРОГО НЕТ!!!")]") + return + + var/obj/item/organ/internal/cyberimp/tail/blade/implant = P.get_organ_slot(INTERNAL_ORGAN_TAIL_DEVICE) + if(istype(implant) && implant.activated) // KEEP YOUR HANDS AWAY FROM ME! + if(H.has_pain()) + H.emote("scream") + + H.custom_emote(message = span_danger("пыта[pluralize_ru(H.gender, "ет", "ют")]ся дёрнуть [P] за хвост, но резко одёргива[pluralize_ru(H.gender, "ет", "ют")] руки!")) + H.apply_damage(5, implant.damage_type, BODY_ZONE_PRECISE_R_HAND) + H.apply_damage(5, implant.damage_type, BODY_ZONE_PRECISE_L_HAND) + return + + if(prob(70)) + H.custom_emote(message = "пыта[pluralize_ru(H.gender, "ет", "ют")]ся поймать [P] за хвост!") + return + + if((tail.is_dead() || tail.has_fracture()) && prob(20)) + H.custom_emote(message = span_danger("отрыва[pluralize_ru(H.gender, "ет", "ют")] [P] хвост!")) + tail.droplimb() + return + + if(tail.brute_dam < 10) + P.apply_damage(1, def_zone = tail) + + H.custom_emote(message = span_danger("дёрга[pluralize_ru(H.gender, "ет", "ют")] [P] за хвост!")) diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 62a0c337ef5..b7670dfae03 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,3 +1,9 @@ +#define PULL_STAMINADAM_WALK 4 +#define PULL_STAMINADAM_RUN 6 +#define PUSH_STAMINADAM_WALK 3 +#define PUSH_STAMINADAM_RUN 4 + + /mob/living/carbon/human/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() if(!forced && (!old_loc || !old_loc.has_gravity()) && has_gravity()) @@ -29,6 +35,7 @@ if(.) // did we actually move? if(body_position != LYING_DOWN && !buckled && !throwing) update_splints() + var/break_bones_chance = get_bones_symptom_prob() if(break_bones_chance && (m_intent == MOVE_INTENT_RUN || pulling)) if(prob(break_bones_chance)) @@ -42,6 +49,40 @@ else if(prob(30)) playsound(src, "bonebreak", 10, TRUE) + // If we sooo weak to pull or push something, except items or tiny mobs, get stamina damage + var/weak_mob = FALSE + if((pulling || now_pushing) && (HAS_TRAIT(src, TRAIT_WEAK_PULLING))) + weak_mob = TRUE + + if(weak_mob) + var/stamina_damage = 0 + var/small_pulled = FALSE + // Handle pulling all non /obj/item stuff or tiny mobs + if(pulling && isliving(pulling)) + var/mob/living/pulled_mob = pulling + if(!pulled_mob.mob_size) // small or bigger mobs + small_pulled = TRUE + + if(pulling && !(small_pulled || isitem(pulling))) + if(m_intent == MOVE_INTENT_WALK) + stamina_damage += PULL_STAMINADAM_WALK + else + stamina_damage += PULL_STAMINADAM_RUN + + if(staminaloss > 69) + balloon_alert(src, "слишком тяжело тащить!") + stop_pulling() + + // Handle pushing, NOT swapping sides with mobs in help intent + if(now_pushing) + if(!(isliving(now_pushing) && a_intent == INTENT_HELP)) + if(m_intent == MOVE_INTENT_WALK) + stamina_damage += PUSH_STAMINADAM_WALK + else + stamina_damage += PUSH_STAMINADAM_RUN + + apply_damage(stamina_damage, STAMINA) + if(!has_gravity()) return . @@ -202,3 +243,9 @@ return FALSE return ..() + + +#undef PULL_STAMINADAM_WALK +#undef PULL_STAMINADAM_RUN +#undef PUSH_STAMINADAM_WALK +#undef PUSH_STAMINADAM_RUN diff --git a/code/modules/mob/living/carbon/human/human_say.dm b/code/modules/mob/living/carbon/human/human_say.dm index bcc9abbb6b4..c6ed4bc625d 100644 --- a/code/modules/mob/living/carbon/human/human_say.dm +++ b/code/modules/mob/living/carbon/human/human_say.dm @@ -85,18 +85,25 @@ /mob/living/carbon/human/IsVocal() - var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate() in internal_organs - if(translator?.active) - return TRUE + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(translator?.active && !mind?.miming) + return TRUE // Cyberimps don't care if you need to breathe at all, but make some respect to mimes + if(HAS_TRAIT(src, TRAIT_MUTE)) return FALSE + + if(TRAIT_NO_VOCAL_CORDS in dna?.species.inherent_traits) + return FALSE + // how do species that don't breathe talk? magic, that's what. var/breathes = !HAS_TRAIT(src, TRAIT_NO_BREATH) var/obj/item/organ/internal/lungs = get_organ_slot(INTERNAL_ORGAN_LUNGS) if((breathes && !lungs) || (breathes && lungs && lungs.is_dead())) return FALSE + if(mind) return !mind.miming + return TRUE /mob/living/carbon/human/cannot_speak_loudly() @@ -131,21 +138,22 @@ /mob/living/carbon/human/handle_speech_problems(list/message_pieces, verb) var/span = "" + var/check_mute = TRUE + var/check_wingdings = TRUE - var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate() in internal_organs - if(translator?.active && !HAS_TRAIT(src, TRAIT_MUTE)) - span = translator.speech_span - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = "[S.message]" - verb = translator.speech_verb - return list("verb" = verb) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(translator?.active) // Yes, we can speak even muted, unless being EMPed + check_mute = FALSE + + if(translator.can_wingdings) // Active wingdings chip allowed us to speak normally + check_wingdings = FALSE if(HAS_TRAIT(src, TRAIT_COMIC) \ || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \ || HAS_TRAIT(src, TRAIT_JESTER)) span = "sans" - if(HAS_TRAIT(src, TRAIT_WINGDINGS)) + if(check_wingdings && HAS_TRAIT(src, TRAIT_WINGDINGS)) span = "wingdings" var/list/parent = ..() @@ -155,8 +163,9 @@ if(S.speaking?.flags & NO_STUTTER) continue - if(HAS_TRAIT(src, TRAIT_MUTE)) + if(check_mute && (HAS_TRAIT(src, TRAIT_MUTE))) S.message = "" + continue if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) var/obj/item/clothing/mask/horsehead/hoers = wear_mask @@ -166,13 +175,21 @@ if(dna) for(var/datum/dna/gene/gene as anything in GLOB.dna_genes) if(gene.is_active(src)) + if(!check_wingdings && istype(gene, /datum/dna/gene/disability/wingdings)) + continue + S.message = gene.OnSay(src, S.message) + if(check_mute && (TRAIT_NO_VOCAL_CORDS in dna.species.inherent_traits)) // Species neither have vocal cords nor translator + S.message = "" + continue + var/braindam = getBrainLoss() if(braindam >= 60) if(prob(braindam / 4)) S.message = stutter(S.message) verb = "gibbers" + if(prob(braindam)) S.message = uppertext(S.message) verb = "yells loudly" diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 3f08ff83a94..d625200111c 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -291,8 +291,10 @@ if(!environment) return + SEND_SIGNAL(src, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT, environment) + var/loc_temp = get_temperature(environment) -// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]") +// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_main_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]") //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. if(stat != DEAD) @@ -415,20 +417,22 @@ . = ..() if(!. || HAS_TRAIT(src, TRAIT_RESIST_HEAT)) return - var/thermal_protection = get_thermal_protection() + var/thermal_protection_main = get_main_thermal_protection() + var/thermal_protection_secondary = get_secondary_thermal_protection() - if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) + if(thermal_protection_main >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) return - if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT) - adjust_bodytemperature(11) + + if(thermal_protection_main >= FIRE_SUIT_MAX_TEMP_PROTECT) + adjust_bodytemperature(11 * (1 - thermal_protection_secondary)) else - adjust_bodytemperature(BODYTEMP_HEATING_MAX + (fire_stacks * 12)) + adjust_bodytemperature((BODYTEMP_HEATING_MAX + (fire_stacks * 12)) * (1 - thermal_protection_secondary)) var/datum/antagonist/vampire/vamp = mind?.has_antag_datum(/datum/antagonist/vampire) if(vamp && !vamp.get_ability(/datum/vampire_passive/full) && stat != DEAD) vamp.bloodusable = max(vamp.bloodusable - 5, 0) -/mob/living/carbon/human/proc/get_thermal_protection() +/mob/living/carbon/human/proc/get_main_thermal_protection() if(HAS_TRAIT(src, TRAIT_RESIST_HEAT)) return FIRE_IMMUNITY_MAX_TEMP_PROTECT @@ -442,6 +446,25 @@ thermal_protection = round(thermal_protection) return thermal_protection +/mob/living/carbon/human/proc/get_secondary_thermal_protection() + var/result = 0 + + result += getarmor(BODY_ZONE_HEAD, FIRE) / 100 * THERMAL_PROTECTION_HEAD + result += getarmor(BODY_ZONE_CHEST, FIRE) / 100 * THERMAL_PROTECTION_UPPER_TORSO + result += getarmor(BODY_ZONE_PRECISE_GROIN, FIRE) / 100 * THERMAL_PROTECTION_LOWER_TORSO + + result += getarmor(BODY_ZONE_L_ARM, FIRE) / 100 * THERMAL_PROTECTION_ARM_LEFT + result += getarmor(BODY_ZONE_PRECISE_L_HAND, FIRE) / 100 * THERMAL_PROTECTION_HAND_LEFT + result += getarmor(BODY_ZONE_R_ARM, FIRE) / 100 * THERMAL_PROTECTION_ARM_RIGHT + result += getarmor(BODY_ZONE_PRECISE_R_HAND, FIRE) / 100 * THERMAL_PROTECTION_HAND_RIGHT + + result += getarmor(BODY_ZONE_L_LEG, FIRE) / 100 * THERMAL_PROTECTION_LEG_LEFT + result += getarmor(BODY_ZONE_PRECISE_L_FOOT, FIRE) / 100 * THERMAL_PROTECTION_FOOT_LEFT + result += getarmor(BODY_ZONE_R_LEG, FIRE) / 100 * THERMAL_PROTECTION_LEG_RIGHT + result += getarmor(BODY_ZONE_PRECISE_R_FOOT, FIRE) / 100 * THERMAL_PROTECTION_FOOT_RIGHT + + return result + //END FIRE CODE /mob/living/carbon/human/proc/body_thermal_regulation(loc_temp) @@ -767,7 +790,8 @@ if(dna.species.update_health_hud()) return else - + if(SEND_SIGNAL(src, COMSIG_HUMAN_UPDATING_HEALTH_HUD, health) & COMPONENT_OVERRIDE_HEALTH_HUD) + return var/shock_reduction = 0 if(HAS_TRAIT(src, TRAIT_NO_PAIN_HUD)) shock_reduction = INFINITY diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index 70d7d6b7c41..1ed47e2c5ef 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -272,6 +272,8 @@ var/datum/language/species_language = GLOB.all_languages[language] return species_language.get_random_name(gender) +/datum/species/proc/is_allowed_hair_style(mob/living/carbon/human/human, datum/robolimb/robohead, datum/sprite_accessory/style) + return TRUE /proc/get_age_limits(datum/species/species, list/tags) if(!islist(tags)) diff --git a/code/modules/mob/living/carbon/human/species/diona.dm b/code/modules/mob/living/carbon/human/species/diona.dm index 12c74470e67..a17a546af1c 100644 --- a/code/modules/mob/living/carbon/human/species/diona.dm +++ b/code/modules/mob/living/carbon/human/species/diona.dm @@ -148,7 +148,7 @@ if(update) H.updatehealth() if(H.blood_volume < BLOOD_VOLUME_NORMAL) - H.blood_volume += 0.5 + H.AdjustBlood(0.5) if(!is_vamp && H.nutrition < NUTRITION_LEVEL_STARVING + 50) H.adjustBruteLoss(2) diff --git a/code/modules/mob/living/carbon/human/species/drask.dm b/code/modules/mob/living/carbon/human/species/drask.dm index 2d2b25dc886..04b47fd6e14 100644 --- a/code/modules/mob/living/carbon/human/species/drask.dm +++ b/code/modules/mob/living/carbon/human/species/drask.dm @@ -1,5 +1,3 @@ -#define DRASK_COOLINGSTARTTEMP 280 -#define ENVIRONMENT_COOLINGSTOPTEMP 400 #define DRASK_PITCH_SHIFT -0.1 // a bit lower emotes /datum/species/drask @@ -95,28 +93,40 @@ var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) return E.eye_colour -/datum/species/drask/on_species_gain(mob/living/carbon/human/H) +/datum/species/drask/on_species_gain(mob/living/carbon/human/human) . = ..() - add_verb(H, /mob/living/carbon/human/proc/emote_hum) -/datum/species/drask/on_species_loss(mob/living/carbon/human/H) + var/datum/action/innate/drask/coma/coma = locate() in human.actions + + if(!coma) + coma = new + coma.Grant(human) + + add_verb(human, /mob/living/carbon/human/proc/emote_hum) + +/datum/species/drask/on_species_loss(mob/living/carbon/human/human) + . = ..() + + var/datum/action/innate/drask/coma/coma = locate() in human.actions + coma?.Remove(human) + + remove_verb(human, /mob/living/carbon/human/proc/emote_hum) + +/datum/species/drask/handle_life(mob/living/carbon/human/human) . = ..() - remove_verb(H, /mob/living/carbon/human/proc/emote_hum) -/datum/species/drask/handle_life(mob/living/carbon/human/H) - ..() - if(H.stat == DEAD) + if(human.stat == DEAD) return - var/datum/gas_mixture/environment = H.return_air() - if(environment && H.bodytemperature > DRASK_COOLINGSTARTTEMP && environment.temperature <= ENVIRONMENT_COOLINGSTOPTEMP) - H.adjust_bodytemperature(-5) - if(H.bodytemperature < TCRYO) + + if(human.bodytemperature < TCRYO) var/update = NONE - update |= H.heal_overall_damage(2, 4, updating_health = FALSE) - update |= H.heal_damages(tox = 0.5, oxy = 2, clone = 1, updating_health = FALSE) + update |= human.heal_overall_damage(2, 4, updating_health = FALSE) + update |= human.heal_damages(tox = 0.5, oxy = 2, clone = 1, updating_health = FALSE) + if(update) - H.updatehealth() - var/obj/item/organ/external/head/head = H.get_organ(BODY_ZONE_HEAD) + human.updatehealth() + + var/obj/item/organ/external/head/head = human.get_organ(BODY_ZONE_HEAD) head?.undisfigure() /datum/species/drask/handle_reagents(mob/living/carbon/human/H, datum/reagent/R) @@ -127,15 +137,65 @@ if("salglu_solution") if(prob(33)) H.heal_overall_damage(1, 1, updating_health = FALSE) + H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM * H.metabolism_efficiency * H.digestion_ratio) return FALSE + return ..() +/datum/action/innate/drask + +/datum/action/innate/drask/Grant(mob/user) + . = ..() + + if(!. || !isliving(user)) + return FALSE + + return . + +/datum/action/innate/drask/coma + name = "Enter coma" + desc = "Постепенно вводит в состояние комы, понижает температуру тела. Повторная активация способности позволит прервать вход в кому, либо выйти из нее." + + button_icon_state = "heal" + + COOLDOWN_DECLARE(wake_up_cooldown) + +/datum/action/innate/drask/coma/Activate() + var/mob/living/living = owner + + if(!living.has_status_effect(STATUS_EFFECT_DRASK_COMA)) + handle_activation(living) + return + + handle_deactivation(living) + +/datum/action/innate/drask/coma/proc/handle_activation(mob/living/living) + if(living.stat) + return FALSE + + if(!do_after(living, 5 SECONDS, living, ALL, cancel_on_max = TRUE, max_interact_count = 1)) + return FALSE + + living.apply_status_effect(STATUS_EFFECT_DRASK_COMA) + COOLDOWN_START(src, wake_up_cooldown, 10 SECONDS) + + return TRUE + +/datum/action/innate/drask/coma/proc/handle_deactivation(mob/living/living) + if(!COOLDOWN_FINISHED(src, wake_up_cooldown)) + to_chat(living, span_warning("Вы не можете пробудиться сейчас.")) + return FALSE + + if(!do_after(living, 10 SECONDS, living, ALL, cancel_on_max = TRUE, max_interact_count = 1)) + return FALSE + + living.remove_status_effect(STATUS_EFFECT_DRASK_COMA) + + return TRUE + /datum/species/drask/get_emote_pitch(mob/living/carbon/human/H, tolerance) . = ..() . += DRASK_PITCH_SHIFT - -#undef DRASK_COOLINGSTARTTEMP -#undef ENVIRONMENT_COOLINGSTOPTEMP #undef DRASK_PITCH_SHIFT diff --git a/code/modules/mob/living/carbon/human/species/grey.dm b/code/modules/mob/living/carbon/human/species/grey.dm index f73f111c338..26c51f0b3a0 100644 --- a/code/modules/mob/living/carbon/human/species/grey.dm +++ b/code/modules/mob/living/carbon/human/species/grey.dm @@ -1,3 +1,6 @@ +#define GREYS_ADDITIONAL_GENE_STABILITY 20 +#define GREYS_WATER_DAMAGE 0.6 // 0.6 burn per unit + /datum/species/grey name = SPECIES_GREY name_plural = "Greys" @@ -14,25 +17,31 @@ INTERNAL_ORGAN_KIDNEYS = /obj/item/organ/internal/kidneys/grey, INTERNAL_ORGAN_BRAIN = /obj/item/organ/internal/brain/grey, INTERNAL_ORGAN_APPENDIX = /obj/item/organ/internal/appendix, - INTERNAL_ORGAN_EYES = /obj/item/organ/internal/eyes/grey, //5 darksight. + INTERNAL_ORGAN_EYES = /obj/item/organ/internal/eyes/grey, // 3 darksight. INTERNAL_ORGAN_EARS = /obj/item/organ/internal/ears, ) meat_type = /obj/item/reagent_containers/food/snacks/meat/humanoid/grey - total_health = 90 - oxy_mod = 1.2 //greys are fragile + total_health = 80 // Greys are fragile + oxy_mod = 1.3 stamina_mod = 1.2 + clone_mod = 0.7 - toolspeedmod = -0.2 //20% faster - surgeryspeedmod = -0.2 + toolspeedmod = -0.5 // 50% faster + surgeryspeedmod = -0.5 default_genes = list(/datum/dna/gene/basic/grant_spell/remotetalk) inherent_traits = list( + TRAIT_WEAK_PULLING, + TRAIT_NO_VOCAL_CORDS, TRAIT_HAS_LIPS, TRAIT_HAS_REGENERATION, + TRAIT_ADVANCED_CYBERIMPLANTS, + TRAIT_ACID_PROTECTED, ) + blacklisted_disabilities = NONE clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS bodyflags = HAS_BODY_MARKINGS @@ -55,12 +64,14 @@ /datum/species/grey/on_species_gain(mob/living/carbon/human/H) . = ..() - H.gene_stability += GENE_INSTABILITY_MODERATE + H.gene_stability += GREYS_ADDITIONAL_GENE_STABILITY + RegisterSignal(H, COMSIG_SINK_ACT, PROC_REF(sink_act)) /datum/species/grey/on_species_loss(mob/living/carbon/human/H) . = ..() - H.gene_stability -= GENE_INSTABILITY_MODERATE + H.gene_stability -= GREYS_ADDITIONAL_GENE_STABILITY + UnregisterSignal(H, COMSIG_SINK_ACT) /datum/species/grey/handle_dna(mob/living/carbon/human/H, remove = FALSE) @@ -71,61 +82,87 @@ . = ..() if(method == REAGENT_TOUCH) - if(H.wear_mask) - to_chat(H, "Ваша [H.wear_mask] защищает вас от кислоты!") + var/water_damage = (GREYS_WATER_DAMAGE * volume * H.get_permeability_protection()) + + H.adjustFireLoss(min(water_damage, 80)) + + if(H.has_pain()) + H.emote("scream") + to_chat(H, span_danger("[water_damage > 30 ? "Вы чувствуете ужасающую боль после контакта с водой!" : "Вода жжёт вас!"]")) + + if(volume > 24) + var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD) + if(affecting) + affecting.disfigure() + + else // IV bags and etc + H.adjustFireLoss(min((GREYS_WATER_DAMAGE * volume * 0.5), 80)) + + if(volume < 10) return - if(H.head) - to_chat(H, "Ваша [H.wear_mask] защищает вас от кислоты!") + if(prob(75)) // Prevent emote and chat spam return - if(volume > 25) - if(prob(75)) - H.take_organ_damage(5, 10) - H.emote("scream") - var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD) - if(affecting) - affecting.disfigure() - else - H.take_organ_damage(5, 10) - else - H.take_organ_damage(5, 10) - else - to_chat(H, "Вода жжет вас[volume < 10 ? ", но она недостаточно сконцентрирована, чтобы вам навредить" : null]!") - if(volume >= 10) - H.adjustFireLoss(min(max(4, (volume - 10) * 2), 20)) + if(H.has_pain()) H.emote("scream") - to_chat(H, "Вода жжет вас[volume < 10 ? ", но она недостаточно сконцентрирована, чтобы вам навредить" : null]!") + + to_chat(H, span_danger("Вы чувствуете острое жжение!")) + /datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H) + var/obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator/retranslator = new + retranslator.insert(H) + var/translator_pref = H.client.prefs.speciesprefs - if(translator_pref || ((ismindshielded(H) || J.is_command || J.supervisors == "the captain") && HAS_TRAIT(H, TRAIT_WINGDINGS))) - if(J.title == JOB_TITLE_MIME) - return - if(J.title == JOB_TITLE_CLOWN) - var/obj/item/organ/internal/cyberimp/brain/speech_translator/clown/implant = new - implant.insert(H) - else - var/obj/item/organ/internal/cyberimp/brain/speech_translator/implant = new - implant.insert(H) - if(!translator_pref) - to_chat(H, "Имплант переводчика речи был установлен вам, из-за вашей роли на станции.") + + if(!HAS_TRAIT(H, TRAIT_WINGDINGS)) + return handle_loadout_chip(H, retranslator) + + var/command_roles = FALSE + + if(ismindshielded(H) || J.is_command || J.supervisors == "the captain") + command_roles = TRUE + + if(!translator_pref && !command_roles) // Not command and didn't want wingdings chip, so.. + return handle_loadout_chip(H, retranslator) + + var/obj/item/translator_chip/wingdings/chip = new + if(retranslator.install_chip(H, chip, ignore_lid = TRUE)) + to_chat(H, span_notice("В связи с ваш[translator_pref ? "им недугом" : "ей ответственной работой"], у вас уже есть установленный чип Вингдингс.")) + + handle_loadout_chip(H, retranslator) + + +/datum/species/grey/proc/handle_loadout_chip(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator/retranslator) + var/obj/item/translator_chip/chip = locate() in H.contents // we can take only one chip from loadout + retranslator.install_chip(H, chip, ignore_lid = TRUE) + /datum/species/grey/handle_reagents(mob/living/carbon/human/H, datum/reagent/R) - if(R.id == "sacid") - H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM) - return FALSE - if(R.id == "facid") - H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM) - return FALSE - if(R.id == "acetic_acid") - H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM) - return FALSE if(R.id == "water") H.adjustFireLoss(1) return TRUE + return ..() + /datum/species/grey/get_species_runechat_color(mob/living/carbon/human/H) var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) return E.eye_colour + + +/datum/species/grey/proc/sink_act(mob/living/carbon/human/source) + SIGNAL_HANDLER + + var/grey_message = pick("Вы не ожидали, что в раковине окажется вода!", "Вы слишком поздно понимаете, что совершили ошибку!", "Вы чувствуете адскую боль по всему телу!") + source.adjustFireLoss(30 * source.get_permeability_protection()) + to_chat(source, span_danger("[grey_message]")) + if(source.has_pain()) + source.emote("scream") + + return COMSIG_SINK_ACT_SUCCESS + + +#undef GREYS_ADDITIONAL_GENE_STABILITY +#undef GREYS_WATER_DAMAGE diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index c12df2472d3..c8e23ad1f82 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -102,17 +102,18 @@ JOB_MIN_AGE_COMMAND = 15, ) -/datum/species/machine/on_species_gain(mob/living/carbon/human/H) +/datum/species/machine/on_species_gain(mob/living/carbon/human/human) . = ..() - var/datum/action/innate/change_monitor/monitor = locate() in H.actions + var/datum/action/innate/change_monitor/monitor = locate() in human.actions + if(!monitor) monitor = new - monitor.Grant(H) - monitor = new() - monitor.Grant(H) + monitor.Grant(human) + var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - medhud.remove_from_hud(H) - add_verb(H, list( + medhud.remove_from_hud(human) + + add_verb(human, list( /mob/living/carbon/human/proc/emote_ping, /mob/living/carbon/human/proc/emote_beep, /mob/living/carbon/human/proc/emote_buzz, @@ -120,14 +121,15 @@ /mob/living/carbon/human/proc/emote_yes, /mob/living/carbon/human/proc/emote_no)) - -/datum/species/machine/on_species_loss(mob/living/carbon/human/H) +/datum/species/machine/on_species_loss(mob/living/carbon/human/human) . = ..() - var/datum/action/innate/change_monitor/monitor = locate() in H.actions - monitor?.Remove(H) + var/datum/action/innate/change_monitor/monitor = locate() in human.actions + monitor?.Remove(human) + var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] - medhud.add_to_hud(H) - remove_verb(H, list( + medhud.add_to_hud(human) + + remove_verb(human, list( /mob/living/carbon/human/proc/emote_ping, /mob/living/carbon/human/proc/emote_beep, /mob/living/carbon/human/proc/emote_buzz, @@ -135,6 +137,18 @@ /mob/living/carbon/human/proc/emote_yes, /mob/living/carbon/human/proc/emote_no)) +/datum/species/machine/is_allowed_hair_style(mob/living/carbon/human/human, datum/robolimb/robohead, datum/sprite_accessory/style) + . = ..() + + if(!.) + return + + if(!robohead.is_monitor || !(style.models_allowed && (robohead.company in style.models_allowed)) && style.models_allowed) + return FALSE + + else if(robohead.is_monitor || !(SPECIES_HUMAN in style.species_allowed)) + return FALSE + // Allows IPC's to change their monitor display /datum/action/innate/change_monitor name = "Change Monitor" @@ -153,11 +167,11 @@ if(!head_organ) return if(!robohead.is_monitor) //If they've got a prosthetic head and it isn't a monitor, they've no screen to adjust. Instead, let them change the colour of their optics! - var/optic_colour = input(H, "Select optic colour", H.m_colours["head"]) as color|null + var/optic_colour = tgui_input_color(H, "Select optic colour", H.m_colours["head"]) if(H.incapacitated(INC_IGNORE_RESTRAINED|INC_IGNORE_GRABBED)) to_chat(H, "Ваша попытка сменить отображаемый цвет была прервана.") return - if(optic_colour) + if(!isnull(optic_colour)) H.change_markings(optic_colour, "head") else if(robohead.is_monitor) //Means that the character's head is a monitor (has a screen). Time to customize. diff --git a/code/modules/mob/living/carbon/human/species/wryn.dm b/code/modules/mob/living/carbon/human/species/wryn.dm index 8774d576256..676a5a97482 100644 --- a/code/modules/mob/living/carbon/human/species/wryn.dm +++ b/code/modules/mob/living/carbon/human/species/wryn.dm @@ -5,6 +5,7 @@ deform = 'icons/mob/human_races/r_wryn.dmi' blacklisted = TRUE tail = "wryntail" + eyes = "wryn_eyes_s" punchdamagelow = 0 punchdamagehigh = 1 speed_mod = 1 @@ -60,7 +61,7 @@ TRAIT_NO_SCAN, ) clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS - bodyflags = HAS_SKIN_COLOR + bodyflags = HAS_SKIN_COLOR | HAS_BODY_ACCESSORY dies_at_threshold = TRUE @@ -70,8 +71,10 @@ blood_color = "#FFFF99" blood_species = "Wryn" //Default styles for created mobs. - default_hair = "Antennae" - + default_hair = "Normal antennae" + default_fhair = "Default mane" + default_bodyacc = "Bee Tail" + default_fhair_colour = "#704300" age_sheet = list( SPECIES_AGE_MIN = 15, SPECIES_AGE_MAX = 55, @@ -93,14 +96,10 @@ /datum/species/wryn/after_equip_job(datum/job/J, mob/living/carbon/human/H) var/comb_deafness = H.client.prefs.speciesprefs + if(comb_deafness) var/obj/item/organ/internal/wryn/hivenode/node = H.get_int_organ(/obj/item/organ/internal/wryn/hivenode) - node.remove(H) qdel(node) - else - var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) - head_organ.h_style = "Antennae" - H.update_hair() /* Wryn Sting Action Begin */ diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 3d8ab5a1387..cf013ce2c08 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -283,6 +283,8 @@ if(healths) if(stat != DEAD) . = TRUE + if(SEND_SIGNAL(src, COMSIG_CARBON_UPDATING_HEALTH_HUD, shown_health_amount) & COMPONENT_OVERRIDE_HEALTH_HUD) + return if(shown_health_amount == null) shown_health_amount = health if(shown_health_amount >= maxHealth) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index dc484ceedcb..275345c0147 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -40,12 +40,14 @@ return TRUE /mob/living/proc/can_die() - return !(stat == DEAD || HAS_TRAIT(src, TRAIT_GODMODE)) + return !(stat == DEAD || HAS_TRAIT(src, TRAIT_GODMODE) || HAS_TRAIT(src, TRAIT_NO_DEATH)) // Returns true if mob transitioned from live to dead // Do a check with `can_die` beforehand if you need to do any // handling before `stat` is set /mob/living/death(gibbed) + SEND_SIGNAL(src, COMSIG_LIVING_EARLY_DEATH, gibbed) + if(stat == DEAD || !can_die()) // Whew! Good thing I'm indestructible! (or already dead) return FALSE @@ -103,10 +105,6 @@ SSticker.mode.check_win() clear_alert("succumb") - - if(mind && mind.devilinfo) // Expand this into a general-purpose death-response system when appropriate - mind.devilinfo.beginResurrectionCheck(src) - SEND_SIGNAL(src, COMSIG_LIVING_DEATH, gibbed) // u no we dead return TRUE diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 765ff34e0f8..a6ea4f598d3 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -4,15 +4,7 @@ SEND_SIGNAL(src, COMSIG_LIVING_LIFE, seconds, times_fired) - if(client || registered_z) // This is a temporary error tracker to make sure we've caught everything - var/turf/T = get_turf(src) - if(client && registered_z != T.z) - message_admins("[src] [ADMIN_FLW(src, "FLW")] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z]. If you could ask them how that happened and notify the coders, it would be appreciated.") - add_misc_logs(src, "Z-TRACKING: [src] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z].") - update_z(T.z) - else if (!client && registered_z) - add_misc_logs(src, "Z-TRACKING: [src] of type [src.type] has a Z-registration despite not having a client.") - update_z(null) + track_z() if(HAS_TRAIT(src, TRAIT_NO_TRANSFORM)) return FALSE @@ -229,13 +221,13 @@ severity = 6 livingdoll.icon_state = "living[severity]" if(!livingdoll.filtered) - livingdoll.filtered = TRUE var/icon/mob_mask = icon(icon, icon_state) if(mob_mask.Height() > world.icon_size || mob_mask.Width() > world.icon_size) var/health_doll_icon_state = health_doll_icon ? health_doll_icon : "megasprite" mob_mask = icon('icons/mob/screen_gen.dmi', health_doll_icon_state) //swap to something generic if they have no special doll livingdoll.add_filter("mob_shape_mask", 1, alpha_mask_filter(icon = mob_mask)) livingdoll.add_filter("inset_drop_shadow", 2, drop_shadow_filter(size = -1)) + livingdoll.filtered = TRUE if(severity > 0) overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity) else diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f966df04963..50d50476a21 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -7,6 +7,8 @@ faction += "\ref[src]" determine_move_and_pull_forces() gravity_setup() + if(unique_name) + set_name() if(ventcrawler_trait) var/static/list/ventcrawler_sanity = list( TRAIT_VENTCRAWLER_ALWAYS, @@ -756,6 +758,7 @@ ExtinguishMob() CureAllDiseases(FALSE) fire_stacks = 0 + fire_stacks = 0 on_fire = 0 suiciding = 0 if(buckled) //Unbuckle the mob and clear the alerts. @@ -873,36 +876,49 @@ /mob/living/proc/makeTrail(turf/T) if(!has_gravity()) return - var/blood_exists = 0 - for(var/obj/effect/decal/cleanable/trail_holder/C in loc) //checks for blood splatter already on the floor - blood_exists = 1 + var/blood_exists = FALSE + + for(var/obj/effect/decal/cleanable/trail_holder/C in loc) // checks for blood splatter already on the floor + blood_exists = TRUE + if(isturf(loc)) var/trail_type = getTrail() + if(trail_type) var/brute_ratio = round(getBruteLoss()/maxHealth, 0.1) - if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0))//don't leave trail if blood volume below a threshold - blood_volume = max(blood_volume - max(1, brute_ratio * 2), 0) //that depends on our brute damage. + + if(blood_volume && blood_volume > max(BLOOD_VOLUME_NORMAL*(1 - brute_ratio * 0.25), 0)) // don't leave trail if blood volume below a threshold + setBlood(max(blood_volume - max(1, brute_ratio * 2), 0)) // that depends on our brute damage. var/newdir = get_dir(T, loc) + if(newdir != src.dir) newdir = newdir | dir + if(newdir == 3) //N + S newdir = NORTH + else if(newdir == 12) //E + W newdir = EAST + if((newdir in GLOB.cardinal) && (prob(50))) newdir = turn(get_dir(T, loc), 180) + if(!blood_exists) new /obj/effect/decal/cleanable/trail_holder(loc) + for(var/obj/effect/decal/cleanable/trail_holder/TH in loc) if((!(newdir in TH.existing_dirs) || trail_type == "trails_1" || trail_type == "trails_2") && TH.existing_dirs.len <= 16) //maximum amount of overlays is 16 (all light & heavy directions filled) TH.existing_dirs += newdir TH.overlays.Add(image('icons/effects/blood.dmi', trail_type, dir = newdir)) TH.transfer_mob_blood_dna(src) + if(ishuman(src)) var/mob/living/carbon/human/H = src + if(H.dna.species.blood_color) TH.color = H.dna.species.blood_color + else TH.color = "#A10808" @@ -1250,6 +1266,10 @@ /mob/living/proc/flash_eyes(intensity = 1, override_blindness_check, affect_silicon, visual, type = /atom/movable/screen/fullscreen/flash) if(HAS_TRAIT(src, TRAIT_GODMODE)) return FALSE + + if(SEND_SIGNAL(src, COMSIG_LIVING_EARLY_FLASH_EYES, intensity, override_blindness_check, affect_silicon, visual, type) & STOP_FLASHING_EYES) + return FALSE + if(check_eye_prot() < intensity && (override_blindness_check || !HAS_TRAIT(src, TRAIT_BLIND))) overlay_fullscreen("flash", type) addtimer(CALLBACK(src, PROC_REF(clear_fullscreen), "flash", 25), 25) @@ -1627,68 +1647,10 @@ target.devoured(grabber) -/mob/living/proc/update_z(new_z) // 1+ to register, null to unregister - if(registered_z == new_z) - return - if(registered_z) - SSmobs.clients_by_zlevel[registered_z] -= src - if(isnull(client)) - registered_z = null - return - if(!new_z) - registered_z = new_z - return - //Figure out how many clients were here before - var/oldlen = SSmobs.clients_by_zlevel[new_z].len - SSmobs.clients_by_zlevel[new_z] += src - for(var/index in length(SSidlenpcpool.idle_mobs_by_zlevel[new_z]) to 1 step -1) //Backwards loop because we're removing (guarantees optimal rather than worst-case performance), it's fine to use .len here but doesn't compile on 511 - var/mob/living/simple_animal/animal = SSidlenpcpool.idle_mobs_by_zlevel[new_z][index] - if(animal) - if(!oldlen) - //Start AI idle if nobody else was on this z level before (mobs will switch off when this is the case) - animal.toggle_ai(AI_IDLE) - //If they are also within a close distance ask the AI if it wants to wake up - if(get_dist(get_turf(src), get_turf(animal)) < MAX_SIMPLEMOB_WAKEUP_RANGE) - animal.consider_wakeup() // Ask the mob if it wants to turn on it's AI - //They should clean up in destroy, but often don't so we get them here - else - SSidlenpcpool.idle_mobs_by_zlevel[new_z] -= animal - registered_z = new_z - - /mob/living/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents = TRUE) ..() update_z(new_turf?.z) -/mob/living/proc/owns_soul() - if(mind) - return mind.soulOwner == mind - return 1 - -/mob/living/proc/return_soul() - if(mind) - if(mind.soulOwner.devilinfo)//Not sure how this could happen, but whatever. - mind.soulOwner.devilinfo.remove_soul(mind) - mind.soulOwner = mind - mind.damnation_type = 0 - -/mob/living/proc/has_bane(banetype) - if(mind) - if(mind.devilinfo) - return mind.devilinfo.bane == banetype - return 0 - -/mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker) - if(mind && mind.devilinfo) - return check_devil_bane_multiplier(weapon, attacker) - return 1 - -/mob/living/proc/check_acedia() - if(src.mind && src.mind.objectives) - for(var/datum/objective/sintouched/acedia/A in src.mind.objectives) - return 1 - return 0 - /mob/living/proc/fakefireextinguish() return @@ -1799,6 +1761,11 @@ return var/examine_time = target.get_examine_time() + + var/obj/item/organ/internal/eyes/eyes = get_organ_slot(INTERNAL_ORGAN_EYES) + if(eyes) + examine_time *= eyes.examine_mod + if(examine_time && target != src) var/visible_gender = target.get_visible_gender() var/visible_species = "Unknown" @@ -1838,6 +1805,9 @@ return TRUE return FALSE +/mob/living/examine(mob/user, infix, suffix) + . = ..() + SEND_SIGNAL(src, COMSIG_LIVING_EXAMINE, user, .) /** * Sets the mob's direction lock towards a given atom. @@ -2165,8 +2135,8 @@ update_blind_effects() update_blurry_effects() update_unconscious_overlay() - GLOB.alive_mob_list += src - GLOB.dead_mob_list -= src + add_to_alive_mob_list() + remove_from_dead_mob_list() switch(stat) //Current stat. if(CONSCIOUS) @@ -2179,8 +2149,8 @@ SetLoseBreath(0) SetDisgust(0) SetEyeBlurry(0) - GLOB.alive_mob_list -= src - GLOB.dead_mob_list += src + remove_from_alive_mob_list() + add_to_dead_mob_list() /// Updates hands HUD element. @@ -2322,3 +2292,9 @@ . |= RECHARGE_SUCCESSFUL to_chat(src, span_notice("You feel [(. & RECHARGE_SUCCESSFUL) ? "raw magical energy flowing through you, it feels good!" : "very strange for a moment, but then it passes."]")) + +/mob/living/proc/set_name() + if(numba == 0) + numba = rand(1, 1000) + name = "[name] ([numba])" + real_name = name diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 26a49da0a8e..791cde5c3ec 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -85,6 +85,13 @@ ) return shock_damage +/mob/living/blob_vore_act(obj/structure/blob/special/core/voring_core) + . = ..() + if(HAS_TRAIT(src, TRAIT_BLOB_ZOMBIFIED) || QDELETED(src)) + return FALSE + if(stat == DEAD) + forceMove(voring_core) + /mob/living/emp_act(severity) ..() @@ -185,8 +192,8 @@ /mob/living/proc/IgniteMob() if(fire_stacks > 0 && !on_fire) on_fire = TRUE - visible_message("[src.declent_ru(NOMINATIVE)] загора[pluralize_ru(src.gender,"ется","ются")]!", \ - "[pluralize_ru(src.gender,"Ты загораешься","Вы загораетесь")]!") + visible_message(span_warning("[src.declent_ru(NOMINATIVE)] загора[pluralize_ru(src.gender,"ется","ются")]!"), \ + span_userdanger("[pluralize_ru(src.gender,"Ты загораешься","Вы загораетесь")]!")) set_light_range(light_range + 3) set_light_color("#ED9200") throw_alert("fire", /atom/movable/screen/alert/fire) @@ -212,6 +219,8 @@ /mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person SEND_SIGNAL(src, COMSIG_MOB_ADJUST_FIRE) fire_stacks = clamp(fire_stacks + add_fire_stacks, -20, 20) + var/datum/status_effect/stacking/wet/wet_effect = has_status_effect(/datum/status_effect/stacking/wet) + wet_effect?.combine_wet_and_fire() if(on_fire && fire_stacks <= 0) ExtinguishMob() @@ -239,6 +248,24 @@ SEND_SIGNAL(src, COMSIG_LIVING_FIRE_TICK) return TRUE +/mob/living/proc/WetMob(wet_type = /datum/status_effect/stacking/wet) + var/datum/status_effect/stacking/wet/effect = has_status_effect(wet_type) + return effect?.WetMob() + + +/mob/living/proc/adjust_wet_stacks(add_wet_stacks, wet_type = /datum/status_effect/stacking/wet) //Adjusting the amount of fire_stacks we have on person + var/datum/status_effect/stacking/wet/effect = has_status_effect(wet_type) + if(effect) + effect.add_stacks(add_wet_stacks) + else + apply_status_effect(wet_type, add_wet_stacks) + + +/mob/living/proc/DryMob(wet_type = /datum/status_effect/stacking/wet) + var/datum/status_effect/stacking/wet/effect = has_status_effect(wet_type) + return effect?.DryMob() + + /mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) ..() adjust_fire_stacks(3) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 0ee33dffb53..ede980b09be 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -38,6 +38,7 @@ var/on_fire = 0 //The "Are we on fire?" var var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20 + var/mob_size = MOB_SIZE_HUMAN var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes. @@ -66,6 +67,9 @@ var/gene_stability = DEFAULT_GENE_STABILITY var/ignore_gene_stability = 0 + /// the id a mob gets when it's created + var/numba = 0 + var/unique_name = FALSE /// A log of what we've said, plain text, no spans or junk, essentially just each individual "message" var/list/say_log @@ -141,3 +145,6 @@ /// Famous last words -- if succumbing, what the user's last words were var/last_words + + /// List of alpha changelog from various sources + var/list/alphas = list(ALPHA_SOURCE_DEFAULT = 1) diff --git a/code/modules/mob/living/living_infected_blob_mobs.dm b/code/modules/mob/living/living_infected_blob_mobs.dm index 65970863476..b86bf2aac47 100644 --- a/code/modules/mob/living/living_infected_blob_mobs.dm +++ b/code/modules/mob/living/living_infected_blob_mobs.dm @@ -101,6 +101,10 @@ return FALSE +/mob/living/simple_animal/hostile/illusion/can_be_blob() + return FALSE + + /mob/living/simple_animal/hostile/asteroid/can_be_blob() return FALSE diff --git a/code/modules/mob/living/living_say.dm b/code/modules/mob/living/living_say.dm index 10ac49a2132..eef30503432 100644 --- a/code/modules/mob/living/living_say.dm +++ b/code/modules/mob/living/living_say.dm @@ -205,6 +205,10 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) if(check_mute(client.ckey, MUTE_IC)) to_chat(src, span_danger("You cannot speak in IC (Muted).")) return FALSE + + var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_TRY_SPEECH, message) + if(sigreturn & COMPONENT_CANNOT_SPEAK) + return FALSE if(sanitize) message = trim_strip_html_properly(message, 512) @@ -234,6 +238,17 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) var/datum/multilingual_say_piece/first_piece = message_pieces[1] + if(SEND_SIGNAL( \ + src, \ + COMSIG_LIVING_EARLY_SAY, \ + message, \ + verb, \ + ignore_speech_problems, \ + ignore_atmospherics, \ + ignore_languages, \ + first_piece) & COMPONENT_PREVENT_SPEAKING) + return FALSE + if(first_piece.speaking?.flags & HIVEMIND) first_piece.speaking.broadcast(src, first_piece.message) return TRUE @@ -270,6 +285,11 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) ignore_atmospherics = TRUE if(is_muzzled()) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(translator) // we can whisper with translator and muzzle + whisper_say(message_pieces) + return TRUE + var/obj/item/clothing/mask/muzzle/G = wear_mask if(G.mute == MUZZLE_MUTE_ALL) //if the mask is supposed to mute you completely or just muffle you to_chat(src, span_danger("You're muzzled and cannot speak!")) @@ -386,7 +406,7 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) var/speech_bubble_test = say_test(message) for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency) + M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency, FALSE) if(M.client) speech_bubble_recipients.Add(M.client) @@ -449,7 +469,8 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) if(stat) return - if(is_muzzled()) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(is_muzzled() && !translator?.active) if(istype(wear_mask, /obj/item/clothing/mask/muzzle/tapegag)) //just for tape to_chat(src, span_danger("Your mouth is taped and you cannot speak!")) else @@ -546,14 +567,14 @@ GLOBAL_LIST_EMPTY(channel_to_radio_key) var/speech_bubble_test = say_test(message) for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE, is_whisper = TRUE) if(M.client) speech_bubble_recipients.Add(M.client) if(eavesdropping.len) stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. for(var/mob/M in eavesdropping) - M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE) + M.hear_say(message_pieces, verb, italics, src, use_voice = FALSE, is_whisper = TRUE) if(M.client) speech_bubble_recipients.Add(M.client) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 74bb56e8bd9..b221298d294 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -47,6 +47,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( sight = SEE_TURFS | SEE_MOBS | SEE_OBJS nightvision = 8 can_buckle_to = FALSE + hud_type = /datum/hud/ai var/list/network = list("SS13","Telecomms","Research Outpost","Mining Outpost") var/obj/machinery/camera/current = null var/list/connected_robots = list() @@ -659,9 +660,8 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( /mob/living/silicon/ai/blob_act(obj/structure/blob/B) if(stat != DEAD) adjustBruteLoss(60) - return 1 - return 0 - + return TRUE + return TRUE /mob/living/silicon/ai/emp_act(severity) ..() @@ -1335,6 +1335,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( on_the_card = TRUE aiRestorePowerRoutine = 0//So the AI initially has power. update_blind_effects() + update_sight() control_disabled = TRUE//Can't control things remotely if you're stuck in a card! aiRadio.disabledAi = TRUE //No talking on the built-in radio for you either! forceMove(card) //Throw AI into the card. @@ -1351,7 +1352,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list( return TRUE -/mob/living/silicon/ai/proc/can_see(atom/A) +/mob/living/silicon/ai/can_see(atom/A) if(isturf(loc)) //AI in core, check if on cameras //get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera //apc_override is needed here because AIs use their own APC when depowered diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index ff7645f789f..56d89bb9be2 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -26,10 +26,10 @@ if(doomsday_device) doomsday_device.timing = 0 - SSshuttle.emergencyNoEscape = 0 + SSshuttle.emergencyNoEscape = FALSE if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time + SSshuttle.emergency.timer = world.time + 3 MINUTES GLOB.priority_announcement.Announce("Вредоносное окружение устранено. У вас есть 3 минуты, чтобы подняться на борт эвакуационного шаттла.", "Приоритетное оповещение.", 'sound/AI/shuttledock.ogg') qdel(doomsday_device) diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index 1db3c6566cb..003bba26bf5 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -176,7 +176,7 @@ to_chat(src, span_notice("You move down.")) -/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) +/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE, is_whisper = FALSE) if(relay_speech) if(istype(ai)) ai.relay_speech(speaker, message_pieces, verb) diff --git a/code/modules/mob/living/silicon/ai/update_status.dm b/code/modules/mob/living/silicon/ai/update_status.dm index 989330dedfc..c351e0ee6ce 100644 --- a/code/modules/mob/living/silicon/ai/update_status.dm +++ b/code/modules/mob/living/silicon/ai/update_status.dm @@ -10,4 +10,4 @@ ..() /mob/living/silicon/ai/has_vision(information_only = FALSE) - return ..() && !lacks_power() + return ..() && (!lacks_power() || on_the_card) diff --git a/code/modules/mob/living/silicon/death.dm b/code/modules/mob/living/silicon/death.dm index e3383f980b5..df12411bfd4 100644 --- a/code/modules/mob/living/silicon/death.dm +++ b/code/modules/mob/living/silicon/death.dm @@ -16,7 +16,7 @@ drop_hat() - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() spawn(15) if(animation) qdel(animation) if(src) qdel(src) @@ -28,7 +28,7 @@ icon = null invisibility = INVISIBILITY_ABSTRACT dust_animation() - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() QDEL_IN(src, 15) return TRUE diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 709c20446e5..8efe5264e71 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -230,8 +230,8 @@ /mob/living/silicon/pai/blob_act() if(stat != DEAD) adjustBruteLoss(60) - return 1 - return 0 + return TRUE + return FALSE /mob/living/silicon/pai/emp_act(severity) diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index b58059c48a3..3ce665ce098 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -22,8 +22,8 @@ drop_hat() - GLOB.alive_mob_list -= src - GLOB.dead_mob_list -= src + remove_from_alive_mob_list() + remove_from_dead_mob_list() QDEL_IN(animation, 15) QDEL_IN(src, 15) return TRUE @@ -36,7 +36,7 @@ invisibility = INVISIBILITY_ABSTRACT if(mmi) qdel(mmi) //Delete the MMI first so that it won't go popping out. - GLOB.dead_mob_list -= src + remove_from_dead_mob_list() QDEL_IN(src, 15) return TRUE diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index ed9a968742d..3df009b8cd6 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -109,6 +109,54 @@ ) ..() +/obj/item/gripper/universal + name = "Universal gripper" + desc = "Универсальный захватывающий инструмент, используемый для выполнения сверх секретных заданий клана паука." + icon_state = "diskgripper" + can_hold = list(/obj/item/firealarm_electronics, + /obj/item/airalarm_electronics, + /obj/item/airlock_electronics, + /obj/item/firelock_electronics, + /obj/item/intercom_electronics, + /obj/item/apc_electronics, + /obj/item/access_control, + /obj/item/tracker_electronics, + /obj/item/stock_parts, + /obj/item/vending_refill, + /obj/item/mounted/frame/light_fixture, + /obj/item/mounted/frame/apc_frame, + /obj/item/mounted/frame/alarm_frame, + /obj/item/mounted/frame/firealarm, + /obj/item/mounted/frame/newscaster_frame, + /obj/item/mounted/frame/intercom, + /obj/item/mounted/frame/extinguisher, + /obj/item/mounted/frame/light_switch, + /obj/item/mounted/frame/door_control, + /obj/item/assembly/control, + /obj/item/rack_parts, + /obj/item/camera_assembly, + /obj/item/tank, + /obj/item/circuitboard, + /obj/item/stack/tile/light, + /obj/item/stack/ore/bluespace_crystal, + /obj/item/organ, + /obj/item/reagent_containers/iv_bag, + /obj/item/robot_parts/head, + /obj/item/robot_parts/l_arm, + /obj/item/robot_parts/r_arm, + /obj/item/robot_parts/l_leg, + /obj/item/robot_parts/r_leg, + /obj/item/robot_parts/chest, + /obj/item/stack/sheet/mineral/plasma, + /obj/item/card, + /obj/item/camera_film, + /obj/item/paper, + /obj/item/photo, + /obj/item/toy/plushie, + /obj/item/reagent_containers/food, + /obj/item/seeds, + /obj/item/disk/plantgene) + /obj/item/gripper/nuclear name = "Nuclear gripper" desc = "Designed for all your nuclear needs." diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index dbe4d67f6b1..7b122a9c00a 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -110,6 +110,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/updating = 0 //portable camera camerachunk update hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + hud_type = /datum/hud/robot var/default_cell_type = /obj/item/stock_parts/cell/high ///Jetpack-like effect. @@ -171,12 +172,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( cell = new default_cell_type(src) initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type + + for(var/V in components) + if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type ..() @@ -195,6 +196,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(length(module?.borg_skins) <= 1 && (has_transform_animation || module?.has_transform_animation)) transform_animation(icon_state, TRUE) + add_strippable_element() /mob/living/silicon/robot/proc/add_strippable_element() @@ -207,9 +209,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( additional_law_channels["Binary"] = get_language_prefix(LANGUAGE_BINARY) if(!connect_to_AI) return + var/found_ai = ai_to_sync_to if(!found_ai) found_ai = select_active_ai_with_fewest_borgs() + if(found_ai) lawupdate = TRUE connect_to_ai(found_ai) @@ -220,7 +224,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/rename_character(oldname, newname) if(!..(oldname, newname)) - return 0 + return FALSE if(oldname != real_name) notify_ai(ROBOT_NOTIFY_AI_NAME, oldname, newname) @@ -251,7 +255,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(mmi && mmi.brainmob) mmi.brainmob.name = newname - return 1 + return TRUE /mob/living/silicon/robot/proc/get_default_name(var/prefix as text) @@ -272,11 +276,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/verb/Namepick() set category = "Robot Commands" + if(custom_name) - return 0 + return FALSE + if(!allow_rename) to_chat(src, span_warning("Rename functionality is not enabled on this unit.")) - return 0 + return FALSE + rename_self(braintype, 1) /mob/living/silicon/robot/verb/Change_Voice() @@ -294,8 +301,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/setup_PDA() if(!rbPDA) rbPDA = new(src) + rbPDA.set_name_and_job(real_name, braintype) var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) + if(M) if(scrambledcodes) M.hidden = 1 @@ -304,16 +313,21 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/binarycheck() if(is_component_functioning("comms")) - return 1 - return 0 + return TRUE + + return FALSE //If there's an MMI in the robot, have it ejected when the mob goes away. --NEO //Improved /N /mob/living/silicon/robot/Destroy() SStgui.close_uis(wires) + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T + + if(T) + mmi.loc = T + if(mmi.brainmob) mind.transfer_to(mmi.brainmob) mmi.update_icon() @@ -321,9 +335,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( to_chat(src, span_boldannounceooc("Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.")) ghostize() error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) connected_ai.connected_robots -= src + QDEL_NULL(wires) QDEL_NULL(module) QDEL_NULL(camera) @@ -332,161 +349,79 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( QDEL_NULL(spark_system) QDEL_NULL(self_diagnosis) QDEL_NULL(ion_trail) + return ..() /mob/living/silicon/robot/proc/pick_module(var/forced_module = null) if(module) return + var/list/modules = list("Generalist", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(islist(limited_modules) && limited_modules.len) + + if(islist(limited_modules) && LAZYLEN(limited_modules)) modules = limited_modules.Copy() + if(mmi?.alien) forced_module = "Hunter" + if(mmi?.syndicate) modules = list("Syndicate Saboteur", "Syndicate Medical", "Syndicate Bloodhound") + if(mmi?.ninja) forced_module = "Ninja" + if(mmi?.clock || isclocker(src)) forced_module = "Clockwork" + if(forced_module) modtype = forced_module + else modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules + if(!modtype) robot_module_hat_offset(icon_state) return + designation = modtype if(module) return - switch(modtype) - if("Generalist") - module = new /obj/item/robot_module/standard(src) - - if("Service") - module = new /obj/item/robot_module/butler(src) - see_reagents = TRUE - - if("Miner") - module = new /obj/item/robot_module/miner(src) - if(camera && ("Robots" in camera.network)) - camera.network.Add("Mining Outpost") - - if("Medical") - module = new /obj/item/robot_module/medical(src) - if(camera && ("Robots" in camera.network)) - camera.network.Add("Medical") - status_flags &= ~CANPUSH - see_reagents = TRUE - - if("Security") - if(!weapons_unlock) - var/count_secborgs = 0 - for(var/mob/living/silicon/robot/R in GLOB.alive_mob_list) - if(R && R.stat != DEAD && R.module && istype(R.module, /obj/item/robot_module/security)) - count_secborgs++ - var/max_secborgs = 2 - if(GLOB.security_level == SEC_LEVEL_GREEN) - max_secborgs = 1 - if(count_secborgs >= max_secborgs) - to_chat(src, span_warning("There are too many Security cyborgs active. Please choose another module.")) - return - module = new /obj/item/robot_module/security(src) - status_flags &= ~CANPUSH - - if("Engineering") - module = new /obj/item/robot_module/engineering(src) - if(camera && ("Robots" in camera.network)) - camera.network.Add("Engineering") - - ADD_TRAIT(src, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) - - if("Janitor") - module = new /obj/item/robot_module/janitor(src) - - if("Combat") // Gamma ERT - module = new /obj/item/robot_module/combat(src) - status_flags &= ~CANPUSH - - if("Hunter") - module = new /obj/item/robot_module/hunter(src) - modtype = "Xeno-Hu" - - if("Syndicate Saboteur") - spawn_syndicate_borgs(src, "Saboteur", get_turf(src)) - qdel(src) - return - - if("Syndicate Medical") - spawn_syndicate_borgs(src, "Medical", get_turf(src)) - qdel(src) - return - - if("Syndicate Bloodhound") - spawn_syndicate_borgs(src, "Bloodhound", get_turf(src)) - qdel(src) - return - - if("Clockwork") - module = new /obj/item/robot_module/clockwork(src) - icon = 'icons/mob/clockwork_mobs.dmi' - icon_state = "cyborg" - status_flags &= ~CANPUSH - QDEL_NULL(mmi) - mmi = new /obj/item/mmi/robotic_brain/clockwork(src) - - if("Drone") - var/mob/living/silicon/robot/drone/drone = new(get_turf(src)) - mind.transfer_to(drone) - qdel(src) - return - - if("Cogscarab") - var/mob/living/silicon/robot/cogscarab/cogscarab = new(get_turf(src)) - mind.transfer_to(cogscarab) - qdel(src) - return + for(var/obj/item/robot_module/r_module as anything in subtypesof(/obj/item/robot_module)) + if(modtype != r_module.name) + continue - if("Ninja") - var/mob/living/silicon/robot/syndicate/saboteur/ninja/ninja = new(get_turf(src)) - mind.transfer_to(ninja) - qdel(src) - return + module = r_module + break - if("Deathsquad") - var/mob/living/silicon/robot/deathsquad/death = new(get_turf(src)) - mind.transfer_to(death) - qdel(src) - return - - if("Destroyer") // Rolling Borg - var/mob/living/silicon/robot/destroyer/destroy = new(get_turf(src)) - mind.transfer_to(destroy) - qdel(src) - return + module = new module(src) if(!module) CRASH("[key_name_log(src)] tried to choose non-existent '[modtype]' module!") - //languages + /// module effects + if(!module.on_apply(src)) + module = initial(module) + return + /// languages module.add_languages(src) - //subsystems + /// subsystems module.add_subsystems_and_actions(src) hands.icon_state = lowertext(module.module_type) SSblackbox.record_feedback("tally", "cyborg_modtype", 1, "[lowertext(modtype)]") - rename_character(real_name, get_default_name()) - - if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") - status_flags &= ~CANPUSH + rename_character(real_name, get_default_name()) choose_icon() + if(client.stat_tab == "Status") SSstatpanels.set_status_tab(client) + if(!static_radio_channels) radio.config(module.channels) + notify_ai(ROBOT_NOTIFY_AI_MODULE) robot_module_hat_offset(icon_state) @@ -527,6 +462,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/datum/robot_component/cell/cell_component = R.components["power cell"] var/obj/item/stock_parts/cell/borg_cell = get_cell(M) + if(borg_cell) QDEL_NULL(R.cell) borg_cell.forceMove(R) @@ -630,9 +566,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/verb/cmd_robot_alerts() set category = "Robot Commands" set name = "Show Alerts" + if(usr.stat == DEAD) to_chat(src, span_userdanger("Alert: You are dead.")) return //won't work if dead + robot_alerts() /mob/living/silicon/robot/proc/robot_alerts() @@ -668,9 +606,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/ionpulse() if(!ionpulse_on) return FALSE + if(!cell || !cell.use(25)) // 500 steps on a default cell. toggle_ionpulse(silent = TRUE) return FALSE + return TRUE @@ -678,6 +618,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!ionpulse) if(!silent) to_chat(src, span_notice("No thrusters are installed!")) + return if(!ion_trail) @@ -705,6 +646,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( adjustBruteLoss(30) else gib() + return TRUE // this function displays the cyborgs current cell charge in the stat panel @@ -745,18 +687,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource) if(!(class in alarms_listend_for)) return + if(alarmsource.z != z) return + if(stat == DEAD) return + queueAlarm(text("--- [class] alarm detected in [A.name]!"), class) /mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared) if(cleared) if(!(class in alarms_listend_for)) return + if(origin.z != z) return + queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0) /mob/living/silicon/robot/ex_act(severity) @@ -773,7 +720,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() + + if(prob(75) && Proj.damage > 0) + spark_system.start() + return 2 @@ -787,37 +737,47 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!opened) to_chat(user, span_warning("You must open the cover to access cyborg's internals!")) return ATTACK_CHAIN_PROCEED + for(var/V in components) var/datum/robot_component/component = components[V] if(!component.installed && istype(I, component.external_type)) if(!user.drop_transfer_item_to_loc(I, src)) return ..() + component.installed = TRUE component.wrapped = I component.install() + I.move_to_null_space() var/obj/item/robot_parts/robot_component/robot_component = I + if(istype(robot_component)) component.brute_damage = robot_component.brute component.electronics_damage = robot_component.burn + to_chat(user, span_notice("You have installed [I].")) return ATTACK_CHAIN_BLOCKED_ALL if(iscoil(I)) add_fingerprint(user) var/obj/item/stack/cable_coil/coil = I + if(!wiresexposed && !isdrone(src)) to_chat(user, span_warning("You should expose the wires first!")) return ATTACK_CHAIN_PROCEED + if(!getFireLoss()) to_chat(user, span_warning("Nothing to fix!")) return ATTACK_CHAIN_PROCEED + if(!getFireLoss(TRUE)) to_chat(user, span_warning("The damaged components are beyond saving!")) return ATTACK_CHAIN_PROCEED + if(!coil.use(1)) to_chat(user, span_warning("You need at least one length of cable to fix anything!")) return ATTACK_CHAIN_PROCEED + heal_overall_damage(burn = 30) visible_message( span_notice("[user] has fixed some of the burnt wires in [src]'s internals."), @@ -832,16 +792,21 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!opened) to_chat(user, span_warning("You must open the cover to access cyborg's internals!")) return ATTACK_CHAIN_PROCEED + if(wiresexposed) to_chat(user, span_warning("You should hide the wires first!")) return ATTACK_CHAIN_PROCEED + if(cell) to_chat(user, span_warning("There is a power cell already installed!")) return ATTACK_CHAIN_PROCEED + if(!user.drop_transfer_item_to_loc(I, src)) return ..() + to_chat(user, span_notice("You have installed the power cell.")) var/datum/robot_component/cell/cell_component = components["power cell"] + cell = I cell_component.installed = TRUE cell_component.wrapped = I @@ -850,6 +815,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z cell_component.brute_damage = 0 cell_component.electronics_damage = 0 + var/been_hijacked = FALSE for(var/mob/living/simple_animal/demon/pulse_demon/demon in cell) if(!been_hijacked) @@ -857,8 +823,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( been_hijacked = TRUE else demon.exit_to_turf() + if(been_hijacked) cell.rigged = FALSE + module?.update_cells() diag_hud_set_borgcell() return ATTACK_CHAIN_BLOCKED_ALL @@ -868,30 +836,38 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!opened) to_chat(user, span_warning("You must open the cover to access cyborg's internals!")) return ATTACK_CHAIN_PROCEED + if(!radio) //sanityyyyyy to_chat(user, span_warning("Unable to locate a radio!")) return ATTACK_CHAIN_PROCEED + radio.attackby(I, user, params) //GTFO, you have your own procs return ATTACK_CHAIN_BLOCKED_ALL if(I.GetID()) // trying to unlock the interface with an ID card add_fingerprint(user) + if(opened) to_chat(user, span_warning("You must close the cover to swipe an ID card!")) return ATTACK_CHAIN_PROCEED + if(emagged) //still allow them to open the cover to_chat(user, span_danger("The interface seems slightly damaged!")) + if(!allowed(I)) to_chat(user, span_warning("Access denied!")) return ATTACK_CHAIN_PROCEED + locked = !locked visible_message( span_warning("[user] has [locked ? "locked" : "unlocked"] [src]'s interface."), span_notice("[user] has [locked ? "locked" : "unlocked"] your interface."), ignored_mobs = user, ) + to_chat(user, span_notice("You have [locked ? "locked" : "unlocked"] cyborg's interface.")) update_icons() + return ATTACK_CHAIN_PROCEED_SUCCESS if(istype(I, /obj/item/borg/upgrade)) @@ -900,22 +876,28 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!opened) to_chat(user, span_warning("You must open the cover to access cyborg's internals!")) return ATTACK_CHAIN_PROCEED + if(!module && upgrade.require_module) to_chat(user, span_warning("The cyborg must choose a specialization module before it can be upgraded!")) return ATTACK_CHAIN_PROCEED + if(!user.drop_transfer_item_to_loc(upgrade, src)) return ..() + if(!upgrade.action(src, user)) upgrade.forceMove(drop_location()) return ATTACK_CHAIN_BLOCKED_ALL + visible_message( span_warning("[user] has applied [upgrade] to [src]."), span_notice("[user] has applied [upgrade] to you."), ignored_mobs = user, ) + to_chat(user, span_notice("You have applied [upgrade] to [src].")) install_upgrade(upgrade) module?.fix_modules() //Set up newly added items with NODROP trait. + return ATTACK_CHAIN_BLOCKED_ALL if(istype(I, /obj/item/mmi_radio_upgrade)) @@ -923,32 +905,40 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!opened) to_chat(user, span_warning("You must open the cover to access cyborg's internals!")) return ATTACK_CHAIN_PROCEED + if(!mmi) to_chat(user, span_warning("This cyborg does not have an MMI to augment!")) return ATTACK_CHAIN_PROCEED + if(mmi.radio) to_chat(user, span_warning("A radio upgrade is already installed!")) return ATTACK_CHAIN_PROCEED + if(!user.drop_transfer_item_to_loc(I, src)) return ..() + visible_message( span_warning("[user] has installed the radio upgrade to [src]'s MMI."), span_notice("[user] has installed the radio upgrade into yor MMI."), ignored_mobs = user, ) + to_chat(user, span_notice("You have installed the radio upgrade to [src]'s MMI.")) mmi.install_radio() qdel(I) + return ATTACK_CHAIN_BLOCKED_ALL if(istype(I, /obj/item/clockwork/clockslab) && isclocker(src) && isclocker(user) && src != user) add_fingerprint(user) locked = !locked + visible_message( span_warning("[user] has [locked ? "locked" : "unlocked"] [src]'s interface."), span_notice("[user] has [locked ? "locked" : "unlocked"] your interface."), ignored_mobs = user, ) + to_chat(user, span_notice("You have [locked ? "locked" : "unlocked"] cyborg's interface.")) update_icons() return ATTACK_CHAIN_PROCEED_SUCCESS @@ -959,33 +949,42 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) if(user.a_intent == INTENT_HARM) // no interactions in combat return FALSE + if(!opened) return FALSE + . = TRUE if(!I.use_tool(src, user, 0, volume = 0)) return + if(wiresexposed) wires.Interact(user) /mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) if(user.a_intent == INTENT_HARM) // no interactions in combat return FALSE + if(!opened) return FALSE + . = TRUE if(!I.use_tool(src, user, 0, volume = 0)) return + if(wiresexposed) wires.Interact(user) /mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) if(user.a_intent == INTENT_HARM) // no interactions in combat return FALSE + if(!opened) return FALSE + . = TRUE if(!I.use_tool(src, user, volume = I.tool_volume)) return + if(!cell) // haxing wiresexposed = !wiresexposed to_chat(user, span_notice("The wires have been [wiresexposed ? "exposed" : "unexposed"].")) @@ -996,62 +995,79 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything else to_chat(user, "Unable to locate a radio.") + update_icons() /mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) if(user.a_intent == INTENT_HARM) // no interactions in combat return FALSE + . = TRUE if(!I.tool_use_check(user, 0)) return + if(!opened) if(locked) to_chat(user, "The cover is locked and cannot be opened.") return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return + to_chat(user, "You open the cover.") opened = TRUE update_icons() return + else if(cell) if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return + to_chat(user, "You close the cover.") opened = FALSE update_icons() return + else if(wiresexposed && wires.is_all_cut()) //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. if(!mmi) to_chat(user, "[src] has no brain to remove.") return + to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") if(I.use_tool(src, user, 30, volume = I.tool_volume)) user.visible_message("[user] deconstructs [src]!", span_notice("You unfasten the securing bolts, and [src] falls to pieces!")) deconstruct() + return // Okay we're not removing the cell or an MMI, but maybe something else? var/list/removable_components = list() for(var/V in components) if(V == "power cell") continue + var/datum/robot_component/C = components[V] if(C.installed == 1 || C.installed == -1) removable_components += V + if(module) removable_components += module.custom_removals + var/remove = tgui_input_list(user, "Which component do you want to pry out?", "Remove Component", removable_components) if(!remove) return + if(module && module.handle_custom_removal(remove, user, I)) return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return + var/datum/robot_component/C = components[remove] var/obj/item/robot_parts/robot_component/thing = C.wrapped to_chat(user, "You remove \the [thing].") + if(istype(thing)) thing.brute = C.brute_damage thing.burn = C.electronics_damage @@ -1059,6 +1075,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( thing.loc = loc var/was_installed = C.installed C.installed = 0 + if(was_installed == 1) C.uninstall() @@ -1066,23 +1083,29 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/welder_act(mob/user, obj/item/I) if(user.a_intent == INTENT_HARM) // no interactions in combat return FALSE + if(user == src) //No self-repair dummy return FALSE + . = TRUE if(!getBruteLoss()) to_chat(user, span_warning("Nothing to fix!")) return . + if(!getBruteLoss(TRUE)) to_chat(user, span_warning("The damaged components are beyond saving!")) return . + if(!I.use_tool(src, user, volume = I.tool_volume)) return . + heal_overall_damage(brute = 30) visible_message( span_notice("[user] has patched some dents on [src] with [I]."), span_notice("[user] has patched some dents on your externals with [I]."), ignored_mobs = user, ) + to_chat(user, span_notice("You have patched some dents on [src] with [I].")) @@ -1095,9 +1118,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/emag_act(mob/user) if(!ishuman(user) && !issilicon(user)) return + if(isclocker(src)) to_chat(user, span_danger("As you try to emag, a magic force keeps the cover locked!")) return + var/mob/living/M = user if(!opened)//Cover is closed if(!is_emaggable) @@ -1108,14 +1133,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( locked = 0 else to_chat(user, "The cover is already unlocked.") + return if(opened)//Cover is open if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) to_chat(user, "You must close the panel first") return + else add_attack_logs(user, src, "emag converted") add_conversion_logs(src, "Converted as a slave to [key_name_log(user)]") @@ -1152,18 +1180,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( laws.show_laws(src) to_chat(src, span_boldwarning("ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.")) SetLockdown(FALSE) + if(module) module.emag_act(user) module.module_type = "Malf" // For the cool factor update_module_icon() + update_icons() + return // Here so admins can unemag borgs. /mob/living/silicon/robot/unemag() SetEmagged(FALSE) + if(!module) return + uneq_all() module.module_type = initial(module.module_type) update_module_icon() @@ -1176,11 +1209,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/ratvar_act(weak = FALSE) if(isclocker(src) && module?.type == /obj/item/robot_module/clockwork) return + if(!weak) if(module) reset_module() + pick_module("Clockwork") pdahide = TRUE + SSticker.mode.add_clocker(mind) UnlinkSelf() laws = new /datum/ai_laws/ratvar @@ -1196,9 +1232,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( update_icons() to_chat(usr, span_notice("You [locked ? "lock" : "unlock"] your cover.")) return + if(!locked) to_chat(usr, span_warning("You cannot lock your cover yourself. Find a robotocist.")) return + if(tgui_alert(usr, "You cannnot lock your own cover again. Are you sure?\nYou will need a roboticist to re-lock you.", "Unlock Own Cover", list("Yes", "No")) == "Yes") locked = FALSE update_icons() @@ -1217,10 +1255,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(dummy.check_access(I)) qdel(dummy) - return 1 + return TRUE qdel(dummy) - return 0 + return FALSE /mob/living/silicon/robot/regenerate_icons() @@ -1231,16 +1269,21 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( cut_overlays() borg_icons() eyes_overlays() + if(opened) var/panelprefix = "ov" if(custom_sprite) //Custom borgs also have custom panels, heh panelprefix = "[ckey]" + if(custom_panel in custom_panel_names) //For default borgs with different panels panelprefix = custom_panel + if(wiresexposed) add_overlay("[panelprefix]-openpanel +w") + else if(cell) add_overlay("[panelprefix]-openpanel +c") + else add_overlay("[panelprefix]-openpanel -c") @@ -1248,14 +1291,18 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/image/head_icon if(!hat_icon_state) hat_icon_state = inventory_head.icon_state + if(!hat_alpha) hat_alpha = inventory_head.alpha + if(!hat_color) hat_color = inventory_head.color + if(!hat_icon_file) hat_icon_file = inventory_head.onmob_sheets[ITEM_SLOT_HEAD_STRING] head_icon = get_hat_overlay() + if(head_icon) add_overlay(head_icon) @@ -1264,6 +1311,8 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(blocks_emissive) add_overlay(get_emissive_block()) + if(module) + module.set_appearance(src) /mob/living/silicon/robot/proc/borg_icons() // Exists so that robot/destroyer can override it return @@ -1274,15 +1323,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(custom_panel in custom_eye_names) if(isclocker(src) && SSticker.mode.power_reveal) eyes_olay = "eyes-[custom_panel]-clocked" + else eyes_olay = "eyes-[custom_panel]" + else if(isclocker(src) && SSticker.mode.power_reveal) eyes_olay = "eyes-[icon_state]-clocked" + else eyes_olay = "eyes-[icon_state]" + if(eyes_olay) add_overlay(eyes_olay) + return /mob/living/silicon/robot/proc/installed_modules() @@ -1293,6 +1347,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!module) pick_module() return + var/dat = {"Close

@@ -1309,15 +1364,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( for(var/obj in module.modules) if(!obj) dat += text("Resource depleted") + else if(activated(obj)) dat += text("[obj]Activated") + else dat += text("[obj]Activate") + if(emagged || weapons_unlock) if(activated(module.emag)) dat += text("[module.emag]Activated") + else dat += text("[module.emag]Activate") + dat += "" /* if(activated(obj)) @@ -1333,16 +1393,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(!upgrade.instant_use) RegisterSignal(upgrade, COMSIG_QDELETING, PROC_REF(on_upgrade_deleted)) upgrades += upgrade + if(upgrade.loc != src) upgrade.forceMove(src) + else qdel(upgrade) ///Called when an applied upgrade is deleted. /mob/living/silicon/robot/proc/on_upgrade_deleted(obj/item/borg/upgrade/old_upgrade) SIGNAL_HANDLER + if(!QDELETED(src)) old_upgrade.deactivate(src) + upgrades -= old_upgrade UnregisterSignal(old_upgrade, COMSIG_QDELETING) @@ -1350,6 +1414,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( . = ..() if(.) return TRUE + if(href_list["mach_close"]) var/t1 = text("window=[href_list["mach_close"]]") unset_machine() @@ -1366,6 +1431,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/obj/item/O = locate(href_list["act"]) if(!istype(O) || !(O.loc == src || O.loc == src.module)) return TRUE + activate_module(O) installed_modules() return TRUE @@ -1377,20 +1443,26 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(href_list["deact"]) var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) if(module_state_1 == O) module_state_1 = null contents -= O + else if(module_state_2 == O) module_state_2 = null contents -= O + else if(module_state_3 == O) module_state_3 = null contents -= O + else to_chat(src, "Module isn't activated.") + else to_chat(src, "Module isn't activated") + installed_modules() return TRUE @@ -1404,8 +1476,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(lamp_intensity == 0) //We'll skip intensity of 2, since every mob already has such a see-darkness range, so no much need for it. lamp_intensity = 4 + else //Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. lamp_intensity = (lamp_intensity + 2) % (lamp_max + 2) + to_chat(src, span_notice("[lamp_intensity > 2 ? "Headlamp power set to Level [lamp_intensity * 0.5]" : "Headlamp disabled"].")) update_headlamp() @@ -1416,9 +1490,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( lamp_intensity = 0 lamp_cooldown = cooldown == BORG_LAMP_CD_RESET ? 0 : max(world.time + cooldown, lamp_cooldown) set_light_on(FALSE) + else - set_light_range((lamp_intensity + (on_fire ? fire_light_modificator : 0)) * 0.5) + set_light_range((lamp_intensity + (on_fire ? fire_light_modificator : 0)) - 2) set_light_on(TRUE) + else set_light_on(FALSE) @@ -1433,9 +1509,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/deconstruct() var/turf/T = get_turf(src) + if((modtype != "Clockwork" || !mmi.clock) && isclocker(src)) to_chat(src, span_warning("With body torn into pieces, your mind got free from evil cult!")) SSticker.mode.remove_clocker(mind, FALSE) + if(robot_suit) robot_suit.forceMove(T) robot_suit.l_leg.forceMove(T) @@ -1459,6 +1537,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( robot_suit.head.flash2 = null robot_suit.head = null robot_suit.update_icon(UPDATE_OVERLAYS) + else new /obj/item/robot_parts/robot_suit(T) new /obj/item/robot_parts/l_leg(T) @@ -1469,55 +1548,72 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( new /obj/item/robot_parts/r_arm(T) new /obj/item/robot_parts/head(T) var/b + for(b=0, b!=2, b++) var/obj/item/flash/F = new /obj/item/flash(T) F.burn_out() + if(cell) //Sanity check. cell.forceMove(T) cell = null + drop_hat() qdel(src) /mob/living/silicon/robot/Move(atom/newloc, direct = NONE, glide_size_override = 0, update_dir = TRUE) var/oldLoc = src.loc . = ..() + if(.) if(camera && oldLoc != src.loc) GLOB.cameranet.updatePortableCamera(src.camera) + if(module) if(module.type == /obj/item/robot_module/janitor) var/turf/tile = loc if(stat != DEAD && isturf(tile)) var/floor_only = TRUE + for(var/A in tile) if(iseffect(A)) var/obj/effect/check = A + if(check.is_cleanable()) var/obj/effect/decal/cleanable/blood/B = check + if(istype(B) && B.off_floor) floor_only = FALSE + else qdel(B) + else if(isitem(A)) var/obj/item/cleaned_item = A cleaned_item.clean_blood() + else if(ishuman(A)) var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.body_position == LYING_DOWN) if(cleaned_human.head) cleaned_human.head.clean_blood() cleaned_human.update_inv_head() + if(cleaned_human.wear_suit) cleaned_human.wear_suit.clean_blood() cleaned_human.update_inv_wear_suit() + else if(cleaned_human.w_uniform) cleaned_human.w_uniform.clean_blood() cleaned_human.update_inv_w_uniform() + if(cleaned_human.shoes) cleaned_human.shoes.clean_blood() cleaned_human.update_inv_shoes() + cleaned_human.clean_blood() to_chat(cleaned_human, span_danger("[src] cleans your face!")) + if(floor_only) tile.clean_blood() return @@ -1526,9 +1622,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(emagged) if(mmi) qdel(mmi) + explosion(src.loc,1,2,4,flame_range = 2, cause = src) + else explosion(src.loc,-1,0,2, cause = src) + gib() return @@ -1562,6 +1661,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( set src = usr var/obj/item/W = get_active_hand() + if(W) W.attack_self(src) @@ -1571,13 +1671,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/SetLockdown(state = TRUE) if(isclocker(src)) return + // They stay locked down if their wire is cut. if(wires?.is_cut(WIRE_BORG_LOCKED)) state = TRUE + if(state) throw_alert(ALERT_LOCKED, /atom/movable/screen/alert/locked) + else clear_alert(ALERT_LOCKED) + set_lockcharge(state) @@ -1585,11 +1689,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/set_lockcharge(new_lockcharge) if(new_lockcharge == lockcharge) return + . = lockcharge lockcharge = new_lockcharge + if(lockcharge) if(!.) ADD_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT) + else if(.) REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, LOCKED_BORG_TRAIT) @@ -1604,6 +1711,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/list/choices = list() var/choice + if(length(module?.borg_skins) > 1) for(var/skin in module.borg_skins) var/image/skin_image = image(icon = icon, icon_state = module.borg_skins[skin]) @@ -1612,9 +1720,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( choice = show_radial_menu(src, src, choices, require_near = TRUE) cut_overlays() + if(choice) icon_state = module.borg_skins[choice] transform_animation(module.borg_skins[choice]) + else icon_state = module.default_skin transform_animation(module.default_skin, TRUE) @@ -1627,8 +1737,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( Immobilize(5 SECONDS) say("Загрузка модуля...") setDir(SOUTH) + for(var/i in 1 to 4) playsound(loc, pick('sound/items/drill_use.ogg', 'sound/items/jaws_cut.ogg', 'sound/items/jaws_pry.ogg', 'sound/items/welder.ogg', 'sound/items/ratchet.ogg'), 50, TRUE, -1) + flick("[animated_icon]_transform", src) to_chat(src, span_notice("Your icon has been set[default?" by default":""]. You now require a reset module to change it.")) addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/silicon/robot, complete_loading)), 5 SECONDS) @@ -1640,6 +1752,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) if(!connected_ai) return + switch(notifytype) if(ROBOT_NOTIFY_AI_CONNECTED) //New Cyborg to_chat(connected_ai, "

[span_notice("NOTICE - New cyborg connection detected: [name]")]
") @@ -1670,6 +1783,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ) if(suiciding) return ..() + return STATUS_UPDATE_NONE @@ -1681,7 +1795,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/emp_act(severity) if(emp_protection) return + ..() + switch(severity) if(1) disable_component("comms", 160) @@ -1691,11 +1807,14 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /mob/living/silicon/robot/proc/set_connected_ai(new_ai) if(connected_ai == new_ai) return + . = connected_ai connected_ai = new_ai + if(.) var/mob/living/silicon/ai/old_ai = . old_ai.connected_robots -= src + if(connected_ai) connected_ai.connected_robots |= src @@ -1743,6 +1862,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( visible_message(span_danger("The [P.name] gets reflected by [src]!"), span_userdanger("The [P.name] gets reflected by [src]!")) P.reflect_back(src) return -1 + return ..(P) @@ -1772,6 +1892,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ..(loc) var/rnum = rand(1,1000) var/borgname = "[eprefix] ERT [rnum]" + name = borgname custom_name = borgname real_name = name @@ -1780,8 +1901,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( mind.set_original_mob(src) mind.assigned_role = SPECIAL_ROLE_ERT mind.special_role = SPECIAL_ROLE_ERT + if(!(mind in SSticker.minds)) SSticker.minds += mind + SSticker.mode.ert += mind @@ -1841,8 +1964,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( module.add_subsystems_and_actions(src) status_flags &= ~CANPUSH addtimer(CALLBACK(module, TYPE_PROC_REF(/obj/item/robot_module, update_cells)), 1 SECONDS) + if(radio) qdel(radio) + radio = new /obj/item/radio/borg/ert/specops(src) radio.recalculateChannels() playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) @@ -1852,13 +1977,16 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( visible_message(span_danger("The [P.name] gets reflected by [src]!"), span_userdanger("The [P.name] gets reflected by [src]!")) P.reflect_back(src) return -1 + return ..(P) /mob/living/silicon/robot/destroyer/borg_icons() if(base_icon == "") base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/destroyer/mobility)) icon_state = "[base_icon]-roll" + else icon_state = base_icon add_overlay("[base_icon]-shield") @@ -1868,10 +1996,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( var/eyes_olay if(isclocker(src) && SSticker.mode.power_reveal) eyes_olay = "eyes-[base_icon]-clocked" + else eyes_olay = "eyes-[base_icon]" + if(eyes_olay) add_overlay(eyes_olay) + return @@ -1883,14 +2014,17 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( ..() var/brute = 1000 var/burn = 1000 + var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) for(var/datum/robot_component/borked_part in borked_parts) brute = borked_part.brute_damage burn = borked_part.electronics_damage borked_part.installed = 1 borked_part.wrapped = new borked_part.external_type + if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. + borked_part.heal_damage(brute,burn) borked_part.install() @@ -1943,8 +2077,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( /// Used in `robot.dm` when the user presses "Q" by default. /mob/living/silicon/robot/proc/on_drop_hotkey_press() var/obj/item/gripper/G = module_active + if(istype(G) && G.gripped_item) G.drop_gripped_item() // if the active module is a gripper, try to drop its held item. + else uneq_active() // else unequip the module and put it back into the robot's inventory. return @@ -1956,6 +2092,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(makes_sound) audible_message(span_warning("[src] sounds an alarm! \"SYSTEM ERROR: Module 3 OFFLINE.\"")) playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, TRUE) + to_chat(src, span_userdanger("SYSTEM ERROR: Module 3 OFFLINE.")) if(health < 0) @@ -1963,6 +2100,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(makes_sound) audible_message(span_warning("[src] sounds an alarm! \"SYSTEM ERROR: Module 2 OFFLINE.\"")) playsound(loc, 'sound/machines/warning-buzzer.ogg', 60, TRUE) + to_chat(src, span_userdanger("SYSTEM ERROR: Module 2 OFFLINE.")) if(health < -50) @@ -1970,6 +2108,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( if(makes_sound) audible_message(span_warning("[src] sounds an alarm! \"CRITICAL ERROR: All modules OFFLINE.\"")) playsound(loc, 'sound/machines/warning-buzzer.ogg', 75, TRUE) + to_chat(src, span_userdanger("CRITICAL ERROR: All modules OFFLINE.")) /mob/living/silicon/robot/can_see_reagents() @@ -1988,6 +2127,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list( visible_message(span_warning("The power warning light on [span_name("[src]")] flashes urgently."), span_warning("You announce you are operating in low power mode.")) playsound(loc, 'sound/machines/buzz-two.ogg', 50, FALSE) + else to_chat(src, span_warning("You can only use this emote when you're out of charge.")) diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 86c55c3945c..19dd0e9a65e 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -5,21 +5,28 @@ /mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) if(HAS_TRAIT(src, TRAIT_GODMODE)) return 0 + var/amount = 0 + for(var/V in components) var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones amount += C.brute_damage + return amount /mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) if(HAS_TRAIT(src, TRAIT_GODMODE)) - return 0 + return FALSE + var/amount = 0 for(var/V in components) var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones amount += C.electronics_damage + return amount @@ -36,8 +43,10 @@ ) if(amount > 0) take_overall_damage(amount, 0, blocked, forced, updating_health, used_weapon, sharp, silent, affect_robotic) + else heal_overall_damage(amount, 0, updating_health, FALSE, affect_robotic) + return STATUS_UPDATE_HEALTH @@ -54,42 +63,52 @@ ) if(amount > 0) take_overall_damage(0, amount, blocked, forced, updating_health, used_weapon, sharp, silent, affect_robotic) + else heal_overall_damage(0, amount, updating_health, FALSE, affect_robotic) + return STATUS_UPDATE_HEALTH /mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) var/list/datum/robot_component/parts = list() + for(var/V in components) var/datum/robot_component/C = components[V] if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) parts += C + return parts /mob/living/silicon/robot/proc/get_missing_components() var/list/datum/robot_component/parts = list() + for(var/V in components) var/datum/robot_component/C = components[V] if(C.installed == 0) parts += C + return parts /mob/living/silicon/robot/proc/get_damageable_components() var/list/rval = new + for(var/V in components) var/datum/robot_component/C = components[V] if(C.installed == 1) rval += C + return rval /mob/living/silicon/robot/proc/get_armour() if(!LAZYLEN(components)) - return 0 + return FALSE + var/datum/robot_component/C = components["armour"] if(C && C.installed == 1) return C - return 0 + + return FALSE /mob/living/silicon/robot/heal_organ_damage( @@ -101,8 +120,10 @@ ) . = STATUS_UPDATE_NONE var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + if(!LAZYLEN(parts)) return . + var/datum/robot_component/picked = pick(parts) . |= picked.heal_damage(brute, burn, updating_health) @@ -148,7 +169,7 @@ . = STATUS_UPDATE_NONE var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - if(!length(parts)) + if(!LAZYLEN(parts)) return . while(parts.len && (brute > 0 || burn > 0) ) @@ -185,16 +206,18 @@ brute = abs(brute) burn = abs(burn) + if(!forced) brute *= ((100 - clamp(blocked + get_blocking_resistance(brute, BRUTE, null, sharp, used_weapon), 0, 100)) / 100) brute *= get_incoming_damage_modifier(brute, BRUTE, null, sharp, used_weapon) burn *= ((100 - clamp(blocked + get_blocking_resistance(burn, BURN, null, sharp, used_weapon), 0, 100)) / 100) burn *= get_incoming_damage_modifier(burn, BURN, null, sharp, used_weapon) - if(brute <= 0 && burn <= 0) + + if(!brute && !burn) return . var/list/datum/robot_component/parts = get_damageable_components() - if(!length(parts)) + if(!LAZYLEN(parts)) return . var/datum/robot_component/armour/armour = get_armour() diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index f643acd3da5..20d497fb649 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -3,36 +3,49 @@ if(body_position != LYING_DOWN) M.do_attack_animation(src, ATTACK_EFFECT_DISARM) var/obj/item/I = get_active_hand() + if(I) uneq_active() - visible_message("[M] disarmed [src]!", "[M] has disabled [src]'s active module!") + visible_message(span_danger("[M] disarmed [src]!"), span_userdanger("[M] has disabled [src]'s active module!")) add_attack_logs(M, src, "alien disarmed") + else Stun(4 SECONDS) step(src, get_dir(M,src)) add_attack_logs(M, src, "Alien pushed over") - visible_message("[M] forces back [src]!", "[M] forces back [src]!") + visible_message(span_danger("[M] forces back [src]!"), span_userdanger("[M] forces back [src]!")) + playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1) + else ..() + return /mob/living/silicon/robot/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime shock - flash_eyes(3, affect_silicon = TRUE) - var/stunprob = M.powerlevel * 7 + 10 - if(prob(stunprob) && M.powerlevel >= 8) - adjustBruteLoss(M.powerlevel * rand(6,10)) + . = ..() + + if(!.) //successful slime shock + return + + flash_eyes(3, affect_silicon = TRUE) + var/stunprob = M.powerlevel * 7 + 10 + + if(prob(stunprob) && M.powerlevel >= 8) + adjustBruteLoss(M.powerlevel * rand(6,10)) var/damage = rand(1, 3) if(M.age_state.age != SLIME_BABY) damage = rand(20 + M.age_state.damage, 40 + M.age_state.damage) + else damage = rand(5, 35) + damage = round(damage / 2) // borgs recieve half damage adjustBruteLoss(damage) - return + + return . /mob/living/silicon/robot/attack_hand(mob/living/carbon/human/user) add_fingerprint(user) @@ -43,7 +56,7 @@ cell.add_fingerprint(user) cell.forceMove_turf() user.put_in_active_hand(cell, ignore_anim = FALSE) - to_chat(user, "You remove \the [cell].") + to_chat(user, span_notice("You remove \the [cell].")) cell = null var/datum/robot_component/C = components["power cell"] C.installed = 0 @@ -54,7 +67,6 @@ if(!opened) if(..()) // hulk attack spark_system.start() - spawn(0) - step_away(src, user, 15) - sleep(3) - step_away(src, user, 15) + step_away(src, user, 15) + sleep(3) + step_away(src, user, 15) diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 950722af8cc..54d7f7e1f75 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -11,14 +11,17 @@ var/choice = tgui_input_list(user, "Would you like to change colour or mode?", name, list("Colour","Mode")) if(!choice) return + switch(choice) if("Colour") select_colour(user) if("Mode") if(mode == 1) mode = 2 + else mode = 1 + to_chat(user, "Changed printing mode to '[mode == 2 ? "Rename Paper" : "Write Paper"]'") playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) @@ -28,12 +31,13 @@ // see code\modules\paperwork\paper.dm line 62 /obj/item/pen/multi/robopen/proc/RenamePaper(mob/user as mob,obj/paper as obj) - if( !user || !paper ) + if(!user || !paper) return var/n_name = tgui_input_text(user, "What would you like to label the paper?", "Paper Labelling", max_length = MAX_NAME_LEN) if(!Adjacent(user) || !n_name) return + paper.name = "paper - [n_name]" add_fingerprint(user) return @@ -52,18 +56,17 @@ /obj/item/form_printer/afterattack(atom/target, mob/living/user, flag, params) - if(!target || !flag) return - if(istype(target,/obj/structure/table)) + if(istype(target, /obj/structure/table)) deploy_paper(get_turf(target)) /obj/item/form_printer/attack_self(mob/user as mob) deploy_paper(get_turf(src)) /obj/item/form_printer/proc/deploy_paper(var/turf/T) - T.visible_message("\The [src.loc] dispenses a sheet of crisp white paper.") + T.visible_message(span_notice("\The [src.loc] dispenses a sheet of crisp white paper.")) new /obj/item/paper(T) diff --git a/code/modules/mob/living/silicon/robot/robot_module_actions.dm b/code/modules/mob/living/silicon/robot/robot_module_actions.dm index aff796406ec..b47bac42cb0 100644 --- a/code/modules/mob/living/silicon/robot/robot_module_actions.dm +++ b/code/modules/mob/living/silicon/robot/robot_module_actions.dm @@ -1,19 +1,24 @@ /datum/action/innate/robot_sight var/sight_mode = null + icon_icon = 'icons/obj/decals.dmi' button_icon_state = "securearea" /datum/action/innate/robot_sight/Activate() var/mob/living/silicon/robot/R = owner + R.sight_mode |= sight_mode R.update_sight() - active = 1 + + active = TRUE /datum/action/innate/robot_sight/Deactivate() var/mob/living/silicon/robot/R = owner + R.sight_mode &= ~sight_mode R.update_sight() - active = 0 + + active = FALSE /datum/action/innate/robot_sight/xray name = "X-ray Vision" diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 9e0f51636c5..8b84ed89c77 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -28,13 +28,15 @@ if(modules) for(var/obj/O in modules) O.emp_act(severity) + if(emag) emag.emp_act(severity) + ..() -/obj/item/robot_module/New() - ..() +/obj/item/robot_module/Initialize(mapload) + . = ..() add_default_robot_items() emag = new /obj/item/toy/sword(src) emag.name = "Placeholder Emag Item" @@ -48,10 +50,17 @@ /obj/item/robot_module/proc/add_default_robot_items() modules += new /obj/item/flash/cyborg(src) +/obj/item/robot_module/proc/on_apply(mob/living/silicon/robot/robot) + return TRUE + +/obj/item/robot_module/proc/set_appearance(mob/living/silicon/robot/robot) + return TRUE + /obj/item/robot_module/proc/fix_modules() for(var/obj/item/I in modules) ADD_TRAIT(I, TRAIT_NODROP, CYBORG_ITEM_TRAIT) I.mouse_opacity = MOUSE_OPACITY_OPAQUE + if(emag) ADD_TRAIT(emag, TRAIT_NODROP, CYBORG_ITEM_TRAIT) emag.mouse_opacity = MOUSE_OPACITY_OPAQUE @@ -114,6 +123,7 @@ /obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module var/list/temp_list = modules modules = list() + for(var/obj/O in temp_list) if(!QDELETED(O)) //so items getting deleted don't stay in module list and haunt you modules += O @@ -140,6 +150,7 @@ /obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) add_verb(R, subsystems) + for(var/A in module_actions) var/datum/action/act = new A() act.Grant(R) @@ -147,9 +158,11 @@ /obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) remove_verb(R, subsystems) + for(var/datum/action/A in R.module_actions) A.Remove(R) qdel(A) + R.module_actions.Cut() // Return true in an overridden subtype to prevent normal removal handling @@ -179,42 +192,40 @@ /obj/item/robot_module/standard/New() ..() - modules += new /obj/item/extinguisher/mini(src) // for firefighting, and propulsion in space + modules += new /obj/item/screwdriver/cyborg(src) //added for minor works + modules += new /obj/item/wirecutters/cyborg(src) //addded to be able cut at least its own placed wires and rods modules += new /obj/item/crowbar/cyborg(src) - // sec - modules += new /obj/item/restraints/handcuffs/cable/zipties(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/weldingtool(src) //added instead of upgraded version modules += new /obj/item/melee/baton/telescopic(src) // for minimal possablity to execute sec part of the module and also for tests - // janitorial - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/restraints/handcuffs/cable/zipties(src) + modules += new /obj/item/flash/cyborg(src) modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) // test if will be in active usage and become op to be cutted out later - // service - modules += new /obj/item/instrument/piano_synth(src) // added for minimal service part - // eng + modules += new /obj/item/soap/nanotrasen(src) modules += new /obj/item/stack/sheet/metal/cyborg(src) modules += new /obj/item/stack/sheet/glass/cyborg(src) // regular glass for simplest works on broken window replacement modules += new /obj/item/stack/cable_coil/cyborg(src) - modules += new /obj/item/stack/rods/cyborg(src) - modules += new /obj/item/stack/tile/plasteel/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) //added for minor works - modules += new /obj/item/weldingtool(src) //added instead of upgraded version - modules += new /obj/item/wirecutters/cyborg(src) //addded to be able cut at least its own placed wires and rods - // mining - modules += new /obj/item/pickaxe/drill/cyborg(src) // instead of the pickaxe the worst tool for mining anywhere but killing someone with it - modules += new /obj/item/mining_scanner/cyborg(src) // instead of advanced scanner, we have mining module already - modules += new /obj/item/storage/bag/ore/cyborg(src) - // med modules += new /obj/item/healthanalyzer(src) modules += new /obj/item/reagent_containers/borghypo/basic(src) - modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way modules += new /obj/item/handheld_defibrillator(src) // test if will be in active usage and become op to be cutted out later, instead of salbutomol + modules += new /obj/item/extinguisher/mini(src) // for firefighting, and propulsion in space + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/roller_holder(src) // for taking the injured to medbay without worsening their injuries or leaving a blood trail the whole way + modules += new /obj/item/pickaxe/drill/cyborg(src) // instead of the pickaxe the worst tool for mining anywhere but killing someone with it + modules += new /obj/item/mining_scanner/cyborg(src) // instead of advanced scanner, we have mining module already + modules += new /obj/item/storage/bag/ore/cyborg(src) + modules += new /obj/item/stack/rods/cyborg(src) + modules += new /obj/item/stack/tile/plasteel/cyborg(src) + modules += new /obj/item/instrument/piano_synth(src) // added for minimal service part emag = new /obj/item/melee/energy/sword/cyborg(src) fix_modules() handle_storages() +/obj/item/robot_module/standard/add_default_robot_items() + return + /obj/item/robot_module/standard/respawn_consumable(mob/living/silicon/robot/R) var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules C.reagents.add_reagent("cleaner", 3) @@ -238,33 +249,43 @@ ) has_transform_animation = TRUE +/obj/item/robot_module/medical/on_apply(mob/living/silicon/robot/robot) + if(robot.camera && ("Robots" in robot.camera.network)) + LAZYADD(robot.camera.network, "Medical") + + robot.status_flags &= ~CANPUSH + robot.see_reagents = TRUE + + return TRUE + /obj/item/robot_module/medical/New() ..() + modules += new /obj/item/extinguisher/mini(src) modules += new /obj/item/healthanalyzer/advanced(src) modules += new /obj/item/robotanalyzer(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/twohanded/shockpaddles/borg(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) modules += new /obj/item/reagent_containers/borghypo(src) - modules += new /obj/item/reagent_containers/glass/beaker/large(src) - modules += new /obj/item/reagent_containers/dropper(src) - modules += new /obj/item/reagent_containers/syringe(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) - modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) - modules += new /obj/item/stack/medical/splint/cyborg(src) - modules += new /obj/item/stack/nanopaste/cyborg(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/twohanded/shockpaddles/borg(src) + modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/flash/cyborg(src) modules += new /obj/item/scalpel/laser/laser1(src) modules += new /obj/item/hemostat(src) modules += new /obj/item/retractor(src) + modules += new /obj/item/circular_saw(src) modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/stack/medical/splint/cyborg(src) + modules += new /obj/item/stack/nanopaste/cyborg(src) + modules += new /obj/item/reagent_containers/glass/beaker/large(src) + modules += new /obj/item/reagent_containers/dropper(src) + modules += new /obj/item/reagent_containers/syringe(src) modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) + modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/roller_holder(src) modules += new /obj/item/rlf(src) emag = new /obj/item/reagent_containers/borghypo/emagged(src) // emagged med. cyborg gets a special hypospray. @@ -284,12 +305,17 @@ /obj/item/robot_module/medical/unemag() for(var/obj/item/twohanded/shockpaddles/borg/defib in modules) defib.emag_act() + return ..() +/obj/item/robot_module/medical/add_default_robot_items() + return + /obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) if(emag) var/obj/item/reagent_containers/spray/PS = emag PS.reagents.add_reagent("sacid", 2) + ..() /obj/item/robot_module/engineering @@ -312,8 +338,17 @@ ) has_transform_animation = TRUE +/obj/item/robot_module/engineering/on_apply(mob/living/silicon/robot/robot) + if(robot.camera && ("Robots" in robot.camera.network)) + LAZYADD(robot.camera.network, "Engineering") + + ADD_TRAIT(robot, TRAIT_NEGATES_GRAVITY, ROBOT_TRAIT) + + return TRUE + /obj/item/robot_module/engineering/New() ..() + modules += new /obj/item/flash/cyborg(src) modules += new /obj/item/rcd/borg(src) modules += new /obj/item/rpd(src) modules += new /obj/item/extinguisher(src) @@ -341,6 +376,9 @@ fix_modules() handle_storages() +/obj/item/robot_module/engineering/add_default_robot_items() + return + /obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed) var/obj/item/gripper/G = locate(/obj/item/gripper) in modules if(G) @@ -363,6 +401,29 @@ ) has_transform_animation = TRUE +/obj/item/robot_module/security/on_apply(mob/living/silicon/robot/robot) + if(!robot.weapons_unlock) + var/count_secborgs = 0 + + for(var/mob/living/silicon/robot/silicon in GLOB.alive_mob_list) + if(silicon == robot) + continue + + if(silicon.stat != DEAD && silicon.module && istype(silicon.module, /obj/item/robot_module/security)) + count_secborgs++ + + var/max_secborgs = 2 + if(GLOB.security_level == SEC_LEVEL_GREEN) + max_secborgs = 1 + + if(count_secborgs >= max_secborgs) + to_chat(robot, span_warning("There are too many Security cyborgs active. Please choose another module.")) + return FALSE + + robot.status_flags &= ~CANPUSH + + return TRUE + /obj/item/robot_module/security/New() ..() modules += new /obj/item/restraints/handcuffs/cable/zipties(src) @@ -426,6 +487,11 @@ ) has_transform_animation = TRUE +/obj/item/robot_module/butler/on_apply(mob/living/silicon/robot/robot) + robot.see_reagents = TRUE + + return TRUE + /obj/item/robot_module/butler/New() ..() @@ -474,8 +540,10 @@ if(emag) var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag B.reagents.add_reagent("beer2", 2) + var/obj/item/reagent_containers/spray/pestspray/spray = locate() in modules spray?.reagents.add_reagent("pestkiller", 3) + ..() /obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) @@ -499,9 +567,11 @@ R.add_language(LANGUAGE_MOTH, 1) /obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed) - var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules + var/obj/item/storage/bag/tray/cyborg/T = locate() in modules + if(istype(T)) T.drop_inventory(R) + var/obj/item/gripper/service/G = locate() in modules if(G) G.drop_gripped_item(silent = TRUE) @@ -527,6 +597,12 @@ ) has_transform_animation = TRUE +/obj/item/robot_module/miner/on_apply(mob/living/silicon/robot/robot) + if(robot.camera && ("Robots" in robot.camera.network)) + LAZYADD(robot.camera.network, "Mining Outpost") + + return TRUE + /obj/item/robot_module/miner/New() ..() modules += new /obj/item/storage/bag/ore/cyborg(src) @@ -551,6 +627,7 @@ if(!istype(D, /obj/item/pickaxe/drill/cyborg/diamond)) qdel(D) modules -= D // Remove it from this list so it doesn't get added in the rebuild. + modules += new /obj/item/pickaxe/drill/cyborg/diamond(src) rebuild() @@ -559,15 +636,19 @@ for(var/obj/item/pickaxe/drill/cyborg/diamond/drill in modules) qdel(drill) modules -= drill + modules += new /obj/item/pickaxe/drill/cyborg(src) rebuild() + return ..() /obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) if(component_id == "KA modkits") for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) W.melee_attack_chain(user, D) + return TRUE + return ..() /obj/item/robot_module/deathsquad @@ -581,6 +662,13 @@ borg_skins = list("Deathsquad" = "nano_bloodhound") has_transform_animation = TRUE +/obj/item/robot_module/deathsquad/on_apply(mob/living/silicon/robot/robot) + var/mob/living/silicon/robot/deathsquad/death = new(get_turf(robot)) + robot.mind?.transfer_to(death) + qdel(robot) + + return TRUE + /obj/item/robot_module/deathsquad/New() ..() modules += new /obj/item/melee/energy/sword/cyborg(src) @@ -599,6 +687,12 @@ borg_skins = list("Syndicate Bloodhound" = "syndie_bloodhound") has_transform_animation = TRUE +/obj/item/robot_module/syndicate/on_apply(mob/living/silicon/robot/robot) + robot.spawn_syndicate_borgs(robot, "Bloodhound", get_turf(robot)) + qdel(robot) + + return TRUE + /obj/item/robot_module/syndicate/New() ..() modules += new /obj/item/melee/energy/sword/cyborg(src) @@ -621,40 +715,50 @@ borg_skins = list("Syndicate Medical" = "syndi-medi") has_transform_animation = TRUE +/obj/item/robot_module/syndicate_medical/on_apply(mob/living/silicon/robot/robot) + robot.spawn_syndicate_borgs(robot, "Medical", get_turf(robot)) + qdel(robot) + + return TRUE + /obj/item/robot_module/syndicate_medical/New() ..() + modules += new /obj/item/extinguisher/mini(src) modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/twohanded/shockpaddles/borg(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) modules += new /obj/item/reagent_containers/borghypo/syndicate(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) - modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) - modules += new /obj/item/stack/medical/splint/cyborg(src) - modules += new /obj/item/stack/nanopaste/cyborg(src) + modules += new /obj/item/gun/medbeam(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/twohanded/shockpaddles/borg(src) + modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/flash/cyborg(src) modules += new /obj/item/scalpel/laser/laser1(src) modules += new /obj/item/hemostat(src) modules += new /obj/item/retractor(src) + modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) modules += new /obj/item/bonesetter(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - modules += new /obj/item/gun/medbeam(src) - modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon + modules += new /obj/item/gripper/nuclear(src) modules += new /obj/item/card/emag(src) modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/bodyanalyzer/borg/syndicate(src) + modules += new /obj/item/stack/medical/splint/cyborg(src) + modules += new /obj/item/stack/nanopaste/cyborg(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) + modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) + modules += new /obj/item/reagent_scanner/adv(src) modules += new /obj/item/pinpointer/operative(src) modules += new /obj/item/pinpointer/nukeop(src) - modules += new /obj/item/gripper/nuclear(src) + modules += new /obj/item/roller_holder(src) emag = null fix_modules() handle_storages() +/obj/item/robot_module/syndicate_medical/add_default_robot_items() + return + /obj/item/robot_module/syndicate_saboteur name = "Syndicate Saboteur" name_disguise = "Engineering" @@ -663,26 +767,33 @@ borg_skins = list("Syndicate Saboteur" = "syndi-engi") has_transform_animation = TRUE +/obj/item/robot_module/syndicate_saboteur/on_apply(mob/living/silicon/robot/robot) + robot.spawn_syndicate_borgs(robot, "Saboteur", get_turf(robot)) + qdel(src) + + return TRUE + /obj/item/robot_module/syndicate_saboteur/New() ..() - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/flash/cyborg(src) + modules += new /obj/item/rcd/borg/syndicate(src) + modules += new /obj/item/rpd(src) modules += new /obj/item/t_scanner(src) modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/melee/energy/sword/cyborg(src) modules += new /obj/item/card/emag(src) - modules += new /obj/item/borg_chameleon(src) + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gripper/nuclear(src) + modules += new /obj/item/extinguisher(src) modules += new /obj/item/pinpointer/operative(src) modules += new /obj/item/pinpointer/nukeop(src) - modules += new /obj/item/gripper/nuclear(src) + modules += new /obj/item/borg_chameleon(src) modules += new /obj/item/stack/sheet/metal/cyborg(src) modules += new /obj/item/stack/sheet/glass/cyborg(src) modules += new /obj/item/stack/sheet/rglass/cyborg(src) @@ -694,6 +805,9 @@ fix_modules() handle_storages() +/obj/item/robot_module/syndicate_sabateur/add_default_robot_items() + return + /obj/item/robot_module/destroyer name = "Destroyer" module_type = "Malf" @@ -705,6 +819,13 @@ borg_skins = list("Destroyer" = "droidcombat") has_transform_animation = TRUE +/obj/item/robot_module/destroyer/on_apply(mob/living/silicon/robot/robot) + var/mob/living/silicon/robot/destroyer/destroy = new(get_turf(robot)) + robot.mind?.transfer_to(destroy) + qdel(robot) + + return TRUE + /obj/item/robot_module/destroyer/New() ..() @@ -717,6 +838,7 @@ modules += new /obj/item/gripper/nuclear(src) modules += new /obj/item/pinpointer(src) emag = new /obj/item/gun/energy/pulse/destroyer/annihilator(src) + fix_modules() @@ -728,6 +850,11 @@ borg_skins = list("ERT-GAMMA" = "ertgamma") has_transform_animation = TRUE +/obj/item/robot_module/combat/on_apply(mob/living/silicon/robot/robot) + robot.status_flags &= ~CANPUSH + + return TRUE + /obj/item/robot_module/combat/New() ..() modules += new /obj/item/gun/energy/immolator/multi/cyborg(src) // primary weapon, strong at close range (ie: against blob/terror/xeno), but consumes a lot of energy per shot. @@ -743,6 +870,7 @@ modules += new /obj/item/gripper/nuclear(src) modules += new /obj/item/pinpointer(src) emag = null + fix_modules() @@ -755,12 +883,17 @@ default_skin = "xenoborg" borg_skins = list("Xenoborg" = "xenoborg") +/obj/item/robot_module/hunter/on_apply(mob/living/silicon/robot/robot) + robot.modtype = "Xeno-Hu" + + return TRUE + /obj/item/robot_module/hunter/add_default_robot_items() return /obj/item/robot_module/hunter/New() ..() - modules += new /obj/item/melee/energy/alien/claws(src) + modules += new /obj/item/melee/energy/alien_claws(src) modules += new /obj/item/flash/cyborg/alien(src) var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) S.reagents.add_reagent("cryogenic_liquid",250) //nerfed to sleeptoxin to make it less instant drop. @@ -782,32 +915,39 @@ name = "Drone" module_type = "Engineer" +/obj/item/robot_module/drone/on_apply(mob/living/silicon/robot/robot) + var/mob/living/silicon/robot/drone/drone = new(get_turf(robot)) + robot.mind?.transfer_to(drone) + qdel(robot) + + return TRUE + /obj/item/robot_module/drone/New() ..() - modules += new /obj/item/weldingtool/largetank/cyborg(src) modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/extinguisher(src) modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) modules += new /obj/item/soap(src) - modules += new /obj/item/t_scanner(src) modules += new /obj/item/rpd(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) modules += new /obj/item/stack/sheet/wood/cyborg(src) - modules += new /obj/item/stack/sheet/rglass/cyborg(src) modules += new /obj/item/stack/tile/wood/cyborg(src) - modules += new /obj/item/stack/rods/cyborg(src) - modules += new /obj/item/stack/tile/plasteel/cyborg(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/floor_painter(src) modules += new /obj/item/stack/sheet/metal/cyborg(src) modules += new /obj/item/stack/sheet/glass/cyborg(src) - modules += new /obj/item/floor_painter(src) + modules += new /obj/item/stack/sheet/rglass/cyborg(src) modules += new /obj/item/stack/cable_coil/cyborg(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/extinguisher(src) + modules += new /obj/item/stack/rods/cyborg(src) + modules += new /obj/item/stack/tile/plasteel/cyborg(src) fix_modules() handle_storages() @@ -829,13 +969,20 @@ name = "Cogscarab" module_type = "Cogscarab" +/obj/item/robot_module/cogscarab/on_apply(mob/living/silicon/robot/robot) + var/mob/living/silicon/robot/cogscarab/cogscarab = new(get_turf(robot)) + robot.mind?.transfer_to(cogscarab) + qdel(robot) + + return TRUE + /obj/item/robot_module/cogscarab/Initialize() . = ..() - modules += new /obj/item/weldingtool/experimental/brass(src) modules += new /obj/item/screwdriver/brass(src) - modules += new /obj/item/wrench/brass(src) - modules += new /obj/item/crowbar/brass(src) modules += new /obj/item/wirecutters/brass(src) + modules += new /obj/item/crowbar/brass(src) + modules += new /obj/item/wrench/brass(src) + modules += new /obj/item/weldingtool/experimental/brass(src) modules += new /obj/item/multitool/brass(src) modules += new /obj/item/gripper/cogscarab(src) modules += new /obj/item/stack/sheet/brass/cyborg(src) @@ -862,6 +1009,18 @@ default_skin = "cyborg" borg_skins = list("cyborg" = "cyborg") +/obj/item/robot_module/clockwork/on_apply(mob/living/silicon/robot/robot) + robot.status_flags &= ~CANPUSH + QDEL_NULL(robot.mmi) + + robot.mmi = new /obj/item/mmi/robotic_brain/clockwork(src) + + return TRUE + +/obj/item/robot_module/clockwork/set_appearance(mob/living/silicon/robot/robot) + robot.icon = 'icons/mob/clockwork_mobs.dmi' + robot.icon_state = "cyborg" + /obj/item/robot_module/clockwork/Initialize() . = ..() modules += new /obj/item/clockwork/clockslab(src) @@ -889,7 +1048,7 @@ return /obj/item/robot_module/clockwork/handle_death(mob/living/silicon/robot/R, gibbed) - var/obj/item/gripper/cogscarab/G = locate(/obj/item/gripper/cogscarab) in modules + var/obj/item/gripper/cogscarab/G = locate() in modules G?.drop_gripped_item(silent = TRUE) /obj/item/robot_module/ninja @@ -897,56 +1056,62 @@ name_disguise = "Service" module_type = "ninja" +/obj/item/robot_module/ninja/on_apply(mob/living/silicon/robot/robot) + var/mob/living/silicon/robot/syndicate/saboteur/ninja/ninja = new(get_turf(robot)) + robot.mind?.transfer_to(ninja) + qdel(robot) + + return TRUE + /obj/item/robot_module/ninja/New() ..() - // Ниндзя штучки - modules += new /obj/item/gun/energy/shuriken_emitter/borg(src) modules += new /obj/item/melee/energy_katana/borg(src) - modules += new /obj/item/pinpointer/ninja(src) // Почему бы и да - // Инструменты - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/gun/energy/shuriken_emitter/borg(src) modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/stack/sheet/metal/cyborg(src) - modules += new /obj/item/stack/sheet/glass/cyborg(src) - modules += new /obj/item/stack/sheet/rglass/cyborg(src) - modules += new /obj/item/stack/rods/cyborg(src) - // Наручники + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/reagent_containers/borghypo/upgraded/super(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/twohanded/shockpaddles/borg(src) modules += new /obj/item/restraints/handcuffs/cable/zipties(src) - // Мед. инструменты + modules += new /obj/item/gripper/universal(src) + modules += new /obj/item/flash/cyborg(src) modules += new /obj/item/scalpel/laser/laser1(src) modules += new /obj/item/hemostat(src) modules += new /obj/item/retractor(src) + modules += new /obj/item/circular_saw(src) modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) + modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) + modules += new /obj/item/rcd/borg/syndicate(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/FixOVein(src) modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/healthanalyzer/advanced(src) modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/twohanded/shockpaddles/borg(src) - modules += new /obj/item/handheld_defibrillator(src) modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo/upgraded/super(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced/cyborg(src) - modules += new /obj/item/stack/medical/ointment/advanced/cyborg(src) - + modules += new /obj/item/stack/sheet/metal/cyborg(src) + modules += new /obj/item/stack/sheet/glass/cyborg(src) + modules += new /obj/item/stack/sheet/rglass/cyborg(src) + modules += new /obj/item/stack/rods/cyborg(src) + modules += new /obj/item/pinpointer/ninja(src) // Почему бы и да var/obj/item/borg_chameleon/cham_proj = new /obj/item/borg_chameleon(src) cham_proj.disguise = "maximillion" modules += cham_proj emag = null + fix_modules() handle_storages() +/obj/item/robot_module/ninja/add_default_robot_items() + return //checks whether this item is a module of the robot it is located in. /obj/item/proc/is_robot_module() @@ -969,8 +1134,10 @@ /datum/robot_energy_storage/New(var/obj/item/robot_module/R = null) if(!energy) energy = max_energy + if(R) R.storages |= src + return /datum/robot_energy_storage/proc/use_charge(amount) @@ -978,7 +1145,9 @@ energy -= amount if (energy == 0) return TRUE + return TRUE + else return FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index fdc18586f1f..d44ea5b03e9 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -2,8 +2,10 @@ . = ..() if(.) return TRUE + if(ionpulse()) return TRUE + return FALSE @@ -17,6 +19,7 @@ if(movement_type & (FLYING|FLOATING) && !(old_movement_type & (FLYING|FLOATING))) if(locate(/obj/item/borg/upgrade/vtec) in upgrades) remove_movespeed_modifier(/datum/movespeed_modifier/robot_vtec_upgrade) + if(ionpulse_on) add_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) @@ -26,6 +29,7 @@ if(old_movement_type & (FLYING|FLOATING) && !(movement_type & (FLYING|FLOATING))) if(locate(/obj/item/borg/upgrade/vtec) in upgrades) add_movespeed_modifier(/datum/movespeed_modifier/robot_vtec_upgrade) + if(ionpulse_on) remove_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade) diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index 9081a09ef01..b0dce160943 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -26,8 +26,8 @@ Your cyborg LMG will slowly produce ammunition from your power supply, and your operative pinpointer will find and locate fellow nuclear operatives. \ Help the operatives secure the disk at all costs!
" -/mob/living/silicon/robot/syndicate/New(loc) - ..() +/mob/living/silicon/robot/syndicate/Initialize(mapload) + . = ..() mmi = new /obj/item/mmi/robotic_brain/syndicate(src) mmi.icon_state = "sofia" @@ -39,14 +39,14 @@ if(is_taipan(z)) radio = new /obj/item/radio/borg/syndicate/taipan(src) + else radio = new /obj/item/radio/borg/syndicate(src) radio.recalculateChannels() - spawn(5) - if(playstyle_string) - to_chat(src, playstyle_string) + if(playstyle_string) + addtimer(CALLBACK(GLOBAL_PROC, /proc/to_chat, src, playstyle_string), 5 DECISECONDS) playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) @@ -128,14 +128,18 @@ set name = "Toggle Chameleon Projector" set desc = "Change your appearance to a Nanotrasen cyborg. Costs power to use and maintain." set category = "Saboteur" + if(!cham_proj) for(var/obj/item/borg_chameleon/C in contents) cham_proj = C + for(var/obj/item/borg_chameleon/C in module.contents) cham_proj = C + if(!cham_proj) to_chat(src, "Error : No chameleon projector system found.") return + cham_proj.attack_self(src) /mob/living/silicon/robot/syndicate/saboteur/verb/set_mail_tag() @@ -149,13 +153,13 @@ mail_destination = 0 return - to_chat(src, "You configure your internal beacon, tagging yourself for delivery to '[tag]'.") + to_chat(src, span_notice("You configure your internal beacon, tagging yourself for delivery to '[tag]'.")) mail_destination = GLOB.TAGGERLOCATIONS.Find(tag) //Auto flush if we use this verb inside a disposal chute. var/obj/machinery/disposal/D = src.loc if(istype(D)) - to_chat(src, "\The [D] acknowledges your signal.") + to_chat(src, span_notice("\The [D] acknowledges your signal.")) D.flush_count = D.flush_every_ticks return @@ -169,20 +173,24 @@ /mob/living/silicon/robot/syndicate/saboteur/attack_hand() if(cham_proj) cham_proj.disrupt(src) + ..() /mob/living/silicon/robot/syndicate/saboteur/ex_act() if(cham_proj) cham_proj.disrupt(src) + ..() /mob/living/silicon/robot/syndicate/saboteur/emp_act() ..() + if(cham_proj) cham_proj.disrupt(src) /mob/living/silicon/robot/syndicate/saboteur/bullet_act() if(cham_proj) cham_proj.disrupt(src) + ..() diff --git a/code/modules/mob/living/silicon/robot/update_status.dm b/code/modules/mob/living/silicon/robot/update_status.dm index 2d67160c119..1d230affe2c 100644 --- a/code/modules/mob/living/silicon/robot/update_status.dm +++ b/code/modules/mob/living/silicon/robot/update_status.dm @@ -12,28 +12,35 @@ ..() update_headlamp() return + if(stat != DEAD) if(health <= -maxHealth) //die only once death() update_headlamp(TRUE, 0) return + if(!is_component_functioning("actuator") || !is_component_functioning("power cell") || HAS_TRAIT(src, TRAIT_KNOCKEDOUT) || getOxyLoss() > maxHealth * 0.5) if(stat != UNCONSCIOUS) set_stat(UNCONSCIOUS) - update_headlamp(TRUE, 0) + else if(stat != CONSCIOUS) set_stat(CONSCIOUS) - update_headlamp(FALSE, 0) + update_icons() + update_headlamp(TRUE, 0) + else - if(health > 0) + if(health) update_revive() var/mob/dead/observer/ghost = get_ghost() + if(ghost) - to_chat(ghost, "Your cyborg shell has been repaired, re-enter if you want to continue! (Verbs -> Ghost -> Re-enter corpse)") - ghost << sound('sound/effects/genetics.ogg') + to_chat(ghost, "[span_ghostalert("Your cyborg shell has been repaired, re-enter if you want to continue!")] (Verbs -> Ghost -> Re-enter corpse)") + playsound(ghost, 'sound/effects/genetics.ogg', 50, TRUE) + add_misc_logs(src, "revived, trigger reason: [reason]") + ..() diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 06714b5a316..62c4b4b530a 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -50,6 +50,8 @@ diag_hud_set_status() diag_hud_set_health() + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) + RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, PROC_REF(alarm_triggered)) RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, PROC_REF(alarm_cancelled)) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 70bdf1172f2..2c7a527d58f 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -156,7 +156,10 @@ adjustBruteLoss(bloss) /mob/living/simple_animal/blob_act(obj/structure/blob/B) - adjustBruteLoss(20) + var/result = ..() + if(result) + adjustBruteLoss(20) + return result /mob/living/simple_animal/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect) if(!no_effect && !visual_effect_icon && melee_damage_upper) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 2199f6168da..d6c4e093865 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -24,6 +24,9 @@ light_system = MOVABLE_LIGHT + hud_type = /datum/hud/bot + + var/obj/machinery/bot_core/bot_core = null var/bot_core_type = /obj/machinery/bot_core var/list/users = list() //for dialog updates @@ -218,6 +221,8 @@ bot_core = new bot_core_type(src) addtimer(CALLBACK(src, PROC_REF(add_bot_filter)), 3 SECONDS) + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) + prepare_huds() for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) @@ -240,6 +245,9 @@ /mob/living/simple_animal/bot/can_strip() return FALSE +/mob/living/simple_animal/bot/can_unarmed_attack() + return on + /mob/living/simple_animal/bot/med_hud_set_health() return diag_hud_set_bothealth() //we use a different hud diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index baccfd4f241..c2cf432cfdf 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -293,9 +293,7 @@ ejectpai() -/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/bot/cleanbot/OnUnarmedAttack(atom/A) if(istype(A,/obj/effect/decal/cleanable)) start_clean(A) else diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 361a97b2184..6b7b2378837 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -595,9 +595,7 @@ lasercolor = "r" -/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A) - if(!on || !can_unarmed_attack()) - return +/mob/living/simple_animal/bot/ed209/OnUnarmedAttack(atom/A) if(iscarbon(A)) var/mob/living/carbon/C = A if(C.staminaloss < 110 || arrest_type && !baton_delayed) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 2dc92554766..3f12a46f25f 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -471,9 +471,7 @@ ..() -/mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/bot/floorbot/OnUnarmedAttack(atom/A) if(isturf(A)) repair(A) else if(istype(A,/obj/item/stack/tile/plasteel)) diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index 865dc05a854..937b9932a3b 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -118,12 +118,12 @@ arrived.Weaken(4 SECONDS) -/mob/living/simple_animal/bot/secbot/griefsky/UnarmedAttack(atom/A) //like secbots its only possible with admin intervention - if(!on || !can_unarmed_attack()) +/mob/living/simple_animal/bot/secbot/griefsky/OnUnarmedAttack(atom/atom) //like secbots its only possible with admin intervention + if(!iscarbon(atom)) return - if(iscarbon(A)) - var/mob/living/carbon/C = A - sword_attack(C) + + var/mob/living/carbon/carbon = atom + sword_attack(carbon) /mob/living/simple_animal/bot/secbot/griefsky/bullet_act(obj/item/projectile/P) //so uncivilized diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index eb6296117a1..ec507703105 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -139,9 +139,7 @@ ..() -/mob/living/simple_animal/bot/honkbot/UnarmedAttack(atom/A) - if(!on || !can_unarmed_attack()) - return +/mob/living/simple_animal/bot/honkbot/OnUnarmedAttack(atom/A) if(iscarbon(A)) var/mob/living/carbon/C = A if(emagged <= 1) diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 49b6e53e178..6aa3304f30f 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -498,9 +498,7 @@ return TRUE //If a valid medicine option for the patient exists, they require treatment -/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/bot/medbot/OnUnarmedAttack(atom/A) if(iscarbon(A)) var/mob/living/carbon/C = A patient = C diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 61659f90974..4d72b9f5d95 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -979,9 +979,7 @@ unload() -/mob/living/simple_animal/bot/mulebot/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/bot/mulebot/OnUnarmedAttack(atom/A) if(isturf(A) && isturf(loc) && loc.Adjacent(A) && load) unload(get_dir(loc, A)) else diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 9f9c4ed393c..327910850bd 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -322,9 +322,7 @@ ..() -/mob/living/simple_animal/bot/secbot/UnarmedAttack(atom/A) - if(!on || !can_unarmed_attack()) - return +/mob/living/simple_animal/bot/secbot/OnUnarmedAttack(atom/A) if(iscarbon(A)) var/mob/living/carbon/C = A if((C.staminaloss < 110 || arrest_type) && !baton_delayed) diff --git a/code/modules/mob/living/simple_animal/bot/syndicate.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm index c8bcbcd2c71..a7a43bd133d 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm @@ -209,10 +209,8 @@ return -/mob/living/simple_animal/bot/ed209/syndicate/UnarmedAttack(atom/A) - if(!on || !can_unarmed_attack()) - return - shootAt(A) +/mob/living/simple_animal/bot/ed209/syndicate/OnUnarmedAttack(atom/A) + return shootAt(A) /mob/living/simple_animal/bot/ed209/syndicate/start_cuffing(mob/living/carbon/C) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 3f4e0f0ccac..089dc0ef118 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -127,6 +127,7 @@ force_threshold = 11 playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." + hud_type = /datum/hud/construct/armoured /mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things AIStatus = AI_ON @@ -177,6 +178,7 @@ retreat_distance = 2 //AI wraiths will move in and out of combat playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." tts_seed = "Kelthuzad" + hud_type = /datum/hud/construct/wraith /mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things AIStatus = AI_ON @@ -228,6 +230,7 @@ use magic missile, repair allied constructs (by clicking on them), \ and, most important of all, create new constructs by producing soulstones to capture souls, \ and shells to place those soulstones into." + hud_type = /datum/hud/construct/builder /mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? @@ -309,6 +312,7 @@ attack_sound = 'sound/weapons/punch4.ogg' force_threshold = 11 construct_type = "behemoth" + hud_type = /datum/hud/construct/armoured var/energy = 0 var/max_energy = 1000 @@ -341,6 +345,7 @@ retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ Bring those who still cling to this world of illusion back to the master so they may know Truth." + hud_type = /datum/hud/construct/harvester /mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) diff --git a/code/modules/mob/living/simple_animal/friendly/animals_named.dm b/code/modules/mob/living/simple_animal/friendly/animals_named.dm index a8b8f873064..cba5533f316 100644 --- a/code/modules/mob/living/simple_animal/friendly/animals_named.dm +++ b/code/modules/mob/living/simple_animal/friendly/animals_named.dm @@ -59,6 +59,8 @@ unique_pet = TRUE gold_core_spawnable = NO_SPAWN resting = TRUE + gender = FEMALE + tts_seed = "Widowmaker" /mob/living/simple_animal/pet/cat/birman/Crusher name = "Бедокур" //Не цель для воров diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 188ed783e26..81bfddd6c38 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -96,9 +96,7 @@ evolve_action.Grant(src) steal_blood_action.Grant(src) -/mob/living/simple_animal/diona/UnarmedAttack(atom/A) - if(!can_unarmed_attack()) - return +/mob/living/simple_animal/diona/OnUnarmedAttack(atom/A) if(isdiona(A) && (src in A.contents)) //can't attack your gestalt visible_message("[src] wiggles around a bit.") else diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 1b3978b8299..84c6470ba4e 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -25,6 +25,7 @@ turns_per_move = 10 mob_size = MOB_SIZE_SMALL gold_core_spawnable = FRIENDLY_SPAWN + hud_type = /datum/hud/corgi var/bark_sound = list('sound/creatures/dog_bark1.ogg','sound/creatures/dog_bark2.ogg') //Used in emote. var/bark_emote = list("ла%(ет,ют)%.", "гавка%(ет,ют)%.") // used in emote. var/growl_sound = list('sound/creatures/dog_grawl1.ogg','sound/creatures/dog_grawl2.ogg') //Used in emote. diff --git a/code/modules/mob/living/simple_animal/gondolas/gondola.dm b/code/modules/mob/living/simple_animal/gondolas/gondola.dm new file mode 100644 index 00000000000..e12dbe4e15a --- /dev/null +++ b/code/modules/mob/living/simple_animal/gondolas/gondola.dm @@ -0,0 +1,74 @@ +#define GONDOLA_HEIGHT pick(list("gondola_body_long", "gondola_body_medium", "gondola_body_short")) +#define GONDOLA_COLOR pick(list("A87855", "915E48", "683E2C")) +#define GONDOLA_MOUSTACHE pick(list("gondola_moustache_large", "gondola_moustache_small")) +#define GONDOLA_EYES pick(list("gondola_eyes_close", "gondola_eyes_far")) + +/mob/living/simple_animal/pet/gondola + name = "gondola" + real_name = "gondola" + desc = "Гондола — бесшумный ходок. \ + Не имея рук, он воплощает даосский принцип у-вэй (недействие), а его улыбающееся \ + выражение лица показывает его полное и полное принятие мира таким, какой он есть." + icon = 'icons/mob/gondolas.dmi' + icon_state = "gondola" + icon_living = "gondola" + + maxHealth = 200 + health = 200 + faction = list("gandola") + response_help = "pets" + response_disarm = "bops" + response_harm = "kicks" + + //Gondolas aren't affected by cold. + unsuitable_atmos_damage = 0 + del_on_death = TRUE + + ///List of loot drops on death, since it deletes itself on death (like trooper). + loot = list( + /obj/effect/decal/cleanable/blood/gibs = 1, + ) + + ru_names = list( + NOMINATIVE = "гандола", + GENITIVE = "гандолы", + DATIVE = "гандоле", + ACCUSATIVE = "гандолу", + INSTRUMENTAL = "гандолой", + PREPOSITIONAL = "гандоле" + ) + + +/mob/living/simple_animal/pet/gondola/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_MUTE, INNATE_TRAIT) + create_gondola() + +/mob/living/simple_animal/pet/gondola/proc/create_gondola() + icon_state = null + icon_living = null + var/height = GONDOLA_HEIGHT + var/mutable_appearance/body_overlay = mutable_appearance(icon, height) + var/mutable_appearance/eyes_overlay = mutable_appearance(icon, GONDOLA_EYES) + var/mutable_appearance/moustache_overlay = mutable_appearance(icon, GONDOLA_MOUSTACHE) + body_overlay.color = ("#[GONDOLA_COLOR]") + + //Offset the face to match the Gondola's height. + switch(height) + if("gondola_body_medium") + eyes_overlay.pixel_y = -4 + moustache_overlay.pixel_y = -4 + if("gondola_body_short") + eyes_overlay.pixel_y = -8 + moustache_overlay.pixel_y = -8 + + cut_overlays(TRUE) + add_overlay(body_overlay) + add_overlay(eyes_overlay) + add_overlay(moustache_overlay) + + +#undef GONDOLA_HEIGHT +#undef GONDOLA_COLOR +#undef GONDOLA_MOUSTACHE +#undef GONDOLA_EYES diff --git a/code/modules/mob/living/simple_animal/gondolas/gondolapod.dm b/code/modules/mob/living/simple_animal/gondolas/gondolapod.dm new file mode 100644 index 00000000000..496605b4920 --- /dev/null +++ b/code/modules/mob/living/simple_animal/gondolas/gondolapod.dm @@ -0,0 +1,95 @@ +/mob/living/simple_animal/pet/gondola/gondolapod + name = "gondola" + real_name = "gondola" + desc = "Бесшумный ходок. Кажется, это сотрудник агентства доставки." + icon = 'icons/obj/supplypods.dmi' + icon_state = "gondola" + icon_living = "gondola" + SET_BASE_PIXEL(-16, -5) //2x2 sprite + layer = TABLE_LAYER //so that deliveries dont appear underneath it + + ///Boolean on whether the pod is currently open, and should appear such. + var/opened = FALSE + ///The supply pod attached to the gondola, that actually holds the contents of our delivery. + var/obj/structure/closet/supplypod/centcompod/linked_pod + + ///Static list of actions the gondola is given on creation, and taken away when it successfully delivers. + var/static/list/gondola_delivering_actions = list( + /datum/action/innate/deliver_gondola_package, + /datum/action/innate/check_gondola_contents, + ) + +/mob/living/simple_animal/pet/gondola/gondolapod/Initialize(mapload, pod) + linked_pod = pod || new(src) + name = linked_pod.name + desc = linked_pod.desc + if(!linked_pod.stay_after_drop || !linked_pod.opened) + grant_actions_by_list(gondola_delivering_actions) + return ..() + +/mob/living/simple_animal/pet/gondola/gondolapod/death(gibbed) + QDEL_NULL(linked_pod) //Will cause the open() proc for the linked supplypod to be called with the "broken" parameter set to true, meaning that it will dump its contents on death + return ..() + +/mob/living/simple_animal/pet/gondola/gondolapod/create_gondola() + return + +/mob/living/simple_animal/pet/gondola/gondolapod/update_overlays() + . = ..() + if(opened) + . += "[icon_state]_open" + +/mob/living/simple_animal/pet/gondola/gondolapod/examine(mob/user) + . = ..() + if (contents.len) + . += span_notice("Похоже, посылка еще не доставлена.") + else + . += span_notice("Судя по всему, доставку уже осуществили.") + +/mob/living/simple_animal/pet/gondola/gondolapod/setOpened() + opened = TRUE + layer = initial(layer) + update_appearance() + addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/, setClosed)), 5 SECONDS) + +/mob/living/simple_animal/pet/gondola/gondolapod/setClosed() + opened = FALSE + layer = OBJ_LAYER + update_appearance() + +///Opens the gondola pod and delivers its package, one-time use as it removes all delivery-related actions. +/datum/action/innate/deliver_gondola_package + name = "Доставить" + desc = "Откройте хранилище и освободите все содержимое, хранящееся внутри." + button_icon_state = "arrow" + +/datum/action/innate/deliver_gondola_package/Trigger(left_click) + . = ..() + if(!.) + return + + var/mob/living/simple_animal/pet/gondola/gondolapod/gondola_owner = owner + gondola_owner.linked_pod.open_pod(gondola_owner, forced = TRUE) + for(var/datum/action/actions as anything in gondola_owner.actions) + if(actions.type in gondola_owner.gondola_delivering_actions) + actions.Remove(gondola_owner) + return TRUE + +///Checks the contents of the gondola and lets them know what they're holding. +/datum/action/innate/check_gondola_contents + name = "Проверить содержимое" + desc = "Посмотрите, сколько предметов вы сейчас держите в капсуле." + button_icon_state = "storage" + +/datum/action/innate/check_gondola_contents/Trigger(left_click) + . = ..() + if(!.) + return + + var/mob/living/simple_animal/pet/gondola/gondolapod/gondola_owner = owner + var/total = gondola_owner.contents.len + if (total) + to_chat(gondola_owner, span_notice("You detect [total] object\s within your incredibly vast belly.")) + else + to_chat(gondola_owner, span_notice("A closer look inside yourself reveals... nothing.")) + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 44a5af40f2f..fec8fa967ce 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -59,6 +59,7 @@ regenerate_icons() AddComponent(/datum/component/swarming) AddElement(/datum/element/simple_flying) + AddElement(/datum/element/reagent_attack/bee) /mob/living/simple_animal/hostile/poison/bees/ComponentInitialize() AddComponent( \ @@ -156,14 +157,6 @@ return //no don't attack the goddamm box else . = ..() - if(. && isliving(target) && (!client || a_intent == INTENT_HARM)) - var/mob/living/L = target - if(L.reagents) - if(beegent) - beegent.reaction_mob(L, REAGENT_INGEST) - L.reagents.add_reagent(beegent.id, rand(1, 5)) - else - L.reagents.add_reagent("beetoxin", 5) /mob/living/simple_animal/hostile/poison/bees/proc/assign_reagent(datum/reagent/R) if(istype(R)) diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 55d751ee2b4..f6c657164bd 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -37,10 +37,10 @@ talk_sound = list('sound/creatures/spider_talk1.ogg', 'sound/creatures/spider_talk2.ogg') damaged_sound = list('sound/creatures/spider_attack1.ogg', 'sound/creatures/spider_attack2.ogg') gold_core_spawnable = HOSTILE_SPAWN - var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. var/busy = 0 footstep_type = FOOTSTEP_MOB_CLAW AI_delay_max = 0.5 SECONDS + hud_type = /datum/hud/simple_animal/spider /mob/living/simple_animal/hostile/poison/giant_spider/ComponentInitialize() AddComponent( \ @@ -49,15 +49,6 @@ cold_damage = 20, \ ) -/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() - // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. - . = ..() - if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) - var/mob/living/carbon/C = target - var/inject_target = pick(BODY_ZONE_CHEST, BODY_ZONE_HEAD) - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.reagents.add_reagent("spidertoxin", venom_per_bite) - /mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup(moving_direction, continuous_move) . = ..() // If we don't find any normal thing to use, attempt to use any nearby spider structure instead. @@ -77,10 +68,21 @@ health = 40 melee_damage_lower = 5 melee_damage_upper = 10 - venom_per_bite = 30 var/atom/cocoon_target var/fed = 0 +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/Initialize(mapload) + . = ..() + + AddElement( \ + /datum/element/reagent_attack, \ + "spidertoxin", \ + 30, \ + FALSE, \ + null, \ + list(BODY_ZONE_CHEST, BODY_ZONE_HEAD), \ + ) + //hunters have the most poison and move the fastest, so they can find prey /mob/living/simple_animal/hostile/poison/giant_spider/hunter desc = "Furry and dark purple, it makes you shudder to look at it. This one has sparkling purple eyes." @@ -91,9 +93,19 @@ health = 120 melee_damage_lower = 10 melee_damage_upper = 20 - venom_per_bite = 10 move_to_delay = 5 +/mob/living/simple_animal/hostile/poison/giant_spider/hunter/Initialize(mapload) + . = ..() + + AddElement( + /datum/element/reagent_attack, \ + "spidertoxin", \ + 10, \ + FALSE, \ + null, \ + list(BODY_ZONE_CHEST, BODY_ZONE_HEAD), \ + ) /mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. . = ..() diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm index cbcb20e1b22..79e7a3878a0 100644 --- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm @@ -93,7 +93,7 @@ return ..() -/mob/living/simple_animal/hostile/gorilla/hear_say(list/message_pieces, verb = "says", italics = FALSE, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) +/mob/living/simple_animal/hostile/gorilla/hear_say(list/message_pieces, verb = "says", italics = FALSE, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE, is_whisper = FALSE) if(client || !can_befriend || !ishuman(speaker) || speaker == src || incapacitated() || is_on_cooldown()) return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 32908854ec8..f25826fb2bd 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -171,16 +171,39 @@ if(!search_objects) . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - - for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) - if(can_see(targets_from, HM, vision_range)) + var/static/possible_targets = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod, /mob/living)) + for(var/HM in typecache_filter_list(range(vision_range, targets_from), possible_targets)) + if(targets_from.can_see(HM, vision_range)) . += HM else . = oview(vision_range, targets_from) if(retaliate_only) return . &= enemies // Remove all entries that aren't in enemies +/mob/living/simple_animal/hostile/can_see(atom/target, length) + if(!target || target.invisibility > see_invisible) + return FALSE + var/turf/current_turf = get_turf(src) + var/turf/target_turf = get_turf(target) + if(!current_turf || !target_turf) // nullspace + return FALSE + if(get_dist(current_turf, target_turf) > length) + return FALSE + if(current_turf == target_turf)//they are on the same turf, source can see the target + return TRUE + if(isliving(target) && (sight & SEE_MOBS))//if a mob sees mobs through walls, it always sees the target mob within line of sight + return TRUE + var/steps = 1 + current_turf = get_step_towards(current_turf, target_turf) + while(current_turf != target_turf) + if(steps > length) + return FALSE + if(IS_OPAQUE_TURF(current_turf)) + return FALSE + current_turf = get_step_towards(current_turf, target_turf) + steps++ + return TRUE + /mob/living/simple_animal/hostile/proc/FindTarget(list/possible_targets)//Step 2, filter down possible targets to things we actually care about if(QDELETED(src)) @@ -316,7 +339,7 @@ if(L in friends) return FALSE else - if((faction_check && !attack_same) || L.stat) + if((faction_check && !attack_same) || L.stat > stat_attack) return FALSE return TRUE @@ -463,7 +486,9 @@ SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) if(!client) mob_attack_logs += "[time_stamp()] Attacked [target] at [COORD(src)]" - return target.attack_animal(src) + var/result = target.attack_animal(src) + SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result) + return result /mob/living/simple_animal/hostile/proc/Aggro() diff --git a/code/modules/mob/living/simple_animal/hostile/illusion.dm b/code/modules/mob/living/simple_animal/hostile/illusion.dm index d3437761665..5282ea0e811 100644 --- a/code/modules/mob/living/simple_animal/hostile/illusion.dm +++ b/code/modules/mob/living/simple_animal/hostile/illusion.dm @@ -20,6 +20,11 @@ del_on_death = 1 +/mob/living/simple_animal/hostile/illusion/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_WET_IMMUNITY, INNATE_TRAIT) + + /mob/living/simple_animal/hostile/illusion/Life() ..() if(world.time > life_span) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/pet.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/pet.dm index 833ef4de31a..71c05d106ec 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/pet.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/pet.dm @@ -20,4 +20,5 @@ unique_pet = TRUE atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 2, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) gender = FEMALE + hud_type = /datum/hud/simple_animal/spider diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index 593dbc8434b..9f5dda6e667 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -88,6 +88,9 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list) lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE sight = SEE_TURFS|SEE_MOBS|SEE_OBJS + // HUD + hud_type = /datum/hud/simple_animal/spider + // AI aggression settings var/ai_target_method = TS_DAMAGE_SIMPLE diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/widow.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/widow.dm index 0e5b72c0b2f..cfe298d286c 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/widow.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/widow.dm @@ -34,6 +34,10 @@ tts_seed = "Karastamper" spider_intro_text = "Будучи Вдовой Ужаса, ваша цель - внести хаос на поле боя при помощи своих плевков, вы также смертоносны вблизи и с каждым укусом вводите в противников опасный яд. Несмотря на скорость и смертоносность, вы довольно хрупки, поэтому не стоит атаковать тяжело вооружённых противников!" +/mob/living/simple_animal/hostile/poison/terror_spider/widow/Initialize(mapload) + . = ..() + AddElement(/datum/element/reagent_attack/widow) + /mob/living/simple_animal/hostile/poison/terror_spider/widow/spider_specialattack(mob/living/carbon/human/L, poisonable) . = ..() if(!.) @@ -41,15 +45,6 @@ L.AdjustSilence(10 SECONDS) if(!poisonable) return TRUE - if(L.reagents.has_reagent("terror_black_toxin", 100)) - return TRUE - var/inject_target = pick(BODY_ZONE_CHEST, BODY_ZONE_HEAD) - if(HAS_TRAIT(L, TRAIT_INCAPACITATED) || L.can_inject(null, FALSE, inject_target, FALSE)) - L.reagents.add_reagent("terror_black_toxin", 33) // inject our special poison - visible_message(span_danger("[src] buries its long fangs deep into the [inject_target] of [target]!")) - else - L.reagents.add_reagent("terror_black_toxin", 20) - visible_message(span_danger("[src] pierces armour and buries its long fangs deep into the [inject_target] of [target]!")) if(!ckey && (!(target in enemies) || L.reagents.has_reagent("terror_black_toxin", 60))) step_away(src, L) step_away(src, L) diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 8e3c8d08245..82704ca80e9 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -840,7 +840,7 @@ used_radios += ears -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE, is_whisper = FALSE) if(speaker != src && prob(50)) parrot_hear(html_decode(multilingual_to_message(message_pieces))) ..() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 49f45d9499b..5d5286d9e71 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -9,6 +9,8 @@ universal_speak = 0 status_flags = CANPUSH + hud_type = /datum/hud/simple_animal + var/icon_living = "" var/icon_dead = "" var/icon_resting = "" @@ -369,12 +371,9 @@ /mob/living/simple_animal/say_quote(message) - var/verb = "says" - - if(speak_emote.len) - verb = pick(speak_emote) - - return verb + if(speak_emote?.len) + return get_verb(speak_emote) + return ..() /mob/living/simple_animal/proc/set_varspeed(var_value) @@ -697,6 +696,11 @@ /mob/living/simple_animal/Login() ..() SSmove_manager.stop_looping(src) // if mob is moving under ai control, then stop AI movement + toggle_ai(AI_OFF) + +/mob/living/simple_animal/Logout() + . = ..() + toggle_ai(AI_ON) /mob/living/simple_animal/say(message, verb = "says", sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE) diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 8679d64a699..f61609ffd48 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -1,15 +1,4 @@ -/mob/living/simple_animal/slime/say_quote(text, datum/language/speaking) - var/verb = "blorbles" - var/ending = copytext(text, length(text)) - - if(ending == "?") - verb = "inquisitively blorbles" - else if(ending == "!") - verb = "loudly blorbles" - - return verb - -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency, use_voice = TRUE, is_whisper = FALSE) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index 6b51b2320a1..997dc6e86ad 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -20,7 +20,10 @@ response_disarm = "shoos" response_harm = "stomps on" emote_see = list("jiggles", "bounces in place") - speak_emote = list("blorbles") + verb_say = "blorbles" + verb_ask = "inquisitively blorbles" + verb_exclaim = "loudly blorbles" + verb_yell = "loudly blorbles" bubble_icon = "slime" tts_seed = "Chen" @@ -40,6 +43,8 @@ footstep_type = FOOTSTEP_MOB_SLIME + hud_type = /datum/hud/slime + var/cores = 1 // the number of /obj/item/slime_extract's the slime has left inside var/mutation_chance = 30 // Chance of mutating, should be between 25 and 35 var/chance_reproduce = 80 diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index d4134aae515..0e9bd6c5f23 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -29,8 +29,7 @@ if(!client) return FALSE canon_client = client - GLOB.player_list |= src - GLOB.keyloop_list |= src + add_to_player_list() last_known_ckey = ckey update_Login_details() world.update_status() diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 44d97c84b3a..4ad2bd7fd1f 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -3,8 +3,7 @@ set_typing_indicator(FALSE) SStgui.on_logout(src) // Cleanup any TGUIs the user has open unset_machine() - GLOB.player_list -= src - GLOB.keyloop_list -= src + remove_from_player_list() log_access_out(src) add_game_logs("OWNERSHIP: [key_name(src)] is no longer owning mob [src]([src.type])") // `holder` is nil'd out by now, so we check the `admin_datums` array directly diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 2d71404ccba..02b62e4adf1 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1,7 +1,7 @@ /mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game. - GLOB.mob_list -= src - GLOB.dead_mob_list -= src - GLOB.alive_mob_list -= src + remove_from_mob_list() + remove_from_alive_mob_list() + remove_from_dead_mob_list() focus = null QDEL_NULL(hud_used) if(mind && mind.current == src) @@ -25,11 +25,11 @@ return ..() /mob/Initialize(mapload) - GLOB.mob_list += src + add_to_mob_list() if(stat == DEAD) - GLOB.dead_mob_list += src + add_to_dead_mob_list() else - GLOB.alive_mob_list += src + add_to_alive_mob_list() set_focus(src) prepare_huds() . = ..() @@ -344,8 +344,10 @@ DEFAULT_QUEUE_OR_CALL_VERB(VERB_CALLBACK(src, PROC_REF(run_examinate), A)) -/mob/proc/run_examinate(atom/A) - var/list/result = A.examine(src) +/mob/proc/run_examinate(atom/target) + var/list/result = target.examine(src) + SEND_SIGNAL(src, COMSIG_MOB_RUN_EXAMINATE, target, result) + to_chat(src, chat_box_examine(result.Join("\n")), MESSAGE_TYPE_INFO, confidential = TRUE) @@ -591,8 +593,9 @@ /mob/proc/get_status_tab_items() SHOULD_CALL_PARENT(TRUE) - var/list/status_tab_data = list() - return status_tab_data + . = list() + SEND_SIGNAL(src, COMSIG_MOB_GET_STATUS_TAB_ITEMS, .) + return . // facing verbs /mob/proc/canface() @@ -1000,6 +1003,17 @@ SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_alpha() +///Update the mouse pointer of the attached client in this mob +/mob/proc/update_mouse_pointer() + if(!client) + return + + if(client.mouse_pointer_icon != initial(client.mouse_pointer_icon))//only send changes to the client if theyre needed + client.mouse_pointer_icon = initial(client.mouse_pointer_icon) + + if(client.mouse_override_icon) + client.mouse_pointer_icon = client.mouse_override_icon + /mob/proc/set_vision_override(datum/vision_override/O) QDEL_NULL(vision_type) if(O) //in case of null @@ -1164,3 +1178,43 @@ GLOBAL_LIST_INIT(holy_areas, typecacheof(list( if(update) update_actionspeed() +/mob/proc/update_z(new_z) // 1+ to register, null to unregister + if(registered_z == new_z) + return + if(registered_z) + SSmobs.clients_by_zlevel[registered_z] -= src + if(isnull(client)) + registered_z = null + return + if(!new_z) + registered_z = new_z + return + //Figure out how many clients were here before + var/oldlen = SSmobs.clients_by_zlevel[new_z].len + SSmobs.clients_by_zlevel[new_z] += src + for(var/index in length(SSidlenpcpool.idle_mobs_by_zlevel[new_z]) to 1 step -1) //Backwards loop because we're removing (guarantees optimal rather than worst-case performance), it's fine to use .len here but doesn't compile on 511 + var/mob/living/simple_animal/animal = SSidlenpcpool.idle_mobs_by_zlevel[new_z][index] + if(animal) + if(!oldlen) + //Start AI idle if nobody else was on this z level before (mobs will switch off when this is the case) + animal.toggle_ai(AI_IDLE) + //If they are also within a close distance ask the AI if it wants to wake up + if(get_dist(get_turf(src), get_turf(animal)) < MAX_SIMPLEMOB_WAKEUP_RANGE) + animal.consider_wakeup() // Ask the mob if it wants to turn on it's AI + //They should clean up in destroy, but often don't so we get them here + else + SSidlenpcpool.idle_mobs_by_zlevel[new_z] -= animal + registered_z = new_z + +/mob/proc/track_z() + if(client || registered_z) // This is a temporary error tracker to make sure we've caught everything + var/turf/T = get_turf(src) + if(client && registered_z != T.z) + message_admins("[src] [ADMIN_FLW(src, "FLW")] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z]. If you could ask them how that happened and notify the coders, it would be appreciated.") + add_misc_logs(src, "Z-TRACKING: [src] has somehow ended up in Z-level [T.z] despite being registered in Z-level [registered_z].") + update_z(T.z) + else if(!client && registered_z) + add_misc_logs(src, "Z-TRACKING: [src] of type [src.type] has a Z-registration despite not having a client.") + update_z(null) + + diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index a76ad7796fa..66ecf8a68a2 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -93,7 +93,11 @@ var/list/datum/language/languages /// For reagents that grant language knowlege. var/list/temporary_languages - var/list/speak_emote = list("says") // Verbs used when speaking. Defaults to 'say' if speak_emote is null. + var/list/speak_emote = list() // Verbs used when speaking. Defaults to 'say' if speak_emote is null. + var/verb_say = "says" + var/verb_ask = "asks" + var/verb_exclaim = list("exclaims", "shouts") + var/verb_yell = "yells" /// Define emote default type, EMOTE_VISIBLE for seen emotes, EMOTE_AUDIBLE for heard emotes. var/emote_type = EMOTE_VISIBLE var/name_archive //For admin things like possession @@ -123,6 +127,8 @@ var/obj/item/clothing/mask/wear_mask = null //Carbon var/datum/hud/hud_used = null + /// Mob hud type + var/hud_type = /datum/hud hud_possible = list(SPECIALROLE_HUD) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 075c44d9ffd..fd397aea8e3 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -850,3 +850,14 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM return FALSE return TRUE + +/// Takes in an associated list (key `/datum/action` typepaths, value is the AI blackboard key) and handles granting the action and adding it to the mob's AI controller blackboard. +/// This is only useful in instances where you don't want to store the reference to the action on a variable on the mob. +/mob/proc/grant_actions_by_list(list/input) + if(length(input) <= 0) + return + + for(var/action in input) + var/datum/action/ability = new action(src) + ability.Grant(src) + diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm new file mode 100644 index 00000000000..d2e76b8f96f --- /dev/null +++ b/code/modules/mob/mob_lists.dm @@ -0,0 +1,90 @@ +///Adds the mob reference to the list and directory of all mobs. Called on Initialize(). +/mob/proc/add_to_mob_list() + GLOB.mob_list |= src + +///Removes the mob reference from the list and directory of all mobs. Called on Destroy(). +/mob/proc/remove_from_mob_list() + GLOB.mob_list -= src + +///Adds the mob reference to the list of all mobs alive. If mob is cliented, it adds it to the list of all living player-mobs. +/mob/proc/add_to_alive_mob_list() + if(QDELETED(src)) + return + GLOB.alive_mob_list |= src + if(client) + add_to_current_living_players() + +///Removes the mob reference from the list of all mobs alive. If mob is cliented, it removes it from the list of all living player-mobs. +/mob/proc/remove_from_alive_mob_list() + GLOB.alive_mob_list -= src + if(client) + remove_from_current_living_players() + +///Adds the mob reference to the list of all the dead mobs. If mob is cliented, it adds it to the list of all dead player-mobs. +/mob/proc/add_to_dead_mob_list() + if(QDELETED(src)) + return + GLOB.dead_mob_list |= src + if(client) + add_to_current_dead_players() + +///Remvoes the mob reference from list of all the dead mobs. If mob is cliented, it adds it to the list of all dead player-mobs. +/mob/proc/remove_from_dead_mob_list() + GLOB.dead_mob_list -= src + if(client) + remove_from_current_dead_players() + + +///Adds the cliented mob reference to the list of all player-mobs, besides to either the of dead or alive player-mob lists, as appropriate. Called on Login(). +/mob/proc/add_to_player_list() + SHOULD_CALL_PARENT(TRUE) + GLOB.player_list |= src + GLOB.keyloop_list |= src + if(stat == DEAD) + add_to_current_dead_players() + else + add_to_current_living_players() + +///Removes the mob reference from the list of all player-mobs, besides from either the of dead or alive player-mob lists, as appropriate. Called on Logout(). +/mob/proc/remove_from_player_list() + SHOULD_CALL_PARENT(TRUE) + GLOB.player_list -= src + GLOB.keyloop_list -= src + if(stat == DEAD) + remove_from_current_dead_players() + else + remove_from_current_living_players() + + +///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game. +/mob/proc/add_to_current_dead_players() + GLOB.dead_player_list |= src + +/mob/dead/observer/add_to_current_dead_players() + if(started_as_observer) + GLOB.current_observers_list |= src + return + return ..() + +/mob/dead/new_player/add_to_current_dead_players() + return + +///Removes the mob reference from either the list of dead player-mobs or from the list of observers, depending on how they joined the game. +/mob/proc/remove_from_current_dead_players() + GLOB.dead_player_list -= src + +/mob/dead/observer/remove_from_current_dead_players() + if(started_as_observer) + GLOB.current_observers_list -= src + return + return ..() + + +///Adds the cliented mob reference to the list of living player-mobs. If the mob is an antag, it adds it to the list of living antag player-mobs. +/mob/proc/add_to_current_living_players() + GLOB.alive_player_list |= src + +///Removes the mob reference from the list of living player-mobs. If the mob is an antag, it removes it from the list of living antag player-mobs. +/mob/proc/remove_from_current_living_players() + GLOB.alive_player_list -= src + diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm index 34adbed0045..50f0010cdf2 100644 --- a/code/modules/mob/mob_say.dm +++ b/code/modules/mob/mob_say.dm @@ -121,17 +121,23 @@ /mob/proc/say_quote(message, datum/language/speaking = null) - var/verb = "says" - var/ending = copytext(message, length(message)) - + var/ending = copytext_char(message, -1) if(speaking) - verb = genderize_decode(src, speaking.get_spoken_verb(ending)) - else - if(ending == "!") - verb = pick("exclaims", "shouts", "yells") - else if(ending == "?") - verb = "asks" - return verb + return genderize_decode(src, speaking.get_spoken_verb(ending)) + else if(ending == "!") + return get_verb(verb_exclaim) + else if(ending == "?") + return get_verb(verb_ask) + else if(copytext_char(message, -2) == "!!") + return get_verb(verb_yell) + return get_verb(verb_say) + +/mob/proc/get_verb(list/verbs) + if(!verbs) + return "" + if(!istype(verbs)) + return verbs + return pick(verbs) /// Transforms the speech emphasis mods from [/atom/movable/proc/say_emphasis] into the appropriate HTML tags #define ENCODE_HTML_EMPHASIS(input, char, html, varname) \ diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index 5e66d1a35f4..4f22cfe517f 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -47,7 +47,9 @@ mind.transfer_to(M) else M.key = key - + + SEND_SIGNAL(src, COMSIG_MOB_CHANGED_TYPE, M) + if(delete_old_mob) spawn(1) qdel(src) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 404cb514df6..5fba35858c8 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -16,7 +16,7 @@ if(flags & INITIALIZED) stack_trace("Warning: [src]([type]) initialized multiple times!") flags |= INITIALIZED - GLOB.mob_list += src + add_to_mob_list() return INITIALIZE_HINT_NORMAL /mob/new_player/proc/privacy_consent() diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 4cce405c45c..ba197d6d9d6 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -21,7 +21,7 @@ body_accessory = random_body_accessory(species, S.optional_body_accessory) if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) s_tone = random_skin_tone(species) - h_style = random_hair_style(gender, species, robohead) + h_style = random_hair_style(gender, S, robohead) f_style = random_facial_hair_style(gender, species, robohead) if(species in list(SPECIES_HUMAN, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_SKRELL, SPECIES_MACNINEPERSON, SPECIES_WRYN, SPECIES_VULPKANIN, SPECIES_VOX)) randomize_hair_color("hair") @@ -211,14 +211,12 @@ if(Y)\ I.Shift(NORTH, Y);\ -/datum/preferences/proc/update_preview_icon(var/for_observer=0) //seriously. This is horrendous. +/datum/preferences/proc/update_preview_icon(for_observer = FALSE) // seriously. This is horrendous. qdel(preview_icon_front) qdel(preview_icon_side) qdel(preview_icon) - var/g = "m" - if(gender == FEMALE) g = "f" - + var/gender_suffix = gender == FEMALE ? "f" : "m" var/icon/icobase var/datum/species/current_species = GLOB.all_species[species] @@ -228,6 +226,9 @@ if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race. var/mob/living/carbon/human/H = new + if(!H.dna) + H.dna = new + H.dna.species = current_species H.s_tone = s_tone H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive. @@ -243,14 +244,14 @@ else icobase = 'icons/mob/human_races/r_human.dmi' - preview_icon = new /icon(icobase, "torso_[g]") - preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY) + preview_icon = new /icon(icobase, "torso_[gender_suffix]") + preview_icon.Blend(new /icon(icobase, "groin_[gender_suffix]"), ICON_OVERLAY) var/head = "head" if(alt_head && current_species.bodyflags & HAS_ALT_HEADS) var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head] if(H.icon_state) head = H.icon_state - preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY) + preview_icon.Blend(new /icon(icobase, "[head]_[gender_suffix]"), ICON_OVERLAY) var/list/check_list = list( BODY_ZONE_CHEST, BODY_ZONE_PRECISE_GROIN, diff --git a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm deleted file mode 100644 index 9cf722e1449..00000000000 --- a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm +++ /dev/null @@ -1,8 +0,0 @@ -/datum/sprite_accessory/hair/wryn - icon = 'icons/mob/sprite_accessories/wryn/wryn_face.dmi' - species_allowed = list(SPECIES_WRYN) - glasses_over = 1 - -/datum/sprite_accessory/hair/wryn/wry_antennae_default - name = "Antennae" - icon_state = "antennae" diff --git a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_facial_hair.dm new file mode 100644 index 00000000000..1dcaba6dad6 --- /dev/null +++ b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_facial_hair.dm @@ -0,0 +1,25 @@ +/datum/sprite_accessory/facial_hair/wryn + icon = 'icons/mob/sprite_accessories/wryn/wryn_facial_hair.dmi' + species_allowed = list(SPECIES_WRYN) + unsuitable_gender = null + over_hair = TRUE + +/datum/sprite_accessory/facial_hair/wryn/default + name = "Default mane" + icon_state = "default" + +/datum/sprite_accessory/facial_hair/wryn/fluff + name = "Fluff mane" + icon_state = "fluff" + +/datum/sprite_accessory/facial_hair/wryn/sharp + name = "Sharp mane" + icon_state = "sharp" + +/datum/sprite_accessory/facial_hair/wryn/square + name = "Square mane" + icon_state = "square" + +/datum/sprite_accessory/facial_hair/wryn/round + name = "Round mane" + icon_state = "round" diff --git a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_hair.dm b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_hair.dm new file mode 100644 index 00000000000..5ec3493d7bf --- /dev/null +++ b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_hair.dm @@ -0,0 +1,36 @@ +/datum/sprite_accessory/hair/wryn + icon = 'icons/mob/sprite_accessories/wryn/wryn_head_accessories.dmi' + species_allowed = list(SPECIES_WRYN) + glasses_over = 1 + +/datum/sprite_accessory/hair/wryn/default + name = "Normal antennae" + icon_state = "antennae" + +/datum/sprite_accessory/hair/wryn/curvy + name = "Curvy antennae" + icon_state = "curvy" + +/datum/sprite_accessory/hair/wryn/nian + name = "Nian antennae" + icon_state = "moth" + +/datum/sprite_accessory/hair/wryn/perky + name = "Perky antennae" + icon_state = "perky" + +/datum/sprite_accessory/hair/wryn/sweep + name = "Sweep antennae" + icon_state = "sweep" + +/datum/sprite_accessory/hair/wryn/short + name = "Short antennae" + icon_state = "short" + +/datum/sprite_accessory/hair/wryn/long + name = "Long antennae" + icon_state = "long" + +/datum/sprite_accessory/hair/wryn/long + name = "Low Long antennae" + icon_state = "low_long" diff --git a/code/modules/newscaster/obj/newscaster.dm b/code/modules/newscaster/obj/newscaster.dm index 6677b469acb..b0c47a59881 100644 --- a/code/modules/newscaster/obj/newscaster.dm +++ b/code/modules/newscaster/obj/newscaster.dm @@ -76,7 +76,6 @@ /datum/job/pilot, /datum/job/brigdoc, /datum/job/mechanic, - /datum/job/barber, /datum/job/chaplain, /datum/job/ntnavyofficer, /datum/job/ntnavyofficer/field, diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index 6696c160477..d1b51b7a0c3 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -38,35 +38,11 @@
Кроме того, Раб соглашается передать право на владение своей душой отделу лояльности Вездесущего и полезного наблюдателя за человечеством.\
В случае, если передача души Раба невозможна, Раб вносит вместо неё залог.
Подписано,
[target]" - -/obj/item/paper/contract/employment/attack(mob/living/victim, mob/living/user, params, def_zone, skip_attack_anim = FALSE) - . = ..() - if(!ATTACK_CHAIN_SUCCESS_CHECK(.)) - return . - - var/deconvert = 0 - if(victim.mind == target && target.soulOwner != target) - if(user.mind && (user.mind.assigned_role == JOB_TITLE_LAWYER)) - deconvert = 60 - else if (user.mind && (user.mind.assigned_role == JOB_TITLE_HOP) || (user.mind.assigned_role == "Centcom Commander") || (user.mind.assigned_role == JOB_TITLE_JUDGE)) - deconvert = 40 - else if(user.mind && (user.mind.assigned_role == JOB_TITLE_CAPTAIN)) - deconvert = 25 - else - deconvert = 0.0001 - - if(prob(deconvert)) - victim.visible_message( - span_notice("Благодаря [user] [victim] вспоминает, что душа [victim] уже приобретена НаноТрейзен!"), - span_boldnotice("Вы чувствуете, как Ваша душа возвращается к её правомочному владельцу — НаноТрейзен."), - ) - victim.return_soul() - - /obj/item/paper/contract/infernal var/contractType = 0 resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/datum/mind/owner + var/datum/antagonist/devil/devilinfo + var/mob/living/carbon/human/owner icon_state = "evil_contract" /obj/item/paper/contract/infernal/power @@ -103,6 +79,7 @@ /obj/item/paper/contract/infernal/New(atom/loc, mob/living/nTarget, datum/mind/nOwner) ..() + devilinfo = nOwner.has_antag_datum(/datum/antagonist/devil) owner = nOwner target = nTarget update_text() @@ -122,56 +99,56 @@ info = "This shouldn't be seen. Error DEVIL:6" /obj/item/paper/contract/infernal/power/update_text(var/signature = "____________", blood = 0) - info = "
Contract for infernal power



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for power and physical strength. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for infernal power



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for power and physical strength. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/wealth/update_text(var/signature = "____________", blood = 0) - info = "
Contract for unlimited wealth



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for a pocket that never runs out of valuable resources. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for unlimited wealth



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for a pocket that never runs out of valuable resources. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/prestige/update_text(var/signature = "____________", blood = 0) - info = "
Contract for prestige



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for prestige and esteem among my peers. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for prestige



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for prestige and esteem among my peers. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/magic/update_text(var/signature = "____________", blood = 0) - info = "
Contract for magic



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for arcane abilities beyond normal human ability. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for magic



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for arcane abilities beyond normal human ability. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/revive/update_text(var/signature = "____________", blood = 0) - info = "
Contract for resurrection



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for resurrection and curing of all injuries. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for resurrection



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for resurrection and curing of all injuries. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/knowledge/update_text(var/signature = "____________", blood = 0) - info = "
Contract for knowledge



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for boundless knowledge. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for knowledge



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for boundless knowledge. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/friendship/update_text(var/signature = "____________", blood = 0) - info = "
Contract for friendship



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename], in exchange for true unconditional friendship. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for friendship



I, [target] of sound mind, do hereby willingly offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename], in exchange for true unconditional friendship. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else info += "[signature]" /obj/item/paper/contract/infernal/unwilling/update_text(var/signature = "____________", blood = 0) - info = "
Contract for slave



I, [target], hereby offer my soul to the infernal hells by way of the infernal agent [owner.devilinfo.truename]. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " + info = "
Contract for slave



I, [target], hereby offer my soul to the infernal hells by way of the infernal agent [devilinfo.info.truename]. I understand that upon my demise, my soul shall fall into the infernal hells, and my body may not be resurrected, cloned, or otherwise brought back to life. I also understand that this will prevent my brain from being used in an MMI.


Signed, " if(blood) info += "[signature]" else @@ -209,7 +186,7 @@ span_danger("You slice your wrist open and scrawl your name in blood."), ) if(ishuman(user)) - user.blood_volume = max(0, user.blood_volume - 100) + user.AdjustBlood(-100) /obj/item/paper/contract/infernal/proc/attempt_signature(mob/living/carbon/human/user, blood = 0) @@ -276,16 +253,22 @@ /obj/item/paper/contract/infernal/proc/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0) signed = 1 + if(!user.mind) - return 0 - if(user.mind.soulOwner != user.mind && user.mind.soulOwner.devilinfo) //They already sold their soul to someone else? - user.mind.soulOwner.devilinfo.remove_soul(user.mind) //Then they lose their claim. + return FALSE + + var/datum/antagonist/devil/devilinfo = user.mind.has_antag_datum(/datum/antagonist/devil) + if(user.mind.soulOwner != user.mind && devilinfo) //They already sold their soul to someone else? + devilinfo.remove_soul(user.mind) //Then they lose their claim. + user.mind.soulOwner = owner user.mind.damnation_type = contractType - owner.devilinfo.add_soul(user.mind) + devilinfo.add_soul(user.mind) + update_text(user.real_name, blood) - to_chat(user, "A profound emptiness washes over you as you lose ownership of your soul.") - to_chat(user, "This does NOT make you an antagonist if you were not already.") + to_chat(user, span_notice("A profound emptiness washes over you as you lose ownership of your soul.")) + to_chat(user, span_boldnotice("This does NOT make you an antagonist if you were not already.")) + return 1 /obj/item/paper/contract/infernal/power/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0) diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index 5cf60cd6afc..5a197dfc2d7 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -12,6 +12,8 @@ GLOBAL_LIST_EMPTY(PDAs) desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." icon = 'icons/obj/pda.dmi' icon_state = "pda" + lefthand_file = 'icons/mob/inhands/pda_lefthand.dmi' + righthand_file = 'icons/mob/inhands/pda_righthand.dmi' w_class = WEIGHT_CLASS_TINY item_flags = DENY_UI_BLOCKED slot_flags = ITEM_SLOT_ID|ITEM_SLOT_PDA|ITEM_SLOT_BELT @@ -79,8 +81,6 @@ GLOBAL_LIST_EMPTY(PDAs) var/obj/item/pda/chameleon_skin /// Custom job name used in chameleon PDA. var/fakejob - /// Our icon saved in the text format for TGUI usage - var/base64icon /// Custom PDA name used in update_name() var/custom_name /// Current PDA case @@ -105,8 +105,6 @@ GLOBAL_LIST_EMPTY(PDAs) GLOB.PDAs += src GLOB.PDAs = sortAtom(GLOB.PDAs) - base64icon = "[icon2base64(icon(icon, icon_state, frame = 1))]" - update_programs() if(default_cartridge) cartridge = new default_cartridge(src) @@ -354,16 +352,12 @@ GLOBAL_LIST_EMPTY(PDAs) /obj/item/pda/update_icon_state() if(chameleon_skin) icon_state = initial(chameleon_skin.icon_state) - base64icon = "[icon2base64(icon(icon, icon_state, frame = 1))]" else if(current_case?.new_icon_state) icon_state = current_case.new_icon_state - base64icon = "[icon2base64(icon(icon, icon_state, frame = 1))]" else if(current_painting) icon_state = current_painting["icon"] - base64icon = current_painting["base64"] else icon_state = initial(icon_state) - base64icon = "[icon2base64(icon(icon, icon_state, frame = 1))]" if(chameleon_skin) item_state = initial(chameleon_skin.item_state) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 29e7c8c8995..a49251cfbed 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -790,6 +790,10 @@ return ..() +/obj/machinery/power/apc/examine(mob/user) + . = ..() + if(in_range(src, user)) + . += "Alt-click to toggle locker.
Ctrl-click to toggle power.
" /obj/machinery/power/apc/AltClick(mob/user) var/mob/living/carbon/human/human = user diff --git a/code/modules/power/cable_coil.dm b/code/modules/power/cable_coil.dm index c51cb95ab69..c94392cfc22 100644 --- a/code/modules/power/cable_coil.dm +++ b/code/modules/power/cable_coil.dm @@ -5,6 +5,8 @@ singular_name = "cable" icon = 'icons/obj/engines_and_power/power.dmi' icon_state = "coil" + righthand_file = 'icons/mob/inhands/tools_righthand.dmi' + lefthand_file = 'icons/mob/inhands/tools_lefthand.dmi' item_state = "coil_red" belt_icon = "cable_coil" amount = MAXCOIL @@ -20,11 +22,21 @@ materials = list(MAT_METAL=10, MAT_GLASS=5) flags = CONDUCT slot_flags = ITEM_SLOT_BELT - item_state = "coil" attack_verb = list("whipped", "lashed", "disciplined", "flogged") usesound = 'sound/items/deconstruct.ogg' toolspeed = 1 + var/static/list/wire_colors = list( + WIRE_COLOR_BLUE = "blue", + WIRE_COLOR_CYAN = "cyan", + WIRE_COLOR_GREEN = "green", + WIRE_COLOR_ORANGE = "orange", + WIRE_COLOR_PINK = "pink", + WIRE_COLOR_RED = "red", + WIRE_COLOR_WHITE = "white", + WIRE_COLOR_YELLOW = "yellow" + ) + /obj/item/stack/cable_coil/Initialize(mapload, new_amount, merge = TRUE, cable_color = null) . = ..() @@ -59,7 +71,7 @@ icon_state = "coil2" else icon_state = "coil" - + item_state = wire_colors[color] /obj/item/stack/cable_coil/update_weight() if(amount == 1) diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 8617697874b..692dcab1425 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -23,6 +23,9 @@ /obj/item/stock_parts/cell/laser maxcharge = 1500 +/obj/item/stock_parts/cell/laser/gatling + maxcharge = 9000 + /obj/item/stock_parts/cell/get_cell() return src diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 598647c2b75..f8dc37b811a 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -14,6 +14,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne #define GRAV_NEEDS_PLASTEEL 2 #define GRAV_NEEDS_WRENCH 3 +#define BLOB_HITS_NEED 4 + // // Abstract Generator // @@ -27,6 +29,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne use_power = NO_POWER_USE resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | NO_MALF_EFFECT var/sprite_number = 0 + /// Number of successful blob hits + var/blob_hits = 0 /obj/machinery/gravity_generator/ex_act(severity) @@ -35,7 +39,8 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne /obj/machinery/gravity_generator/blob_act(obj/structure/blob/B) - if(prob(20)) + blob_hits++ + if(blob_hits >= BLOB_HITS_NEED) set_broken() diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index c3bc21edaa9..b84f766eb14 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -9,6 +9,7 @@ density = TRUE anchored = FALSE use_power = NO_POWER_USE + var/datum/looping_sound/port_gen/soundloop var/active = 0 var/power_gen = 5000 @@ -17,6 +18,14 @@ var/power_output = 1 var/base_icon = "portgen0" +/obj/machinery/power/port_gen/Initialize(mapload) + . = ..() + soundloop = new(list(src), active) + +/obj/machinery/power/port_gen/Destroy() + QDEL_NULL(soundloop) + return ..() + /obj/machinery/power/port_gen/proc/IsBroken() return (stat & (BROKEN|EMPED)) @@ -39,10 +48,12 @@ if(active && HasFuel() && !IsBroken() && anchored && powernet) add_avail(power_gen * power_output) UseFuel() + soundloop.start() else active = 0 handleInactive() update_icon(UPDATE_ICON_STATE) + soundloop.stop() /obj/machinery/power/powered() return TRUE //doesn't require an external power source diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 0ed692764d0..75371cdce4f 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -146,7 +146,7 @@ field_generator power level display /obj/machinery/field/generator/blob_act(obj/structure/blob/B) if(active) - return 0 + return FALSE else ..() diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index bcd6b2b01ab..61f5602019e 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -134,10 +134,6 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin master.toggle_power() investigate_log("was moved whilst active; it powered down.", INVESTIGATE_ENGINE) -/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B) - if(prob(50) && !QDELETED(src)) - qdel(src) - /obj/structure/particle_accelerator/update_icon_state() switch(construction_state) if(ACCELERATOR_UNWRENCHED, ACCELERATOR_WRENCHED) diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 2973373acc0..3954a0bb1d7 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -355,7 +355,7 @@ consume(user) -/obj/machinery/power/supermatter_shard/proc/get_integrity() +/obj/machinery/power/supermatter_shard/proc/get_internal_integrity() var/integrity = damage / explosion_point integrity = round(100 - integrity * 100) integrity = integrity < 0 ? 0 : integrity @@ -541,16 +541,16 @@ if(!air) return SUPERMATTER_ERROR - if(get_integrity() < 25) + if(get_internal_integrity() < 25) return SUPERMATTER_DELAMINATING - if(get_integrity() < 50) + if(get_internal_integrity() < 50) return SUPERMATTER_EMERGENCY - if(get_integrity() < 75) + if(get_internal_integrity() < 75) return SUPERMATTER_DANGER - if((get_integrity() < 100) || (air.temperature > CRITICAL_TEMPERATURE)) + if((get_internal_integrity() < 100) || (air.temperature > CRITICAL_TEMPERATURE)) return SUPERMATTER_WARNING if(air.temperature > (CRITICAL_TEMPERATURE * 0.8)) diff --git a/code/modules/projectiles/ammunition/energy.dm b/code/modules/projectiles/ammunition/energy.dm index 4f961b593bf..9a62d5890c8 100644 --- a/code/modules/projectiles/ammunition/energy.dm +++ b/code/modules/projectiles/ammunition/energy.dm @@ -18,6 +18,10 @@ muzzle_flash_color = LIGHT_COLOR_DARKRED select_name = "kill" +/obj/item/ammo_casing/energy/laser/light + projectile_type = /obj/item/projectile/beam/laser/light + delay = 0.9 + /obj/item/ammo_casing/energy/laser/cyborg //to balance cyborg energy cost seperately e_cost = 250 diff --git a/code/modules/projectiles/firing.dm b/code/modules/projectiles/firing.dm index d17d0e9141e..dbd7faf36b9 100644 --- a/code/modules/projectiles/firing.dm +++ b/code/modules/projectiles/firing.dm @@ -18,6 +18,7 @@ user.changeNext_move(CLICK_CD_RANGE) user.newtonian_move(get_dir(target, user)) update_icon() + SEND_SIGNAL(src, COMSIG_FIRE_CASING, target, user, firer_source_atom, randomspread, spread, zone_override, params, distro) return TRUE @@ -78,7 +79,7 @@ * If the user is holding a weapon in telekinesis grab, * use a starting location from the firer source */ - var/fire_from_tk_grab = !isnull(firer_source_atom) && user.tkgrabbed_objects[firer_source_atom] + var/fire_from_tk_grab = !isnull(firer_source_atom) && ismob(user) && user.tkgrabbed_objects[firer_source_atom] if (fire_from_tk_grab) curloc = get_turf(firer_source_atom) diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index b6e4a1fb8f1..f0a5ce3190a 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -14,7 +14,7 @@ throw_range = 5 force = 5 origin_tech = "combat=1" - needs_permit = 1 + needs_permit = TRUE attack_verb = list("struck", "hit", "bashed") pickup_sound = 'sound/items/handling/gun_pickup.ogg' drop_sound = 'sound/items/handling/gun_drop.ogg' diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index b3bc18b5fb7..40f2ee9cc98 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -14,6 +14,8 @@ var/modifystate = FALSE var/shaded_charge = FALSE //if this gun uses a stateful charge bar for more detail var/selfcharge = FALSE + /// Recharge rate if self-charging + var/recharge_rate = 100 var/can_charge = TRUE var/charge_sections = 4 var/charge_tick = 0 @@ -184,7 +186,7 @@ charge_tick = 0 if(!cell) return // check if we actually need to recharge - cell.give(100) //... to recharge the shot + cell.give(recharge_rate) // to recharge the shot on_recharge() update_icon() @@ -199,14 +201,14 @@ update_icon() -/obj/item/gun/energy/can_shoot(mob/living/user) +/obj/item/gun/energy/can_shoot(mob/living/user, silent = FALSE) if(user && sibyl_mod && !sibyl_mod.check_auth(user)) return FALSE var/obj/item/ammo_casing/energy/shot = ammo_type[select] . = cell.charge >= shot.e_cost - if(!.) + if(!. && !silent) sibyl_mod?.sibyl_sound(user, 'sound/voice/dominator/battery.ogg', 5 SECONDS) diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 31574f8430a..57fd110b524 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -798,7 +798,10 @@ /obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) - bolt_color = input(user,"","Choose Color",bolt_color) as color|null + var/color = tgui_input_color(user,"","Choose Color",bolt_color) + if(isnull(color)) + return + bolt_color = color #undef COMPATIBILITY_STANDART diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index fade0a3224b..a0ce4eaeabb 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -18,7 +18,7 @@ origin_tech = "combat=2;magnets=2" ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) clumsy_check = 0 - needs_permit = 0 + needs_permit = FALSE /obj/item/gun/energy/laser/retro name ="retro laser gun" diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 6c49b4a29af..18948259dd3 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -109,3 +109,79 @@ ammo_x_offset = 1 ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) selfcharge = TRUE + +/obj/item/gun/energy/gun/minigun + name = "Laser gatling gun" + desc = "Огромное лазерное орудие, обладающее выдающейся скорострельностью и поражающей силой. Говорят, что 12 секунд стрельбы из этой малышки обойдутся вам в 400 тысяч кредитов." + ru_names = list( + NOMINATIVE = "Гатлинг-лазер", + GENITIVE = "Гатлинг-лазера", + DATIVE = "Гатлинг-лазеру", + ACCUSATIVE = "Гатлинг-лазер", + INSTRUMENTAL = "Гатлинг-лазером", + PREPOSITIONAL = "Гатлинг-лазере" + ) + icon_state = "gatling" + item_state = "gatling" + fire_sound = "lasergatling" + origin_tech = "combat=7;magnets=6;powerstorage=6" + slot_flags = FALSE + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + weapon_weight = WEAPON_MEDIUM + w_class = WEIGHT_CLASS_GIGANTIC + throw_range = 0 + burst_size = 6 + spread = 45 + can_charge = FALSE + cell_type = /obj/item/stock_parts/cell/laser/gatling + ammo_type = list(/obj/item/ammo_casing/energy/laser/light) + selfcharge = TRUE + charge_delay = 5 + recharge_rate = 600 + slowdown = 0.2 + var/force_unwielded = 10 + var/force_wielded = 20 + +/obj/item/gun/energy/gun/minigun/Initialize(mapload) + . = ..() + AddComponent(/datum/component/two_handed, \ + force_unwielded = src.force_unwielded, \ + force_wielded = src.force_wielded, \ + require_twohands = TRUE \ + ) + +/obj/item/gun/energy/gun/minigun/can_be_pulled(atom/movable/user, force, show_message = FALSE) + ..() + balloon_alert(user, "слишком тяжело!") + +/obj/item/gun/energy/gun/minigun/update_icon_state() + item_state = !cell ? initial(item_state) : "[initial(item_state)][!can_shoot(silent = TRUE) ? "1" : ""]" + icon_state = !cell ? initial(icon_state) : "[initial(icon_state)][!can_shoot(silent = TRUE) ? "1" : ""]" + +/obj/item/gun/energy/gun/minigun/examine(mob/user) + . = ..() + + if(!cell) + return . + + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/charge_amount = round(cell.charge / (shot.e_cost * burst_size)) + + . += span_notice("Индикатор батареи сообщает: заряда хватит на [charge_amount] [declension_ru(charge_amount, "выстрел", "выстрела", "выстрелов")].") + +/obj/item/gun/energy/gun/minigun/pulse + name = "Pulse gatling gun" + icon_state = "gatling_pulse" + item_state = "gatling_pulse" + desc = "Огромное пульсовое орудие, обладающее выдающейся скорострельностью и разрушительной силой. \ + Является модификацией Гатлинг-лазера. Имеет самую совершенную батарею в мире, самозаряд которой полностью компенсирует энергозатраты при стрельбе." + ru_names = list( + NOMINATIVE = "Гатлинг-пульсер", + GENITIVE = "Гатлинг-пульсера", + DATIVE = "Гатлинг-пульсеру", + ACCUSATIVE = "Гатлинг-пульсер", + INSTRUMENTAL = "Гатлинг-пульсером", + PREPOSITIONAL = "Гатлинг-пульсере" + ) + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) + cell_type = /obj/item/stock_parts/cell/infinite diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm index 5651a0815c9..0514260bd3b 100644 --- a/code/modules/projectiles/guns/magic.dm +++ b/code/modules/projectiles/guns/magic.dm @@ -21,8 +21,8 @@ clumsy_check = 0 trigger_guard = TRIGGER_GUARD_ALLOW_ALL // Has no trigger at all, uses magic instead - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' //not really a gun and some toys use these inhands - righthand_file = 'icons/mob/inhands/items_righthand.dmi' + lefthand_file = 'icons/mob/inhands/staff_lefthand.dmi' //not really a gun and some toys use these inhands + righthand_file = 'icons/mob/inhands/staff_righthand.dmi' /obj/item/gun/magic/afterattack(atom/target, mob/living/user, flag, params) if(no_den_usage) diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 8b6744f62ec..a3f0a957ff5 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -2,6 +2,8 @@ slot_flags = ITEM_SLOT_BACK ammo_type = /obj/item/ammo_casing/magic item_flags = NO_MAT_REDEMPTION + lefthand_file = 'icons/mob/inhands/staff_lefthand.dmi' + righthand_file = 'icons/mob/inhands/staff_righthand.dmi' /obj/item/gun/magic/staff/change name = "staff of change" diff --git a/code/modules/projectiles/guns/medbeam.dm b/code/modules/projectiles/guns/medbeam.dm index 46b2b95a7d3..ff0e97b4e2b 100644 --- a/code/modules/projectiles/guns/medbeam.dm +++ b/code/modules/projectiles/guns/medbeam.dm @@ -1,19 +1,33 @@ /obj/item/gun/medbeam name = "Medical Beamgun" - desc = "Delivers volatile medical nanites in a focused beam. Don't cross the beams!" + ru_names = list( + NOMINATIVE = "Медицинская Лучпушка", + GENITIVE = "Медицинской Лучпушки", + DATIVE = "Медицинской Лучпушке", + ACCUSATIVE = "Медицинскую Лучпушку", + INSTRUMENTAL = "Медицинской Лучпушкой", + PREPOSITIONAL = "Медицинской Лучпушку" + ) + + desc = "Передает целебные наниты своим сфокусированным лучом. Не скрещивайте лучи!" + icon = 'icons/obj/chronos.dmi' icon_state = "chronogun" item_state = "chronogun" + w_class = WEIGHT_CLASS_NORMAL + weapon_weight = WEAPON_MEDIUM var/mob/living/current_target - var/last_check = 0 - var/check_delay = 10 //Check los as often as possible, max resolution is SSobj tick though + var/datum/beam/current_beam + + COOLDOWN_DECLARE(last_check) + var/check_delay = 1 SECONDS + var/max_range = 8 - var/active = FALSE - var/datum/beam/current_beam = null - weapon_weight = WEAPON_MEDIUM + var/active = FALSE + var/mounted = FALSE /obj/item/gun/medbeam/Initialize(mapload) @@ -49,6 +63,7 @@ active = FALSE QDEL_NULL(current_beam) on_beam_release(current_target) + current_target = null @@ -61,10 +76,10 @@ SIGNAL_HANDLER if(active && isliving(loc)) - to_chat(loc, span_warning("You lose control of the beam!")) + balloon_alert(loc, "контроль над лучом потерян") current_beam = null - active = FALSE //skip qdelling the beam again if we're doing this proc + active = FALSE // skip qdelling the beam again if we're doing this proc LoseTarget() @@ -77,18 +92,18 @@ LoseTarget() if(old_target == target || !isliving(target)) - return + return FALSE current_target = target active = TRUE current_beam = user.Beam(current_target, icon_state = "medbeam", time = 10 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/medical) - RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died))//this is a WAY better rangecheck than what was done before (process check) + RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died)) // this is a WAY better rangecheck than what was done before (process check) SSblackbox.record_feedback("tally", "gun_fired", 1, type) - + return TRUE /obj/item/gun/medbeam/process() - if(!ishuman(loc) && !isrobot(loc)) + if(!mounted && !isliving(loc)) LoseTarget() return @@ -96,10 +111,10 @@ LoseTarget() return - if(world.time <= last_check + check_delay) + if(!COOLDOWN_FINISHED(src, last_check)) return - last_check = world.time + COOLDOWN_START(src, last_check, check_delay) if(!los_check(loc, current_target)) QDEL_NULL(current_beam)//this will give the target lost message @@ -111,47 +126,69 @@ /obj/item/gun/medbeam/proc/los_check(atom/movable/user, mob/target) var/turf/user_turf = user.loc + + if(mounted) + user_turf = get_turf(user) + if(!istype(user_turf)) return FALSE + var/obj/dummy = new(user_turf) - dummy.pass_flags |= (PASSTABLE|PASSGLASS|PASSGRILLE|PASSFENCE) //Grille/Glass so it can be used through common windows + dummy.pass_flags |= (PASSTABLE | PASSGLASS | PASSGRILLE | PASSFENCE) // Grille/Glass so it can be used through common windows + var/turf/previous_step = user_turf var/first_step = TRUE + for(var/turf/next_step as anything in (get_line(user_turf, target) - user_turf)) if(first_step) for(var/obj/blocker in user_turf) if(!blocker.density || !(blocker.flags & ON_BORDER)) continue + if(blocker.CanPass(dummy, get_dir(user_turf, next_step))) continue + qdel(dummy) return FALSE // Could not leave the first turf. + first_step = FALSE + + if(mounted && next_step == user_turf) + continue // Mechs are dense and thus fail the check + if(next_step.density) qdel(dummy) return FALSE + for(var/atom/movable/movable as anything in next_step) if(!movable.CanPass(dummy, get_dir(next_step, previous_step))) qdel(dummy) return FALSE + for(var/obj/effect/ebeam/medical/B in next_step)// Don't cross the str-beams! if(QDELETED(current_beam)) - break //We shouldn't be processing anymore. + break // We shouldn't be processing anymore. + if(QDELETED(B)) continue + if(!B.owner) stack_trace("beam without an owner! [B]") continue + if(B.owner.origin != current_beam.origin) - next_step.visible_message(span_boldwarning("The medbeams cross and EXPLODE!")) + next_step.visible_message(span_boldwarning("Лучи пересекаются и ПРОИСХОДИТ ВЗРЫВ!")) explosion(B.loc, heavy_impact_range = 3, light_impact_range = 5, flash_range = 8, cause = src) qdel(dummy) return FALSE + previous_step = next_step + qdel(dummy) return TRUE + /obj/item/gun/medbeam/proc/on_beam_hit(mob/living/target) return @@ -160,11 +197,13 @@ var/prev_health = target.health target.heal_overall_damage(4, 4) var/bones_mended = FALSE + if(ishuman(target)) for(var/obj/item/organ/external/bodypart as anything in target.bodyparts) if(bodypart.has_fracture() && prob(10)) bones_mended = TRUE bodypart.mend_fracture() + if(target.health != prev_health || bones_mended) new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF") @@ -172,3 +211,5 @@ /obj/item/gun/medbeam/proc/on_beam_release(mob/living/target) return +/obj/item/gun/medbeam/mech + mounted = TRUE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 74e1e0e6f30..ffd8adfc77d 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -287,6 +287,10 @@ return FALSE prehit(bumped_atom) + if(HAS_TRAIT(src, TRAIT_SHRAPNEL)) + bumped_atom.hitby(src, TRUE) + qdel(src) + var/permutation = bumped_atom.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null if(permutation == -1 || forcedodge)// the bullet passes through a dense object! if(forcedodge > 0) @@ -393,7 +397,7 @@ Angle = round(get_angle(src, current)) if(spread) Angle += (rand() - 0.5) * spread - if(firer) + if(firer && ismob(firer)) hit_crawling_mobs_chance = firer.a_intent == INTENT_HELP ? 0 : 100 // Turn right away var/matrix/M = new @@ -459,8 +463,12 @@ /obj/item/projectile/proc/check_ricochet_flag(atom/A) - if(A.flags & CHECK_RICOCHET) + if((flag in list(ENERGY, LASER)) && (A.flags_ricochet & RICOCHET_SHINY)) + return TRUE + + if((flag in list(BOMB, BULLET)) && (A.flags_ricochet & RICOCHET_HARD)) return TRUE + return FALSE diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index ad9ddc0a321..0fe6b485d8d 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -19,6 +19,9 @@ /obj/item/projectile/beam/laser +/obj/item/projectile/beam/laser/light + damage = 15 + /obj/item/projectile/beam/laser/heavylaser name = "heavy laser" icon_state = "heavylaser" diff --git a/code/modules/projectiles/projectile/shrapnel.dm b/code/modules/projectiles/projectile/shrapnel.dm new file mode 100644 index 00000000000..15b004b4b4b --- /dev/null +++ b/code/modules/projectiles/projectile/shrapnel.dm @@ -0,0 +1,28 @@ +/obj/item/projectile/shrapnel + name = "shrapnel" + icon = 'icons/obj/shards.dmi' + throwforce = 14 + throw_speed = EMBED_THROWSPEED_THRESHOLD + embed_chance = 100 + embedded_fall_chance = 0 + w_class = WEIGHT_CLASS_SMALL + sharp = TRUE + damage = 14 + range = 20 + dismemberment = 5 + ricochets_max = 2 + ricochet_chance = 70 + hitsound = 'sound/weapons/pierce.ogg' + ru_names = list( + NOMINATIVE = "шрапнель", + GENITIVE = "шрапнели", + DATIVE = "шрапнели", + ACCUSATIVE = "шрапнель", + INSTRUMENTAL = "шрапнелью", + PREPOSITIONAL = "шрапнели" + ) + +/obj/item/projectile/shrapnel/Initialize(mapload) + . = ..() + icon_state = pick("shrapnel1", "shrapnel2", "shrapnel3") + ADD_TRAIT(src, TRAIT_SHRAPNEL, INNATE_TRAIT) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 31326844b87..a1f4b1299a8 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -137,9 +137,12 @@ /datum/reagents/proc/copy_to(obj/target, amount = 1, multiplier = 1, preserve_data = TRUE, safety = FALSE) if(!target) return - if(!target.reagents || total_volume <= 0) + if(total_volume <= 0) + return + + var/datum/reagents/R =(istype(target, /datum/reagents))? target : target?.reagents + if(!R || !istype(R)) return - var/datum/reagents/R = target.reagents amount = min(min(amount, total_volume), R.maximum_volume - R.total_volume) var/part = amount / total_volume var/trans_data = null @@ -222,6 +225,18 @@ return transfered +/datum/reagents/proc/can_metabolize(mob/living/carbon/human/H, datum/reagent/R) + if(!H.dna.species || !H.dna.species.reagent_tag) + return FALSE + if((R.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_SYN)) //SYNTHETIC-oriented reagents require PROCESS_SYN + return TRUE + if((R.process_flags & ORGANIC) && (H.dna.species.reagent_tag & PROCESS_ORG)) //ORGANIC-oriented reagents require PROCESS_ORG + return TRUE + //Species with PROCESS_DUO are only affected by reagents that affect both organics and synthetics, like acid and hellwater + if((R.process_flags & ORGANIC) && (R.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_DUO)) + return TRUE + + /datum/reagents/proc/metabolize(mob/living/M) if(M) temperature_reagents(M.bodytemperature - 30) @@ -244,17 +259,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M //Check if this mob's species is set and can process this type of reagent - var/can_process = FALSE - //If we somehow avoided getting a species or reagent_tag set, we'll assume we aren't meant to process ANY reagents (CODERS: SET YOUR SPECIES AND TAG!) - if(H.dna.species && H.dna.species.reagent_tag) - if((R.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_SYN)) //SYNTHETIC-oriented reagents require PROCESS_SYN - can_process = TRUE - if((R.process_flags & ORGANIC) && (H.dna.species.reagent_tag & PROCESS_ORG)) //ORGANIC-oriented reagents require PROCESS_ORG - can_process = TRUE - //Species with PROCESS_DUO are only affected by reagents that affect both organics and synthetics, like acid and hellwater - if((R.process_flags & ORGANIC) && (R.process_flags & SYNTHETIC) && (H.dna.species.reagent_tag & PROCESS_DUO)) - can_process = TRUE - + var/can_process = can_metabolize(H, R) //If handle_reagents returns 0, it's doing the reagent removal on its own var/species_handled = !(H.dna.species.handle_reagents(H, R)) can_process = can_process && !species_handled @@ -628,11 +633,16 @@ /datum/reagents/proc/add_reagent(reagent, amount, list/data=null, reagtemp = T20C, no_react = FALSE) if(!isnum(amount)) return TRUE + update_total() - if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) //Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. + if(total_volume + amount > maximum_volume) amount = (maximum_volume - total_volume) // Doesnt fit in. Make it disappear. Shouldnt happen. Will happen. + if(amount <= 0) return FALSE - chem_temp = clamp((chem_temp * total_volume + reagtemp * amount) / (total_volume + amount), temperature_min, temperature_max) //equalize with new chems + + chem_temp = clamp((chem_temp * total_volume + reagtemp * amount) / (total_volume + amount), temperature_min, temperature_max) // equalize with new chems + if(SEND_SIGNAL(src, COMSIG_EARLY_REAGENT_ADDED, reagent, amount, data, reagtemp, no_react, chem_temp) & COMPONENT_PREVENT_ADD_REAGENT) + return FALSE var/list/cached_reagents = reagent_list for(var/A in cached_reagents) @@ -640,34 +650,43 @@ if(R.id == reagent) R.volume += amount update_total() + if(my_atom) my_atom.on_reagent_change() + R.on_merge(data) + if(!no_react) temperature_react() handle_reactions() + return FALSE - var/datum/reagent/D = GLOB.chemical_reagents_list[reagent] + var/datum/reagent/D = (ispath(reagent))? new reagent() : GLOB.chemical_reagents_list[reagent] if(D) - var/datum/reagent/R = new D.type() cached_reagents += R R.holder = src R.volume = amount R.on_new(data) + if(data) R.data = data if(isliving(my_atom)) - R.on_mob_add(my_atom) //Must occur befor it could posibly run on_mob_delete + R.on_mob_add(my_atom) // Must occur befor it could posibly run on_mob_delete + update_total() + if(my_atom) my_atom.on_reagent_change() + if(!no_react) temperature_react() handle_reactions() + return FALSE + else warning("[my_atom] attempted to add a reagent called '[reagent]' which doesn't exist. ([usr])") @@ -746,6 +765,15 @@ /datum/reagents/proc/get_reagent(type) . = locate(type) in reagent_list +/datum/reagents/proc/get_reagent_by_id(id) + var/list/cached_reagents = reagent_list + for(var/A in cached_reagents) + var/datum/reagent/R = A + if(R.id == id) + return R + + return + /datum/reagents/proc/remove_all_type(reagent_type, amount, strict = FALSE, safety = TRUE) // Removes all reagent of X type. @strict set to 1 determines whether the childs of the type are included. if(!isnum(amount)) return TRUE diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index 3348df63932..150cfbdf9c4 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -112,10 +112,6 @@ if(powered()) . += "waitlight" -/obj/machinery/chem_master/blob_act(obj/structure/blob/B) - if(prob(50) && !QDELETED(src)) - qdel(src) - /obj/machinery/chem_master/power_change() if(!..()) return diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index 2785c3cfd6f..60647332acb 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -231,6 +231,11 @@ updateUsrDialog() return ATTACK_CHAIN_BLOCKED_ALL +/obj/machinery/reagentgrinder/examine(mob/user) + . = ..() + if(in_range(src, user)) + . += "Alt-click to activate it.
Ctrl-Shift-click to dispose content.
" + /obj/machinery/reagentgrinder/AltClick(mob/living/carbon/human/human) if(!istype(human) || !human.Adjacent(src)) return diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index d22721d6f5f..88a9482c900 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -49,8 +49,8 @@ /datum/reagent/proc/reaction_temperature(exposed_temperature, exposed_volume) //By default we do nothing. return -/datum/reagent/proc/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = TRUE) //Some reagents transfer on touch, others don't; dependent on if they penetrate the skin or not. - if(holder) //for catching rare runtimes +/datum/reagent/proc/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume, show_message = TRUE) // Some reagents transfer on touch, others don't; dependent on if they penetrate the skin or not. + if(holder) // for catching rare runtimes if(method == REAGENT_TOUCH && penetrates_skin && M.reagents && volume >= 1) M.reagents.add_reagent(id, volume) @@ -58,7 +58,8 @@ var/can_become_addicted = M.reagents.reaction_check(M, src) if(can_become_addicted) if(count_by_type(M.reagents.addiction_list, addict_supertype) > 0) - to_chat(M, "You feel slightly better, but for how long?") //sate_addiction handles this now, but kept this for the feed back. + to_chat(M, span_notice("You feel slightly better, but for how long?")) // sate_addiction handles this now, but kept this for the feed back. + return TRUE /datum/reagent/proc/reaction_obj(obj/O, volume) @@ -68,6 +69,8 @@ return /datum/reagent/proc/on_mob_life(mob/living/M) + if(current_cycle == 1) + on_mob_start_metabolize(M) current_cycle++ var/total_depletion_rate = metabolization_rate * M.metabolism_efficiency * M.digestion_ratio // Cache it @@ -75,8 +78,16 @@ sate_addiction(M) holder.remove_reagent(id, total_depletion_rate) //By default it slowly disappears. + if(volume <= 0) + on_mob_end_metabolize(M) return STATUS_UPDATE_NONE +/datum/reagent/proc/on_mob_start_metabolize(mob/living/metabolizer) + return + +/datum/reagent/proc/on_mob_end_metabolize(mob/living/metabolizer) + return + /datum/reagent/proc/handle_addiction(mob/living/M, consumption_rate) if(addiction_chance && count_by_type(M.reagents.addiction_list, addict_supertype) < 1) var/datum/reagent/new_reagent = new addict_supertype() diff --git a/code/modules/reagents/chemistry/reagents/blob.dm b/code/modules/reagents/chemistry/reagents/blob.dm deleted file mode 100644 index b1a7952973e..00000000000 --- a/code/modules/reagents/chemistry/reagents/blob.dm +++ /dev/null @@ -1,195 +0,0 @@ -// These can only be applied by blobs. They are what blobs are made out of. -// The 4 damage -/datum/reagent/blob - description = "" - var/complementary_color = "#000000" - var/message = "Блоб наносит вам удар" //message sent to any mob hit by the blob - var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt - can_synth = FALSE - -/datum/reagent/blob/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message, touch_protection) - return round(volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume. - -/datum/reagent/blob/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) //when the blob takes damage, do this - return damage - -/datum/reagent/blob/ripping_tendrils //does brute and a little stamina damage - name = "Разрывающие щупальца" - description = "Наносит высокий урон травмами, а также урон выносливости." - id = "ripping_tendrils" - color = "#7F0000" - complementary_color = "#a15656" - message_living = ", и вы чувствуете, как ваша кожа рвется и слезает." - -/datum/reagent/blob/ripping_tendrils/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.6*volume, BRUTE) - M.adjustStaminaLoss(volume) - if(iscarbon(M)) - M.emote("scream") - -/datum/reagent/blob/boiling_oil //sets you on fire, does burn damage - name = "Кипящее масло" - description = "Наносит высокий урон ожогами и поджигает жертву." - id = "boiling_oil" - color = "#B68D00" - complementary_color = "#c0a856" - message = "Блоб обдает вас горящим маслом" - message_living = ", и вы чувствуете, как ваша кожа обугливается и плавится" - -/datum/reagent/blob/boiling_oil/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - M.adjust_fire_stacks(round(volume/10)) - volume = ..() - M.apply_damage(0.6*volume, BURN) - M.IgniteMob() - M.emote("scream") - -/datum/reagent/blob/envenomed_filaments //toxin, hallucination, and some bonus spore toxin - name = "Ядовитые нити" - description = "Наносит высокий урон токсинами, вызывает галлюцинации и вводит споры в кровоток." - id = "envenomed_filaments" - color = "#9ACD32" - complementary_color = "#b0cd73" - message_living = ", и вы чувствуете себя плохо. Вас тошнит" - -/datum/reagent/blob/envenomed_filaments/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.6 * volume, TOX) - M.AdjustHallucinate(1.2 SECONDS * volume) - if(M.reagents) - M.reagents.add_reagent("spore", 0.4*volume) - -/datum/reagent/blob/lexorin_jelly //does tons of oxygen damage and a little brute - name = "Лексориновое желе" - description = "Наносит средний урон травмами, но огромный урон гипоксией." - id = "lexorin_jelly" - color = "#00FFC5" - complementary_color = "#56ebc9" - message_living = ", и ваши легкие кажутся тяжелыми и слабыми" - -/datum/reagent/blob/lexorin_jelly/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.4*volume, BRUTE) - M.apply_damage(1*volume, OXY) - M.AdjustLoseBreath(round(0.6 SECONDS * volume)) - - -/datum/reagent/blob/kinetic //does semi-random brute damage - name = "Кинетический желатин" - description = "Наносит случайный урон травмами, в 0,33–2,33 раза превышающий стандартное количество." - id = "kinetic" - color = "#FFA500" - complementary_color = "#ebb756" - message = "Блоб избивает вас" - -/datum/reagent/blob/kinetic/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - var/damage = rand(5, 35)/25 - M.apply_damage(damage*volume, BRUTE) - -/datum/reagent/blob/cryogenic_liquid //does low burn damage and stamina damage and cools targets down - name = "Криогенная жидкость" - description = "Наносит средний урон травмами, урон выносливости и вводит в жертв ледяное масло, замораживая их до смерти." - id = "cryogenic_liquid" - color = "#8BA6E9" - complementary_color = "#a8b7df" - message = "Блоб обливает вас ледяной жидкостью" - message_living = ", и вы чувствуете себя холодным и усталым" - -/datum/reagent/blob/cryogenic_liquid/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.4*volume, BURN) - M.adjustStaminaLoss(volume) - if(M.reagents) - M.reagents.add_reagent("frostoil", 0.4*volume) - -/datum/reagent/blob/b_sorium - name = "Сорий" - description = "Наносит высокий урон травмами и отбрасывает людей в стороны." - id = "b_sorium" - color = "#808000" - complementary_color = "#a2a256" - message = "Блоб врезается в вас и отбрасывает в сторону." - -/datum/reagent/blob/b_sorium/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - reagent_vortex(M, 1, volume) - volume = ..() - M.apply_damage(0.6*volume, BRUTE) - -/datum/reagent/blob/proc/reagent_vortex(mob/living/M, setting_type, volume) - var/turf/pull = get_turf(M) - var/range_power = clamp(round(volume/5, 1), 1, 5) - for(var/atom/movable/X in range(range_power,pull)) - if(iseffect(X)) - continue - if(X.move_resist <= MOVE_FORCE_DEFAULT && !X.anchored) - var/distance = get_dist(X, pull) - var/moving_power = max(range_power - distance, 1) - spawn(0) - if(moving_power > 2) //if the vortex is powerful and we're close, we get thrown - if(setting_type) - var/atom/throw_target = get_edge_target_turf(X, get_dir(X, get_step_away(X, pull))) - var/throw_range = 5 - distance - X.throw_at(throw_target, throw_range, 1) - else - X.throw_at(pull, distance, 1) - else - if(setting_type) - for(var/i = 0, i < moving_power, i++) - sleep(2) - if(!step_away(X, pull)) - break - else - for(var/i = 0, i < moving_power, i++) - sleep(2) - if(!step_towards(X, pull)) - break - -/datum/reagent/blob/radioactive_gel - name = "Радиоактивный гель" - description = "Наносит средний урон токсинами и небольшой урон травмами, но облучает тех, кого задевает." - id = "radioactive_gel" - color = "#2476f0" - complementary_color = "#24f0f0" - message_living = ", и вы чувствуете странное тепло изнутри" - -/datum/reagent/blob/radioactive_gel/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.3 * volume, TOX) - M.apply_damage(0.2 * volume, BRUTE) // lets not have IPC / plasmaman only take 7.5 damage from this - if(M.reagents) - M.reagents.add_reagent("uranium", 0.3 * volume) - -/datum/reagent/blob/teslium_paste - name = "Теслиевая паста" - description = "Наносит средний урон ожогами и вызывает удары током у тех, кого задевает, со временем." - id = "teslium_paste" - color = "#20324D" - complementary_color = "#412968" - message_living = ", и вы чувствуете удар статическим электричеством" - -/datum/reagent/blob/teslium_paste/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(method == REAGENT_TOUCH) - volume = ..() - M.apply_damage(0.4 * volume, BURN) - if(M.reagents) - if(M.reagents.has_reagent("teslium") && prob(0.6 * volume)) - M.electrocute_act((0.5 * volume), "разряда блоба", flags = SHOCK_NOGLOVES) - M.reagents.del_reagent("teslium") - return //don't add more teslium after you shock it out of someone. - M.reagents.add_reagent("teslium", 0.125 * volume) // a little goes a long way - -/datum/reagent/blob/proc/send_message(mob/living/M) - var/totalmessage = message - if(message_living && !issilicon(M)) - totalmessage += message_living - totalmessage += "!" - to_chat(M, "[totalmessage]") diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index 6cd40966d6d..c9e9c544bf9 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -141,7 +141,7 @@ if(method == REAGENT_INGEST && iscarbon(M)) var/mob/living/carbon/C = M if(C.get_blood_id() == id && !HAS_TRAIT(C, TRAIT_NO_BLOOD_RESTORE)) - C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL) + C.setBlood(min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL)) C.reagents.del_reagent(id) /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/M) @@ -295,7 +295,8 @@ var/mob/living/carbon/human/H = M //do not restore blood on things with no blood by nature. if(!HAS_TRAIT(H, TRAIT_NO_BLOOD) && !HAS_TRAIT(H, TRAIT_NO_BLOOD_RESTORE) && H.blood_volume < BLOOD_VOLUME_NORMAL) - H.blood_volume += 1 + H.AdjustBlood(1) + return ..() | update_flags /datum/reagent/medicine/synthflesh @@ -1052,20 +1053,20 @@ if(severity == 1) if(effect <= 2) M.vomit(0, VOMIT_BLOOD, 0 SECONDS) - M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + M.AdjustBlood(-rand(5, 10)) else if(effect <= 4) M.vomit(0, VOMIT_BLOOD, 0 SECONDS) - M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + M.AdjustBlood(-rand(1, 2)) else if(severity == 2) if(effect <= 2) M.visible_message("[M] is bleeding from [M.p_their()] very pores!") M.bleed(rand(10, 20)) else if(effect <= 4) M.vomit(0, VOMIT_BLOOD, 0 SECONDS) - M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + M.AdjustBlood(-rand(5, 10)) else if(effect <= 8) M.vomit(0, VOMIT_BLOOD, 0 SECONDS) - M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + M.AdjustBlood(-rand(1, 2)) return list(effect, update_flags) @@ -1428,7 +1429,7 @@ for(var/obj/item/organ/internal/I as anything in M.internal_organs) // 56 healing to all internal organs. I.heal_internal_damage(8) if(!HAS_TRAIT(H, TRAIT_NO_BLOOD_RESTORE) && H.blood_volume < BLOOD_VOLUME_NORMAL * 0.9)// If below 90% blood, regenerate 210 units total - H.blood_volume += 30 + H.AdjustBlood(30) for(var/datum/disease/critical/heart_failure/HF in H.diseases) HF.cure() //Won't fix a stopped heart, but it will sure fix a critical one. Shock is not fixed as healing will fix it if(M.health < 40) diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm index 6380e5ef680..5d0eda7a673 100644 --- a/code/modules/reagents/chemistry/reagents/misc.dm +++ b/code/modules/reagents/chemistry/reagents/misc.dm @@ -117,11 +117,6 @@ color = "#D0D0D0" // rgb: 208, 208, 208 taste_description = "sub-par bling" -/datum/reagent/silver/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(M.has_bane(BANE_SILVER)) - M.reagents.add_reagent("toxin", volume) - . = ..() - /datum/reagent/aluminum name = "Aluminum" id = "aluminum" @@ -165,14 +160,10 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M if(!HAS_TRAIT(H, TRAIT_NO_BLOOD) && !HAS_TRAIT(H, TRAIT_NO_BLOOD_RESTORE) && H.blood_volume < BLOOD_VOLUME_NORMAL) - H.blood_volume += 0.8 + H.AdjustBlood(0.8) + return ..() -/datum/reagent/iron/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) - if(M.has_bane(BANE_IRON) && holder && holder.chem_temp < 150) //If the target is weak to cold iron, then poison them. - M.reagents.add_reagent("toxin", volume) - ..() - //foam /datum/reagent/fluorosurfactant name = "Fluorosurfactant" @@ -355,7 +346,7 @@ if(ishuman(M)) var/mob/living/carbon/human/H = M var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) - head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name, H = H) + head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name, human = H) head_organ.f_style = random_facial_hair_style(H.gender, head_organ.dna.species.name) H.update_hair() H.update_fhair() @@ -380,7 +371,7 @@ if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them. head_organ.h_style = "Very Long Hair" else //Otherwise, give them a random hair style. - head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name, H = H) + head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species, human = H) if(head_organ.dna.species.name in tmp_facial_hair_style.species_allowed) //If 'Very Long Beard' is a style the person's species can have, give it to them. head_organ.f_style = "Very Long Beard" else //Otherwise, give them a random facial hair style. diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index a8d96df05b4..671a633705a 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -6,10 +6,11 @@ color = "#CF3600" // rgb: 207, 54, 0 taste_mult = 1.2 taste_description = "bitterness" + var/toxpwr = 2 /datum/reagent/toxin/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustToxLoss(2, FALSE) + update_flags |= M.adjustToxLoss(toxpwr, FALSE) return ..() | update_flags /datum/reagent/spider_venom @@ -132,7 +133,7 @@ if(method == REAGENT_INGEST && iscarbon(M)) var/mob/living/carbon/C = M if(C.get_blood_id() == id && !HAS_TRAIT(C, TRAIT_NO_BLOOD_RESTORE)) - C.blood_volume = min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL) + C.setBlood(min(C.blood_volume + round(volume, 0.1), BLOOD_VOLUME_NORMAL)) C.reagents.del_reagent(id) /datum/reagent/slimejelly/reaction_turf(turf/T, volume, color) @@ -357,58 +358,78 @@ clothing_penetration = 1 var/acidpwr = 10 //the amount of protection removed from the armour + /datum/reagent/acid/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustFireLoss(1, FALSE) + + if(!acid_proof_species(M)) + update_flags |= M.adjustFireLoss(1, FALSE) + return ..() | update_flags + /datum/reagent/acid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) - if(ishuman(M) && !isgrey(M)) - var/mob/living/carbon/human/H = M - if(method == REAGENT_TOUCH) - to_chat(H, span_warning("The greenish acidic substance stings[volume < 1 ? " you, but isn't concentrated enough to harm you" : null]!")) - if(volume < 1) - return + if(!ishuman(M)) + return - var/damage_coef = 0 - var/should_scream = TRUE - for(var/obj/item/organ/external/bodypart as anything in H.bodyparts) - if(istype(bodypart, /obj/item/organ/external/head) && !H.wear_mask && !H.head && volume > 25) - bodypart.disfigure() - if(H.has_pain() && should_scream) - H.emote("scream") - should_scream = FALSE + var/mob/living/carbon/human/H = M - damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100 - if(damage_coef > 0 && should_scream) - should_scream = FALSE - if(H.has_pain()) - H.emote("scream") - H.apply_damage(clamp(volume - 1, 2, 20) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart) - H.apply_damage(clamp((volume - 1)/2, 1, 10) * damage_coef / length(H.bodyparts), BRUTE, def_zone = bodypart) + if(acid_proof_species(H)) + return + + if(method == REAGENT_TOUCH) + to_chat(H, span_warning("The greenish acidic substance stings[volume < 1 ? " you, but isn't concentrated enough to harm you" : null]!")) + if(volume < 1) return - if(method == REAGENT_INGEST) - to_chat(H, span_warning("The greenish acidic substance stings[volume < 1 ? " you, but isn't concentrated enough to harm you" : null]!")) - if(volume >= 1) - H.adjustFireLoss(clamp((volume - 1) * 2, 0, 30)) + var/damage_coef = 0 + var/should_scream = TRUE + + for(var/obj/item/organ/external/bodypart as anything in H.bodyparts) + if(istype(bodypart, /obj/item/organ/external/head) && !H.wear_mask && !H.head && volume > 25) + bodypart.disfigure() + if(H.has_pain() && should_scream) + H.emote("scream") + should_scream = FALSE + + damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100 + + if(damage_coef > 0 && should_scream) + should_scream = FALSE if(H.has_pain()) H.emote("scream") + H.apply_damage(clamp(volume - 1, 2, 20) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart) + H.apply_damage(clamp((volume - 1)/2, 1, 10) * damage_coef / length(H.bodyparts), BRUTE, def_zone = bodypart) + + return + + if(method == REAGENT_INGEST) + to_chat(H, span_warning("The greenish acidic substance stings[volume < 1 ? " you, but isn't concentrated enough to harm you" : null]!")) + if(volume >= 1) + H.adjustFireLoss(clamp((volume - 1) * 2, 0, 30)) + if(H.has_pain()) + H.emote("scream") + + /datum/reagent/acid/reaction_obj(obj/O, volume) if(ismob(O.loc)) //handled in human acid_act() return + volume = round(volume, 0.1) O.acid_act(acidpwr, volume) + /datum/reagent/acid/reaction_turf(turf/T, volume) if(!istype(T)) return + volume = round(volume, 0.1) T.acid_act(acidpwr, volume) + /datum/reagent/acid/facid - name = "Fluorosulfuric Acid" + name = "Fluorosulfuric acid" id = "facid" description = "Fluorosulfuric acid is a an extremely corrosive super-acid." color = "#5050FF" @@ -416,70 +437,107 @@ //acid is not using permeability_coefficient to calculate protection, but armour["acid"] clothing_penetration = 1 + /datum/reagent/acid/facid/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustToxLoss(0.5, FALSE) + + if(!acid_proof_species(M)) + update_flags |= M.adjustToxLoss(0.5, FALSE) + return ..() | update_flags + /datum/reagent/acid/facid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(method == REAGENT_TOUCH) - if(volume >= 5) - var/damage_coef = 0 - var/should_scream = TRUE - for(var/obj/item/organ/external/bodypart as anything in H.bodyparts) - damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100 - if(damage_coef > 0 && should_scream) - should_scream = FALSE - if(H.has_pain()) - H.emote("scream") - H.apply_damage(clamp((volume - 5) * 3, 8, 75) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart) - - if(volume > 9 && (H.wear_mask || H.head)) - if(H.wear_mask && !(H.wear_mask.resistance_flags & ACID_PROOF)) - to_chat(H, "Your [H.wear_mask.name] melts away!") - qdel(H.wear_mask) - H.update_inv_wear_mask() - if(H.head && !(H.head.resistance_flags & ACID_PROOF)) - to_chat(H, "Your [H.head.name] melts away!") - qdel(H.head) - H.update_inv_head() - return - else - if(volume >= 5) - if(H.has_pain()) + if(!ishuman(M)) + return + + var/mob/living/carbon/human/H = M + var/damage_ignored = acid_proof_species(H) + + if(method == REAGENT_TOUCH) + if(volume >= 5 && !damage_ignored) // Prevent damage to mob, but not to clothes + var/damage_coef = 0 + var/should_scream = TRUE + + for(var/obj/item/organ/external/bodypart as anything in H.bodyparts) + damage_coef = (100 - clamp(H.getarmor_organ(bodypart, "acid"), 0, 100))/100 + if(damage_coef && should_scream && H.has_pain()) // prevent emote spam H.emote("scream") - H.adjustFireLoss(clamp((volume - 5) * 3, 8, 75)); - to_chat(H, "The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!") + should_scream = FALSE + + H.apply_damage(clamp((volume - 5) * 3, 8, 75) * damage_coef / length(H.bodyparts), BURN, def_zone = bodypart) + + if(volume > 9 && (H.wear_mask || H.head)) + if(H.wear_mask && !(H.wear_mask.resistance_flags & ACID_PROOF)) + to_chat(H, span_danger("Your [H.wear_mask.name] melts away!")) + qdel(H.wear_mask) + H.update_inv_wear_mask() + + if(H.head && !(H.head.resistance_flags & ACID_PROOF)) + to_chat(H, span_danger("Your [H.head.name] melts away!")) + qdel(H.head) + H.update_inv_head() + + return + + else + if(damage_ignored) + return + + if(volume >= 5) + H.emote("scream") + H.adjustFireLoss(clamp((volume - 5) * 3, 8, 75)); + + to_chat(H, span_warning("The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!")) + /datum/reagent/acetic_acid - name = "acetic acid" + name = "Acetic acid" id = "acetic_acid" description = "A weak acid that is the main component of vinegar and bad hangovers." color = "#0080ff" reagent_state = LIQUID taste_description = "vinegar" + /datum/reagent/acetic_acid/reaction_mob(mob/M, method = REAGENT_TOUCH, volume) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(method == REAGENT_TOUCH) - if(H.wear_mask || H.head) - return - if(volume >= 50 && prob(75)) - var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD) - if(affecting) - affecting.disfigure() - H.take_overall_damage(5, 15) - H.emote("scream") - else - H.adjustBruteLoss(min(5, volume * 0.25)) + if(!ishuman(M)) + return + + var/mob/living/carbon/human/H = M + if(acid_proof_species(H)) + return + + if(method == REAGENT_TOUCH) + if(H.wear_mask || H.head) + return + + if(volume >= 50 && prob(75)) + var/obj/item/organ/external/affecting = H.get_organ(BODY_ZONE_HEAD) + if(affecting) + affecting.disfigure() + + H.take_overall_damage(5, 15) + H.emote("scream") + else - to_chat(H, "The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!") - if(volume >= 25) - H.take_overall_damage(2) - H.emote("scream") + H.adjustBruteLoss(min(5, volume * 0.25)) + + else + to_chat(H, span_warning("The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!")) + if(volume >= 25) + H.take_overall_damage(2) + H.emote("scream") + + +/datum/reagent/proc/acid_proof_species(mob/living/carbon/human/H) + if(!istype(H)) + return FALSE // skip check + + if(HAS_TRAIT(H, TRAIT_ACID_PROTECTED)) + return TRUE // acid proof species + + return FALSE /datum/reagent/carpotoxin @@ -511,19 +569,35 @@ return ..() | update_flags -/datum/reagent/spore +/datum/reagent/toxin/spore name = "Spore Toxin" - id = "spore" description = "A natural toxin produced by blob spores that inhibits vision when ingested." color = "#9ACD32" + id = "spore" + toxpwr = 1 + can_synth = FALSE taste_description = "bitterness" -/datum/reagent/spore/on_mob_life(mob/living/M) - var/update_flags = STATUS_UPDATE_NONE - update_flags |= M.adjustToxLoss(1, FALSE) - M.damageoverlaytemp = 60 - M.EyeBlurry(6 SECONDS) - return ..() | update_flags +/datum/reagent/toxin/spore/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.damageoverlaytemp = 60 + affected_mob.update_damage_hud() + affected_mob.EyeBlurry(6 SECONDS * REM * seconds_per_tick) + +/datum/reagent/toxin/spore_burning + name = "Burning Spore Toxin" + description = "A natural toxin produced by blob spores that induces combustion in its victim." + color = "#9ACD32" + id = "spore_burn" + toxpwr = 0.5 + taste_description = "burning" + can_synth = FALSE + +/datum/reagent/toxin/spore_burning/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) + . = ..() + affected_mob.adjust_fire_stacks(2 * REM * seconds_per_tick) + affected_mob.IgniteMob() + /datum/reagent/beer2 //disguised as normal beer for use by emagged service borgs name = "Beer" diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index 1daf2b9acac..2b5025254e2 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -6,7 +6,7 @@ desc = "A small bottle." icon = 'icons/obj/chemical.dmi' icon_state = "round_bottle" - item_state = "atoxinbottle" + item_state = "round_bottle" amount_per_transfer_from_this = 10 possible_transfer_amounts = list(5,10,15,25,30) container_type = OPENCONTAINER diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 965ac4fc1e3..0f959176c05 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -106,6 +106,7 @@ investigate_log("[key_name_log(P.firer)] triggered a fueltank explosion with [P.name]", INVESTIGATE_BOMB) ..() + /obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE, log_attack = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion if(rigtrigger) // If the explosion is triggered by an assembly holder add_attack_logs(lastrigger, src, "rigged fuel tank exploded", ATKLOG_FEW) @@ -151,6 +152,11 @@ /obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weldingtool/sword)) + if(I.tool_enabled) + boom(FALSE, TRUE) + return ATTACK_CHAIN_BLOCKED_ALL + if(istype(I, /obj/item/assembly_holder)) add_fingerprint(user) var/obj/item/assembly_holder/assembly = I diff --git a/code/modules/research/designs/AI_module_designs.dm b/code/modules/research/designs/AI_module_designs.dm index 49b8dd5b80a..21c422161a4 100644 --- a/code/modules/research/designs/AI_module_designs.dm +++ b/code/modules/research/designs/AI_module_designs.dm @@ -9,7 +9,7 @@ req_tech = list("programming" = 5, "materials" = 4) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/freeform + build_path = /obj/item/ai_module/freeform category = list("AI Modules") /datum/design/onecrewmember_module @@ -19,7 +19,7 @@ req_tech = list("programming" = 6, "materials" = 4) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/oneCrewMember + build_path = /obj/item/ai_module/one_crew_member category = list("AI Modules") /datum/design/oxygen_module @@ -29,7 +29,7 @@ req_tech = list("programming" = 4, "biotech" = 2, "materials" = 4) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/oxygen + build_path = /obj/item/ai_module/oxygen category = list("AI Modules") /datum/design/protectstation_module @@ -39,7 +39,7 @@ req_tech = list("programming" = 5, "materials" = 4) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/protectStation + build_path = /obj/item/ai_module/protect_station category = list("AI Modules") /datum/design/purge_module @@ -49,7 +49,7 @@ req_tech = list("programming" = 5, "materials" = 6) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/purge + build_path = /obj/item/ai_module/purge category = list("AI Modules") /datum/design/quarantine_module @@ -59,7 +59,7 @@ req_tech = list("programming" = 3, "biotech" = 2, "materials" = 4) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/quarantine + build_path = /obj/item/ai_module/quarantine category = list("AI Modules") /datum/design/reset_module @@ -69,7 +69,7 @@ req_tech = list("programming" = 4, "materials" = 6) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/reset + build_path = /obj/item/ai_module/reset category = list("AI Modules") /datum/design/safeguard_module @@ -79,7 +79,7 @@ req_tech = list("programming" = 3, "materials" = 3) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_GOLD = 100) - build_path = /obj/item/aiModule/safeguard + build_path = /obj/item/ai_module/safeguard category = list("AI Modules") /datum/design/asimov @@ -89,7 +89,7 @@ req_tech = list("programming" = 3, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/asimov + build_path = /obj/item/ai_module/asimov category = list("AI Modules") /datum/design/corporate_module @@ -99,7 +99,7 @@ req_tech = list("programming" = 5, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/corp + build_path = /obj/item/ai_module/corp category = list("AI Modules") /datum/design/crewsimov @@ -109,7 +109,7 @@ req_tech = list("programming" = 3, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/crewsimov + build_path = /obj/item/ai_module/crewsimov category = list("AI Modules") /datum/design/freeformcore_module @@ -119,7 +119,7 @@ req_tech = list("programming" = 6, "materials" = 6) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/freeformcore + build_path = /obj/item/ai_module/freeformcore category = list("AI Modules") /datum/design/paladin_module @@ -129,6 +129,6 @@ req_tech = list("programming" = 5, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/paladin + build_path = /obj/item/ai_module/paladin category = list("AI Modules") diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm index e361291f828..33b08853dbc 100644 --- a/code/modules/research/designs/mechfabricator_designs.dm +++ b/code/modules/research/designs/mechfabricator_designs.dm @@ -1004,6 +1004,17 @@ construction_time = 20 SECONDS category = list("Exosuit Equipment") +/datum/design/medbeamgun + name = "Exosuit Medical Equipment (Mecha Medbeam)" + id = "mech_medical_beamgun" + build_type = MECHFAB + build_path = /obj/item/mecha_parts/mecha_equipment/medical/beamgun + req_tech = list("biotech" = 7, "bluespace" = 7, "powerstorage" = 7) + materials = list(MAT_METAL=5000,MAT_DIAMOND=600,MAT_GLASS=600,MAT_GOLD=600,MAT_URANIUM=300,MAT_BLUESPACE=650) + construction_time = 20 SECONDS + category = list("Exosuit Equipment") + + /datum/design/improved_exosuit_control_system name = "Exosuit Common Equipment (Control System Upgrade)" id = "mech_improved_exosuit_control_system" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 19158649a7a..13b05c9d56d 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -644,6 +644,17 @@ build_path = /obj/item/organ/internal/cyberimp/chest/reviver category = list("Medical") +/datum/design/voice_retranslator + name = "Psionic Voice Retranslator" + desc = "Имплант для перевода псионической речи греев в более понятные для других гуманоидов звуковые волны. Разработан специально для греев." + id = "ci_retranslator" + req_tech = list("materials" = 5, "programming" = 6, "biotech" = 6, "engineering" = 6, "abductor" = 4) + build_type = PROTOLATHE | MECHFAB + construction_time = 50 + materials = list(MAT_METAL = 2500, MAT_GLASS = 1500, MAT_TITANIUM = 1000, MAT_DIAMOND = 600, MAT_PLASMA = 500) + build_path = /obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator + category = list("Medical") + ///////////////////////////////////////// ////////////Regular Implants///////////// ///////////////////////////////////////// @@ -809,7 +820,7 @@ category = list("Medical") /datum/design/modified_medical_gloves - name = "modified medical gloves" + name = "Modified Medical Gloves" desc = "They are very soft and light to the touch and do not hinder movement at all." id = "modified_medical_gloves" req_tech = list("magnets" = 7, "materials" = 7, "programming" = 5, "biotech" = 5) @@ -827,3 +838,13 @@ materials = list(MAT_SILVER = 1200, MAT_GLASS = 800, MAT_DIAMOND = 1200, MAT_GOLD = 400, MAT_BLUESPACE = 2000) build_path = /obj/item/bodybag/bluespace category = list("Medical") + +/datum/design/adv_drug_storage + name = "Advanced drug storage" + desc = "Технологичное устройство для хранения препаратов небольшого размера, имеет два контейнера разной формы, что объединяет центральное хранилище устройства." + id = "adv_drug_storage" + req_tech = list("materials" = 4, "bluespace" = 3, "biotech" = 3, "plasmatech" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 340, MAT_GLASS = 340, MAT_PLASMA = 200, MAT_BLUESPACE = 30) + build_path = /obj/item/storage/pill_bottle/bluespace + category = list("Medical") diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm index 089cb6de111..ec31600f9aa 100644 --- a/code/modules/research/designs/misc_designs.dm +++ b/code/modules/research/designs/misc_designs.dm @@ -151,3 +151,24 @@ materials = list(MAT_METAL = 800, MAT_GLASS = 600) build_path = /obj/item/vending_refill/custom category = list("Miscellaneous") + +/datum/design/translator_chip + name = "PVR Language Chip" + desc = "Крошечный чип с индикатором. Устанавливается в импланты-переводчики." + id = "pvr_language_chip" + req_tech = list("materials" = 3, "programming" = 5, "abductor" = 1) + build_type = PROTOLATHE + build_path = /obj/item/translator_chip + materials = list(MAT_METAL = 1000, MAT_GLASS = 100, MAT_TITANIUM = 500, MAT_PLASMA = 500, MAT_DIAMOND = 100) + category = list("Miscellaneous") + +/datum/design/retranslator_upgrade + name = "PVR Storage Upgrade" + desc = "Маленькое устройство для расширения количества слотов голосовых чипов в ретрансляторе псионического голоса." + id = "pvr_storage_upgrade" + req_tech = list("materials" = 5, "programming" = 6, "bluespace" = 6, "abductor" = 2) + build_type = PROTOLATHE + build_path = /obj/item/translator_upgrade/grey_retraslator + materials = list(MAT_METAL = 1000, MAT_GLASS = 100, MAT_TITANIUM = 500, MAT_PLASMA = 500, MAT_DIAMOND = 100) + category = list("Miscellaneous") + diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 1f96ed55fb1..8bb3ba0bc77 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -321,7 +321,7 @@ req_tech = list("programming" = 5, "syndicate" = 2, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/antimov + build_path = /obj/item/ai_module/antimov locked = TRUE category = list("ILLEGAL") @@ -332,7 +332,7 @@ req_tech = list("programming" = 5, "syndicate" = 2, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/tyrant + build_path = /obj/item/ai_module/tyrant locked = TRUE category = list("ILLEGAL") @@ -491,14 +491,14 @@ locked = TRUE category = list("ILLEGAL") -/datum/design/aiModule_syndicate +/datum/design/ai_module_syndicate name = "Hacked AI Module" desc = "A hacked AI law module" id = "syndiaimodule" req_tech = list("syndicate" = 6, "programming" = 5, "materials" = 5) build_type = IMPRINTER materials = list(MAT_GLASS = 1000, MAT_DIAMOND = 100) - build_path = /obj/item/aiModule/syndicate + build_path = /obj/item/ai_module/syndicate locked = TRUE category = list("ILLEGAL") @@ -576,6 +576,20 @@ build_path = /obj/item/clothing/gloves/color/black/pyro_claws category = list("Weapons") +/* uncomment when every tech is 90 lvl, too op for now +/datum/design/laserminigun + name = "Laser gatling gun" + desc = "Огромное лазерное орудие, обладающее выдающейся скорострельностью и поражающей силой. Говорят, что 12 секунд стрельбы из этой малышки обойдутся вам в 400 тысяч кредитов." + id = "laser_gatling" + build_type = PROTOLATHE + req_tech = list("combat" = 8, "materials" = 7, "magnets" = 7, "powerstorage" = 7) + materials = list(MAT_METAL = 12000, MAT_GLASS = 2400, MAT_URANIUM = 1200, MAT_TITANIUM = 1200, MAT_DIAMOND = 1200) + locked = TRUE + build_path = /obj/item/gun/energy/gun/minigun + category = list("Weapons") + lathe_time_factor = 0.5 +*/ + /datum/design/real_plasma_pistol name = "Plasma Pistol" desc = "HA specialized firearm designed to fire heated bolts of plasma. Can be overloaded for a high damage shield breaking shot." diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index a14e5fac182..ec3f40e3379 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -496,19 +496,19 @@ won't update every console in existence) but it's more of a hassle to do. Also, var/obj/item/new_item_item = new_item new_item_item.update_materials_coeff(coeff) - if(locked) - var/obj/item/storage/lockbox/research/L = new/obj/item/storage/lockbox/research(machine.loc) - new_item.forceMove(L) - L.name += " ([new_item.name])" - L.origin_tech = new_item.origin_tech - L.req_access = being_built.access_requirement + if(locked && isitem(new_item)) + var/obj/item/real_item = new_item + var/obj/item/storage/lockbox/research/lockbox = new /obj/item/storage/lockbox/research(machine.loc) + real_item.forceMove(lockbox) + lockbox.name += " ([real_item.name])" + lockbox.origin_tech = real_item.origin_tech + lockbox.req_access = being_built.access_requirement + lockbox.w_class = real_item.w_class > lockbox.w_class ? real_item.w_class : lockbox.w_class var/list/lockbox_access - for(var/A in L.req_access) + for(var/A in lockbox.req_access) lockbox_access += "[get_access_desc(A)] " - - L.desc = "A locked box. It is locked to [lockbox_access]access." - + lockbox.desc = "A locked box. It is locked to [lockbox_access]access." else new_item.loc = machine.loc diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm index a6e7f227e8b..9cc2a08c205 100644 --- a/code/modules/research/research.dm +++ b/code/modules/research/research.dm @@ -237,78 +237,80 @@ research holder datum. name = "Materials Research" desc = "Development of new and improved materials." id = "materials" - max_level = 7 + max_level = 8 /datum/tech/engineering name = "Engineering Research" desc = "Development of new and improved engineering parts and methods." id = "engineering" - max_level = 7 + max_level = 8 /datum/tech/plasmatech name = "Plasma Research" desc = "Research into the mysterious substance colloqually known as 'plasma'." id = "plasmatech" - max_level = 7 + max_level = 8 rare = 3 /datum/tech/powerstorage name = "Power Manipulation Technology" desc = "The various technologies behind the storage and generation of electicity." id = "powerstorage" - max_level = 7 + max_level = 8 /datum/tech/bluespace name = "'Blue-space' Research" desc = "Research into the sub-reality known as 'blue-space'." id = "bluespace" - max_level = 7 + max_level = 8 rare = 2 /datum/tech/biotech name = "Biological Technology" desc = "Research into the deeper mysteries of life and organic substances." id = "biotech" - max_level = 7 + max_level = 8 /datum/tech/combat name = "Combat Systems Research" desc = "The development of offensive and defensive systems." id = "combat" - max_level = 7 + max_level = 8 /datum/tech/magnets name = "Electromagnetic Spectrum Research" desc = "Research into the electromagnetic spectrum. No clue how they actually work, though." id = "magnets" - max_level = 7 + max_level = 8 /datum/tech/programming name = "Data Theory Research" desc = "The development of new computer and artificial intelligence and data storage systems." id = "programming" - max_level = 7 + max_level = 8 /datum/tech/toxins //not meant to be raised by deconstruction, do not give objects toxins as an origin_tech name = "Toxins Research" desc = "Research into plasma based explosive devices. Upgrade through testing explosives in the toxins lab." id = "toxins" - max_level = 7 + max_level = 8 rare = 2 /datum/tech/syndicate name = "Illegal Technologies Research" desc = "The study of technologies that violate standard Nanotrasen regulations." id = "syndicate" - max_level = 0 // Don't count towards maxed research, since it's illegal. + level = 0 // Illegal tech level dont need to show in roundstart on console + max_level = 8 // Used for admin button so need max level like other tech rare = 4 /datum/tech/abductor name = "Alien Technologies Research" desc = "The study of technologies used by the advanced alien race known as Abductors." id = "abductor" + level = 0 // Alien tech level hide roundstart like illegal + max_level = 8 rare = 5 - level = 0 /* datum/tech/arcane diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index df0112bc7ed..b7b8bedf1cd 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -100,6 +100,10 @@ name = "oil slime extract" icon_state = "oil slime extract" +/obj/item/slime_extract/oil/blob_vore_act(obj/structure/blob/special/core/voring_core) + obj_destruction(MELEE) + + /obj/item/slime_extract/adamantine name = "adamantine slime extract" icon_state = "adamantine slime extract" diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm index 5ff39a32833..65c5e94b687 100644 --- a/code/modules/response_team/ert.dm +++ b/code/modules/response_team/ert.dm @@ -170,7 +170,7 @@ GLOBAL_VAR_INIT(ert_request_answered, FALSE) head_organ.sec_hair_colour = hair_c M.change_eye_color(eye_c) M.s_tone = skin_tone - head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) + head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species) head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) M.rename_character(null, "Безымянный") // Rewritten in /datum/outfit/job/centcom/response_team/pre_equip M.age = rand(23,35) diff --git a/code/modules/ruins/ruin_areas.dm b/code/modules/ruins/ruin_areas.dm index 2c7933ca8a8..7a6ce119091 100644 --- a/code/modules/ruins/ruin_areas.dm +++ b/code/modules/ruins/ruin_areas.dm @@ -9,6 +9,9 @@ ambientsounds = RUINS_SOUNDS sound_environment = SOUND_ENVIRONMENT_STONEROOM +/area/ruin/space + area_flags = NONE + /area/ruin/unpowered always_unpowered = FALSE @@ -31,6 +34,7 @@ /area/ruin/powered/space_bar name = "Space Bar" + area_flags = NONE /area/ruin/powered/shuttle name = "Shuttle" @@ -56,3 +60,4 @@ /area/ruin/spaceprison name = "Space Prison" icon_state = "spaceprison" + area_flags = NONE diff --git a/code/modules/space_management/space_level.dm b/code/modules/space_management/space_level.dm index 2e8d7575966..54cd586bf3b 100644 --- a/code/modules/space_management/space_level.dm +++ b/code/modules/space_management/space_level.dm @@ -1,7 +1,7 @@ /datum/space_level var/name = "Your config settings failed, you need to fix this for the datum space levels to work" var/zpos = 1 - var/flags = list() // We'll use this to keep track of whether you can teleport/etc + var/list/flags = list() // We'll use this to keep track of whether you can teleport/etc // Map transition stuff var/list/neighbors = list() diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 4973d3ce5a3..bac31213c75 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -103,7 +103,9 @@ if("Windows") part_type = WINDOW else - var/coloradd = input(user, "Choose a color", "Color") as color + var/coloradd = tgui_input_color(user, "Choose a color", "Color") + if(isnull(coloradd)) + return colors[part_type] = coloradd if(!has_paint) has_paint = 1 @@ -291,7 +293,7 @@ update_icons() -/obj/spacepod/proc/repair_damage(var/repair_amount) +/obj/spacepod/repair_damage(repair_amount) if(health) health = min(initial(health), health + repair_amount) update_icons() diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index d639b945111..36a1be677b4 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -17,7 +17,7 @@ /obj/item/shard = 60, /obj/item/scissors = 12, /obj/item/twohanded/chainsaw = 1, - /obj/item/claymore = 6, + /obj/item/melee/claymore = 6, /obj/item/melee/energy = 6, /obj/item/pen/edagger = 6, ) diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index ae6d740baef..58e42b02049 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -208,12 +208,6 @@ /obj/item/organ/internal/cyberimp/arm/gun/laser/l parent_organ_zone = BODY_ZONE_L_ARM -/obj/item/organ/internal/cyberimp/arm/gun/laser/Initialize(mapload) - . = ..() - var/obj/item/organ/internal/cyberimp/arm/gun/laser/laserphasergun = locate(/obj/item/gun/energy/laser/mounted) in contents - laserphasergun.icon = icon //No invisible laser guns kthx - laserphasergun.icon_state = icon_state - /obj/item/organ/internal/cyberimp/arm/gun/taser name = "arm-mounted taser implant" desc = "A variant of the arm cannon implant that fires electrodes and disabler shots. The cannon emerges from the subject's arm and remains inside when not in use." diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm index c23dcee60b4..8f7ce1da9f5 100644 --- a/code/modules/surgery/organs/augments_eyes.dm +++ b/code/modules/surgery/organs/augments_eyes.dm @@ -42,11 +42,18 @@ /obj/item/organ/internal/cyberimp/eyes/emp_act(severity) if(!owner || emp_proof) return + if(severity > 1) if(prob(10 * severity)) return + to_chat(owner, span_warning("Static obfuscates your vision!")) - owner.flash_eyes(3, visual = TRUE) + + if(HAS_TRAIT(owner, TRAIT_ADVANCED_CYBERIMPLANTS)) + owner.EyeBlurry(1.5 SECONDS) + else + owner.flash_eyes(3, visual = TRUE) + /obj/item/organ/internal/cyberimp/eyes/meson name = "Meson scanner implant" diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index 2bc4b98d70a..028a5124b41 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -8,6 +8,7 @@ pickup_sound = 'sound/items/handling/component_pickup.ogg' drop_sound = 'sound/items/handling/component_drop.ogg' + /obj/item/organ/internal/cyberimp/New(var/mob/M = null) . = ..() if(implant_overlay) @@ -15,9 +16,15 @@ overlay.color = implant_color overlays |= overlay + /obj/item/organ/internal/cyberimp/emp_act() return // These shouldn't be hurt by EMPs in the standard way + +/obj/item/organ/internal/cyberimp/can_insert(mob/living/user, mob/living/carbon/target, fail_message = "Данное устройство не предусмотрено для существ с подобной анатомией.") + . = ..() + + //[[[[BRAIN]]]] /obj/item/organ/internal/cyberimp/brain @@ -27,6 +34,7 @@ implant_overlay = "brain_implant_overlay" parent_organ_zone = BODY_ZONE_HEAD + /obj/item/organ/internal/cyberimp/brain/emp_act(severity) if(!owner || emp_proof) return @@ -49,6 +57,7 @@ origin_tech = "materials=4;programming=5;biotech=4" actions_types = list(/datum/action/item_action/organ_action/toggle) + /obj/item/organ/internal/cyberimp/brain/anti_drop/ui_action_click(mob/user, datum/action/action, leftclick) active = !active if(active) @@ -144,6 +153,7 @@ ui_action_click() return ..() + /obj/item/organ/internal/cyberimp/brain/anti_stun name = "CNS Rebooter implant" desc = "This implant will automatically give you back control over your central nervous system, reducing downtime when stunned. Incompatible with the Neural Jumpstarter." @@ -151,14 +161,17 @@ slot = INTERNAL_ORGAN_BRAIN_ANTISTUN origin_tech = "materials=5;programming=4;biotech=5" + /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened name = "Hardened CNS Rebooter implant" emp_proof = TRUE + /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened/Initialize(mapload) . = ..() desc += " The implant has been hardened. It is invulnerable to EMPs." + /obj/item/organ/internal/cyberimp/brain/anti_stun/on_life() ..() if(crit_fail) @@ -166,6 +179,7 @@ if(owner.getStaminaLoss() > 60) owner.adjustStaminaLoss(-9) + /obj/item/organ/internal/cyberimp/brain/anti_stun/emp_act(severity) ..() if(crit_fail || emp_proof) @@ -173,15 +187,18 @@ crit_fail = TRUE addtimer(CALLBACK(src, PROC_REF(reboot)), 90 / severity) + /obj/item/organ/internal/cyberimp/brain/anti_stun/proc/reboot() crit_fail = FALSE + /obj/item/organ/internal/cyberimp/brain/anti_stun/hardened name = "Hardened CNS Rebooter implant" desc = "A military-grade version of the standard implant, for NT's more elite forces." origin_tech = "materials=6;programming=5;biotech=5" emp_proof = TRUE + /obj/item/organ/internal/cyberimp/brain/anti_sleep name = "Neural Jumpstarter implant" desc = "This implant will automatically attempt to jolt you awake when it detects you have fallen unconscious. Has a short cooldown, incompatible with the CNS Rebooter." @@ -190,6 +207,7 @@ origin_tech = "materials=5;programming=4;biotech=5" var/cooldown = FALSE + /obj/item/organ/internal/cyberimp/brain/anti_sleep/on_life() ..() if(crit_fail) @@ -201,10 +219,12 @@ cooldown = TRUE addtimer(CALLBACK(src, PROC_REF(sleepy_timer_end)), 50) + /obj/item/organ/internal/cyberimp/brain/anti_sleep/proc/sleepy_timer_end() cooldown = FALSE to_chat(owner, span_notice("You hear a small beep in your head as your Neural Jumpstarter finishes recharging.")) + /obj/item/organ/internal/cyberimp/brain/anti_sleep/emp_act(severity) . = ..() if(crit_fail || emp_proof) @@ -214,22 +234,26 @@ cooldown = TRUE addtimer(CALLBACK(src, PROC_REF(reboot)), 90 / severity) + /obj/item/organ/internal/cyberimp/brain/anti_sleep/proc/reboot() crit_fail = FALSE cooldown = FALSE + /obj/item/organ/internal/cyberimp/brain/anti_sleep/hardened name = "Hardened Neural Jumpstarter implant" desc = "A military-grade version of the standard implant, for NT's more elite forces." origin_tech = "materials=6;programming=5;biotech=5" emp_proof = TRUE + /obj/item/organ/internal/cyberimp/brain/anti_sleep/hardened/compatible name = "Hardened Neural Jumpstarter implant" desc = "A military-grade version of the standard implant, for NT's more elite forces. This one is compatible with the CNS Rebooter implant." slot = INTERNAL_ORGAN_BRAIN_ANTISLEEP emp_proof = TRUE + /obj/item/organ/internal/cyberimp/brain/clown_voice name = "Comical implant" desc = "Uh oh." @@ -237,42 +261,12 @@ slot = INTERNAL_ORGAN_BRAIN_CLOWNVOICE origin_tech = "materials=2;biotech=2" -/obj/item/organ/internal/cyberimp/brain/speech_translator //actual translating done in human/handle_speech_problems - name = "Speech translator implant" - desc = "While known as a translator, this implant actually generates speech based on the user's thoughts when activated, completely bypassing the need to speak." - implant_color = "#C0C0C0" - slot = INTERNAL_ORGAN_BRAIN_SPEECHTRANSLATOR - w_class = WEIGHT_CLASS_TINY - origin_tech = "materials=4;biotech=6" - actions_types = list(/datum/action/item_action/organ_action/toggle) - var/active = TRUE - var/speech_span = "" - var/speech_verb = "states" - -/obj/item/organ/internal/cyberimp/brain/speech_translator/clown - name = "Comical speech translator implant" - implant_color = "#DEDE00" - speech_span = "sans" - -/obj/item/organ/internal/cyberimp/brain/speech_translator/emp_act(severity) - if(emp_proof) - return - if(owner && active) - to_chat(owner, span_notice("Your translator's safeties trigger, it is now turned off.")) - active = FALSE - -/obj/item/organ/internal/cyberimp/brain/speech_translator/ui_action_click(mob/user, datum/action/action, leftclick) - if(owner && !active) - to_chat(owner, span_notice("You turn on your translator implant.")) - active = TRUE - else if(owner && active) - to_chat(owner, span_notice("You turn off your translator implant.")) - active = FALSE //[[[[MOUTH]]]] /obj/item/organ/internal/cyberimp/mouth parent_organ_zone = BODY_ZONE_PRECISE_MOUTH + /obj/item/organ/internal/cyberimp/mouth/breathing_tube name = "breathing tube implant" desc = "This simple implant adds an internals connector to your back, allowing you to use internals without a mask and protecting you from being choked." @@ -281,6 +275,7 @@ w_class = WEIGHT_CLASS_TINY origin_tech = "materials=2;biotech=3" + /obj/item/organ/internal/cyberimp/mouth/breathing_tube/emp_act(severity) if(emp_proof) return @@ -288,6 +283,7 @@ to_chat(owner, span_warning("Your breathing tube suddenly closes!")) owner.AdjustLoseBreath(4 SECONDS) + //[[[[CHEST]]]] /obj/item/organ/internal/cyberimp/chest name = "cybernetic torso implant" @@ -296,6 +292,7 @@ implant_overlay = "chest_implant_overlay" parent_organ_zone = BODY_ZONE_CHEST + /obj/item/organ/internal/cyberimp/chest/nutriment name = "Nutriment pump implant" desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are starving." @@ -325,6 +322,7 @@ owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning to_chat(owner, span_warning("You feel like your insides are burning.")) + /obj/item/organ/internal/cyberimp/chest/nutriment/plus name = "Nutriment pump implant PLUS" desc = "This implant will synthesize a small amount of nutriment and pumps it directly into your bloodstream when you are hungry." @@ -334,6 +332,23 @@ poison_amount = 10 origin_tech = "materials=4;powerstorage=3;biotech=3" + +/obj/item/organ/internal/cyberimp/chest/nutriment/plus/insert(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) + if(HAS_TRAIT(target, TRAIT_ADVANCED_CYBERIMPLANTS)) + hunger_modificator = 0.2 + ADD_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + + . = ..() + + +/obj/item/organ/internal/cyberimp/chest/nutriment/plus/remove(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) + if(HAS_TRAIT_FROM(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src))) + hunger_modificator = initial(hunger_modificator) + REMOVE_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + + . = ..() + + /obj/item/organ/internal/cyberimp/chest/nutriment_old name = "Nutriment pump implant" desc = "This implant with synthesize and pump into your bloodstream a small amount of nutriment when you are starving." @@ -345,6 +360,7 @@ slot = INTERNAL_ORGAN_STOMACH origin_tech = "materials=2;powerstorage=2;biotech=2" + /obj/item/organ/internal/cyberimp/chest/nutriment_old/on_life() if(!owner) return @@ -362,15 +378,18 @@ owner.adjust_nutrition(50) addtimer(CALLBACK(src, PROC_REF(synth_cool)), 50) + /obj/item/organ/internal/cyberimp/chest/nutriment_old/proc/synth_cool() synthesizing = FALSE + /obj/item/organ/internal/cyberimp/chest/nutriment_old/emp_act(severity) if(!owner || emp_proof) return owner.reagents.add_reagent("????",poison_amount / severity) //food poisoning to_chat(owner, span_warning("You feel like your insides are burning.")) + /obj/item/organ/internal/cyberimp/chest/nutriment_old/plus name = "Nutriment pump implant PLUS" desc = "This implant will synthesize and pump into your bloodstream a small amount of nutriment when you are hungry." @@ -380,6 +399,7 @@ poison_amount = 10 origin_tech = "materials=4;powerstorage=3;biotech=3" + /obj/item/organ/internal/cyberimp/chest/reviver name = "Reviver implant" desc = "This implant will attempt to revive you if you lose consciousness. For the faint of heart!" @@ -391,14 +411,17 @@ var/reviving = FALSE var/cooldown = 0 + /obj/item/organ/internal/cyberimp/chest/reviver/hardened name = "Hardened reviver implant" emp_proof = TRUE + /obj/item/organ/internal/cyberimp/chest/reviver/hardened/Initialize(mapload) . = ..() desc += " The implant has been hardened. It is invulnerable to EMPs." + /obj/item/organ/internal/cyberimp/chest/reviver/on_life() if(cooldown > world.time || owner.suiciding) // don't heal while you're in cooldown! return @@ -448,6 +471,7 @@ H.set_heartattack(TRUE) addtimer(CALLBACK(src, PROC_REF(undo_heart_attack)), 600 / severity) + /obj/item/organ/internal/cyberimp/chest/reviver/proc/undo_heart_attack() var/mob/living/carbon/human/H = owner if(!istype(H)) @@ -456,6 +480,7 @@ if(H.stat == CONSCIOUS) to_chat(H, span_notice("You feel your heart beating again!")) + //BOX O' IMPLANTS /obj/item/storage/box/cyber_implants diff --git a/code/modules/surgery/organs/autoimplanter.dm b/code/modules/surgery/organs/autoimplanter.dm index 7c305491918..1653e5e1d02 100644 --- a/code/modules/surgery/organs/autoimplanter.dm +++ b/code/modules/surgery/organs/autoimplanter.dm @@ -8,6 +8,7 @@ usesound = 'sound/weapons/circsawhit.ogg' var/obj/item/organ/internal/cyberimp/storedorgan + /obj/item/autoimplanter/old icon_state = "autoimplanter" @@ -22,15 +23,19 @@ /obj/item/autoimplanter/proc/autoimplant(mob/living/carbon/human/user) if(!ishuman(user)) return FALSE + if(!storedorgan) to_chat(user, span_warning("Киберимплант не обнаружен.")) return FALSE + if(!user.bodyparts_by_name[check_zone(storedorgan.parent_organ_zone)]) to_chat(user, span_warning("Отсутствует требуемая часть тела!")) return FALSE - if(HAS_TRAIT(user, TRAIT_NO_CYBERIMPLANTS)) - to_chat(user, span_warning("Ваш вид неспособен принять этот киберимплант!")) + + if(!storedorgan.can_insert(target = user) || HAS_TRAIT(user, TRAIT_NO_CYBERIMPLANTS)) //make it silent + to_chat(user, span_warning("Ваш вид не подходит для установки этого киберимпланта!")) return FALSE + storedorgan.insert(user)//insert stored organ into the user user.visible_message( span_notice("[user] активиру[pluralize_ru(user.gender,"ет","ют")] автоимплантер и вы слышите недолгий механический шум."), @@ -38,32 +43,39 @@ ) playsound(get_turf(user), usesound, 50, TRUE) storedorgan = null + return TRUE /obj/item/autoimplanter/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/organ/internal/cyberimp)) - add_fingerprint(user) - if(storedorgan) - to_chat(user, span_warning("В устройстве уже установлен другой киберимплант.")) - return ATTACK_CHAIN_PROCEED - if(!user.drop_transfer_item_to_loc(I, src)) - return ..() - storedorgan = I - to_chat(user, span_notice("Вы установили [I.name] в автоимплантер.")) - return ATTACK_CHAIN_BLOCKED_ALL + if(!istype(I, /obj/item/organ/internal/cyberimp)) + return ..() + + add_fingerprint(user) + + if(storedorgan) + balloon_alert(user, "слот для киберимпланта занят!") + return ATTACK_CHAIN_PROCEED + + if(!user.drop_transfer_item_to_loc(I, src)) + return ..() - return ..() + storedorgan = I + balloon_alert(user, "киберимплант установлен") + return ATTACK_CHAIN_BLOCKED_ALL /obj/item/autoimplanter/screwdriver_act(mob/living/user, obj/item/I) . = TRUE + if(!storedorgan) add_fingerprint(user) to_chat(user, span_notice("Устройство не содержит киберимплантов.")) return . + if(!I.use_tool(src, user, volume = I.tool_volume)) return . + storedorgan.forceMove(drop_location()) storedorgan.add_fingerprint(user) storedorgan = null @@ -78,6 +90,7 @@ . = ..() if(!.) return . + visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел.")) new /obj/effect/decal/cleanable/ash(get_turf(src)) user.temporarily_remove_item_from_inventory(src, force = TRUE) @@ -87,8 +100,10 @@ /obj/item/autoimplanter/oneuse/screwdriver_act(mob/living/user, obj/item/I) var/self_destruct = !isnull(storedorgan) . = ..() + if(!self_destruct) return . + visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел.")) new /obj/effect/decal/cleanable/ash(get_turf(src)) user.temporarily_remove_item_from_inventory(src, force = TRUE) @@ -124,9 +139,11 @@ . = ..() if(!.) return . + uses-- if(uses > 0) return . + visible_message(span_warning("Автоимплантер зловеще пищит и через мгновение вспыхивает, оставляя только пепел.")) new /obj/effect/decal/cleanable/ash(get_turf(src)) user.temporarily_remove_item_from_inventory(src, force = TRUE) @@ -135,5 +152,6 @@ /obj/item/autoimplanter/traitor/examine(mob/user) . = ..() + if(uses) . += span_notice("Осталось использований: [uses].") diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index bc7b2659c91..e4b3e75dc3a 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -24,7 +24,7 @@ if(bodytemperature >= TCRYO && !HAS_TRAIT(src, TRAIT_NO_CLONE)) //cryosleep or husked people do not pump the blood. if(!HAS_TRAIT(src, TRAIT_NO_BLOOD_RESTORE) && blood_volume < BLOOD_VOLUME_NORMAL) - blood_volume += 0.1 // regenerate blood VERY slowly + AdjustBlood(0.1) // regenerate blood VERY slowly //Effects of bloodloss @@ -84,12 +84,17 @@ /mob/living/carbon/proc/bleed(amt) if(!blood_volume) return FALSE + . = TRUE - blood_volume = max(blood_volume - amt, 0) + + AdjustBlood(-amt) + if(!isturf(loc)) //Blood loss still happens in locker, floor stays clean return . + if(amt >= 10) add_splatter_floor(loc) + else add_splatter_floor(loc, small_drip = TRUE) @@ -110,12 +115,16 @@ /mob/living/carbon/proc/bleed_internal(amt) if(!blood_volume) return FALSE + . = TRUE - blood_volume = max(blood_volume - amt, 0) + + AdjustBlood(-amt) + if(prob(10 * amt)) // +5% chance per internal bleeding site that we'll cough up blood on a given tick. - custom_emote(EMOTE_AUDIBLE, "кашля%(ет,ют)% кровью!") + custom_emote(EMOTE_AUDIBLE, "кашля%(ет, ют)% кровью!") add_splatter_floor(loc, small_drip = TRUE) return . + // +2.5% chance per internal bleeding site that we'll cough up blood on a given tick. // Must be bleeding internally in more than one place to have a chance at this. if(amt >= 1 && prob(5 * amt)) @@ -134,12 +143,41 @@ return . blood_reagent.reaction_turf(loc, amt * EXOTIC_BLEED_MULTIPLIER, dna.species.blood_color) +/mob/living/proc/AdjustBlood(amount = 0) + if(HAS_TRAIT(src, TRAIT_NO_BLOOD)) + return FALSE + + if(SEND_SIGNAL(src, COMSIG_LIVING_BLOOD_ADJUST, amount) & COMPONENT_PREVENT_BLOODLOSS) + return FALSE + + blood_volume = max(round(blood_volume + amount, DAMAGE_PRECISION), 0) + SEND_SIGNAL(src, COMSIG_LIVING_BLOOD_ADJUSTED, amount) + + return TRUE + +/mob/living/carbon/human/AdjustBlood(amount = 0, bleed_mode_affect = FALSE) + if(bleed_mode_affect) + amount *= physiology.bleed_mod + + return ..(amount) + +/mob/living/proc/setBlood(amount) + if(HAS_TRAIT(src, TRAIT_NO_BLOOD)) + return FALSE + + if(SEND_SIGNAL(src, COMSIG_LIVING_EARLY_SET_BLOOD, amount) & COMPONENT_PREVENT_BLOODLOSS) + return FALSE + + blood_volume = max(round(amount, DAMAGE_PRECISION), 0) + SEND_SIGNAL(src, COMSIG_LIVING_SET_BLOOD, amount) + + return TRUE /mob/living/proc/restore_blood() - blood_volume = initial(blood_volume) + setBlood(initial(blood_volume)) /mob/living/carbon/human/restore_blood() - blood_volume = BLOOD_VOLUME_NORMAL + setBlood(BLOOD_VOLUME_NORMAL) bleed_rate = 0 /**************************************************** @@ -160,7 +198,7 @@ if(!blood_id) return 0 - blood_volume -= amount + AdjustBlood(-amount) var/list/blood_data = get_blood_data(blood_id) @@ -177,7 +215,7 @@ C.reagents.add_reagent("toxin", amount * 0.5) return 1 - C.blood_volume = min(C.blood_volume + round(amount, 0.1), BLOOD_VOLUME_NORMAL) + C.setBlood(min(C.blood_volume + round(amount, 0.1), BLOOD_VOLUME_NORMAL)) return 1 AM.reagents.add_reagent(blood_id, amount, blood_data, bodytemperature) diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 353c02d666f..8665b32915c 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -54,9 +54,17 @@ /obj/item/organ/internal/ears/cybernetic/emp_act(severity) if(emp_proof) return + ..() internal_receive_damage(30) + if(!iscarbon(owner)) return + var/mob/living/carbon/C = owner - C.AdjustDeaf(120 SECONDS) + var/losstime = 120 SECONDS + + if(HAS_TRAIT(C, TRAIT_ADVANCED_CYBERIMPLANTS)) + losstime /= 3 + + C.AdjustDeaf(losstime) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index df6761333d9..b7e3de60b04 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -1,3 +1,4 @@ + /obj/item/organ/internal/eyes name = "eyeballs" icon_state = "eyes" @@ -16,6 +17,8 @@ var/see_in_dark = 2 var/see_invisible = SEE_INVISIBLE_LIVING var/lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + /// Modifies examine time for living mobs. Uses in /mob/living/run_examinate(atom/target) + var/examine_mod = 1 /obj/item/organ/internal/eyes/proc/update_colour() dna.write_eyes_attributes(src) diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 999b0d2e5af..f24c4b2b7c5 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -8,26 +8,31 @@ dead_icon = "heart-off" var/icon_base = "heart" + /obj/item/organ/internal/heart/update_icon_state() if(beating) icon_state = "[icon_base]-on" else icon_state = "[icon_base]-off" + /obj/item/organ/internal/heart/remove(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) if(!special) addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 12 SECONDS) . = ..() + /obj/item/organ/internal/heart/emp_act(intensity) if(!is_robotic() || emp_proof) return Stop() + /obj/item/organ/internal/heart/necrotize(silent = FALSE) if(..()) Stop() + /obj/item/organ/internal/heart/attack_self(mob/user) ..() if(is_dead()) @@ -37,29 +42,35 @@ Restart() addtimer(CALLBACK(src, PROC_REF(stop_if_unowned)), 80) + /obj/item/organ/internal/heart/safe_replace(mob/living/carbon/human/target) Restart() ..() + /obj/item/organ/internal/heart/proc/stop_if_unowned() if(!owner) Stop() + /obj/item/organ/internal/heart/proc/Stop() beating = FALSE update_icon() return TRUE + /obj/item/organ/internal/heart/proc/Restart() beating = TRUE update_icon() return TRUE + /obj/item/organ/internal/heart/prepare_eat() var/obj/S = ..() S.icon_state = dead_icon return S + /obj/item/organ/internal/heart/cursed name = "cursed heart" desc = "it needs to be pumped..." @@ -105,14 +116,17 @@ else last_pump = world.time //lets be extra fair *sigh* + /obj/item/organ/internal/heart/cursed/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) . = ..() if(owner) to_chat(owner, span_userdanger("Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!")) + /datum/action/item_action/organ_action/cursed_heart name = "pump your blood" + //You are now brea- pumping blood manually /datum/action/item_action/organ_action/cursed_heart/Trigger(left_click = TRUE) . = ..() @@ -152,6 +166,7 @@ pickup_sound = 'sound/items/handling/component_pickup.ogg' drop_sound = 'sound/items/handling/component_drop.ogg' + /obj/item/organ/internal/heart/cybernetic/upgraded name = "upgraded cybernetic heart" desc = "A more advanced version of a cybernetic heart. Grants the user additional stamina and heart stability, but the electronics are vulnerable to shock." @@ -161,6 +176,23 @@ var/emagged = FALSE var/attempted_restart = FALSE + +/obj/item/organ/internal/heart/cybernetic/upgraded/insert(mob/living/carbon/target, special) + . = ..() + + if(HAS_TRAIT(target, TRAIT_ADVANCED_CYBERIMPLANTS)) + target.stam_regen_start_modifier *= 0.5 + ADD_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + + +/obj/item/organ/internal/heart/cybernetic/upgraded/remove(mob/living/carbon/human/target, special) + if(HAS_TRAIT_FROM(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src))) + target.stam_regen_start_modifier /= 0.5 + REMOVE_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + + . = ..() + + /obj/item/organ/internal/heart/cybernetic/upgraded/on_life() if(!ishuman(owner)) return @@ -240,9 +272,14 @@ /obj/item/organ/internal/heart/cybernetic/upgraded/emp_act(severity) ..() + if(emp_proof) return - necrotize() + + if(HAS_TRAIT(owner, TRAIT_ADVANCED_CYBERIMPLANTS)) + Stop() + else + necrotize() /obj/item/organ/internal/heart/cybernetic/upgraded/shock_organ(intensity) diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index f348dd3cd8a..ee2d4a522e1 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -54,8 +54,13 @@ /obj/item/organ/internal/lungs/emp_act() if(!is_robotic() || emp_proof) return + if(owner) - owner.LoseBreath(40 SECONDS) + var/losstime = 40 SECONDS + if(HAS_TRAIT(owner, TRAIT_ADVANCED_CYBERIMPLANTS)) + losstime /= 2 + + owner.LoseBreath(losstime) /obj/item/organ/internal/lungs/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT) ..() @@ -334,6 +339,30 @@ cold_level_3_damage = -COLD_GAS_DAMAGE_LEVEL_3 cold_damage_types = list(BRUTE = 0.5, BURN = 0.25) + var/cooling_start_temp = DRASK_LUNGS_COOLING_START_TEMP + var/cooling_stop_temp = DRASK_LUNGS_COOLING_STOP_TEMP + +/obj/item/organ/internal/lungs/drask/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT) + . = ..() + + if(!.) + return FALSE + + RegisterSignal(owner, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT, PROC_REF(regulate_temperature)) + +/obj/item/organ/internal/lungs/drask/proc/regulate_temperature(mob/living/source, datum/gas_mixture/environment) + SIGNAL_HANDLER + + if(source.stat == DEAD) + return + + if(owner.bodytemperature > cooling_start_temp && environment.temperature <= cooling_stop_temp) + owner.adjust_bodytemperature(-5) + +/obj/item/organ/internal/lungs/drask/remove(mob/living/user, special = ORGAN_MANIPULATION_DEFAULT) + UnregisterSignal(owner, COMSIG_HUMAN_EARLY_HANDLE_ENVIRONMENT) + return ..() + /obj/item/organ/internal/lungs/cybernetic name = "cybernetic lungs" desc = "A cybernetic version of the lungs found in traditional humanoid entities. It functions the same as an organic lung and is merely meant as a replacement." @@ -387,3 +416,17 @@ cold_level_1_threshold = 200 cold_level_2_threshold = 140 cold_level_3_threshold = 100 + +/obj/item/organ/internal/lungs/cybernetic/upgraded/insert(mob/living/carbon/human/target, special) + . = ..() + + if(HAS_TRAIT(target, TRAIT_ADVANCED_CYBERIMPLANTS)) + target.physiology.oxy_mod -= 0.5 + ADD_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + +/obj/item/organ/internal/lungs/cybernetic/upgraded/remove(mob/living/carbon/human/target, special) + if(HAS_TRAIT_FROM(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src))) + target.physiology.oxy_mod += 0.5 + REMOVE_TRAIT(target, TRAIT_CYBERIMP_IMPROVED, UNIQUE_TRAIT_SOURCE(src)) + + . = ..() diff --git a/code/modules/surgery/organs/organ.dm b/code/modules/surgery/organs/organ.dm index 462f89ebc09..39475908614 100644 --- a/code/modules/surgery/organs/organ.dm +++ b/code/modules/surgery/organs/organ.dm @@ -66,11 +66,15 @@ /obj/item/organ/Destroy() STOP_PROCESSING(SSobj, src) + if(owner) remove(owner, ORGAN_MANIPULATION_NOEFFECT) + QDEL_LIST_ASSOC_VAL(autopsy_data) + if(dna) QDEL_NULL(dna) + return ..() @@ -87,6 +91,7 @@ if(is_robotic() && !species_type) // no DNA for cybernetics, except IPC parts if(update_blood) update_blood() + return if(!dna) @@ -118,6 +123,7 @@ /obj/item/organ/proc/update_blood() if(!dna || (TRAIT_NO_BLOOD in dna.species.inherent_traits)) return + LAZYSET(blood_DNA, dna.unique_enzymes, dna.blood_type) @@ -128,13 +134,17 @@ /obj/item/organ/proc/necrotize(silent = FALSE) if(status & (ORGAN_ROBOT|ORGAN_DEAD)) return FALSE + damage = max_damage status |= ORGAN_DEAD STOP_PROCESSING(SSobj, src) + if(dead_icon && !is_robotic()) icon_state = dead_icon + if(owner && vital) owner.death() + return TRUE @@ -145,6 +155,7 @@ /obj/item/organ/proc/unnecrotize() if(!is_dead()) return FALSE + status &= ~ORGAN_DEAD return TRUE @@ -153,12 +164,15 @@ if(istype(I, /obj/item/stack/nanopaste)) add_fingerprint(user) var/obj/item/stack/nanopaste/nanopaste = I + if(!is_robotic()) to_chat(user, span_warning("The [nanopaste.name] can only be used on robotic bodyparts.")) return ATTACK_CHAIN_PROCEED + if(!nanopaste.use(1)) to_chat(user, span_warning("You need at least one unit of [nanopaste] to proceed.")) return ATTACK_CHAIN_PROCEED + to_chat(user, span_notice("You have repaired the damage on [src].")) rejuvenate() return ATTACK_CHAIN_PROCEED_SUCCESS @@ -184,10 +198,13 @@ // Maybe scale it down a bit, have it REALLY kick in once past the basic infection threshold // Another mercy for surgeons preparing transplant organs germ_level++ + if(germ_level >= INFECTION_LEVEL_ONE) germ_level += rand(2,6) + if(germ_level >= INFECTION_LEVEL_TWO) germ_level += rand(2,6) + if(germ_level >= INFECTION_LEVEL_THREE) necrotize() @@ -211,12 +228,15 @@ for(var/typepath in preserved_holders) if(is_found_within(typepath)) return TRUE + if(istype(loc,/obj/item/mmi)) // So a brain can slowly recover from being left out of an MMI germ_level = max(0, germ_level - 1) return TRUE + if(istype(loc, /mob/living/simple_animal/hostile/headslug) || istype(loc, /obj/item/organ/internal/body_egg/changeling_egg)) germ_level = 0 // weird stuff might happen, best to be safe return TRUE + if(isturf(loc)) var/is_in_freezer = FALSE if(world.time - last_freezer_update_time > freezer_update_period) @@ -342,6 +362,7 @@ /obj/item/organ/proc/heal_internal_damage(amount, robo_repair = FALSE) if(is_robotic() && !robo_repair) return + damage = max(damage - amount, 0) @@ -372,12 +393,13 @@ if(owner?.stat != DEAD && vital && !special) add_attack_logs(user, owner, "Removed vital organ ([src])") owner.death() + owner = null return src /obj/item/organ/proc/replaced(mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) - return // Nothing uses this, it is always overridden + return // A version of `replaced` that "flattens" the process of insertion, making organs "Plug'n'play" @@ -396,6 +418,7 @@ /obj/item/organ/proc/has_damage() if(damage) return TRUE + return FALSE /obj/item/organ/proc/is_robotic() @@ -404,6 +427,7 @@ /obj/item/organ/serialize() var/data = ..() + if(status != 0) data["status"] = status @@ -411,6 +435,7 @@ // the owner if(!(owner && dna.unique_enzymes == owner.dna.unique_enzymes)) data["dna"] = dna.serialize() + return data diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index acb5ccfd3d3..08749b1b317 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -182,8 +182,10 @@ return var/obj/item/organ/external/replaced = owner.bodyparts_by_name[limb_zone] + if(!isnull(replaced)) replaced.remove(target, ORGAN_MANIPULATION_NOEFFECT) + owner.bodyparts_by_name[limb_zone] = src owner.bodyparts |= src @@ -439,6 +441,9 @@ return update_state() +/obj/item/organ/external/blob_act() + external_receive_damage(max_damage, forced = TRUE) + /obj/item/organ/external/emp_act(severity) if(!is_robotic() || emp_proof) return diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index e929d3fe9e6..7bfee0d24a4 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -8,6 +8,8 @@ /// Whether it shows up as an option to remove during surgery. var/unremovable = FALSE var/can_see_food = FALSE + /// Empty list == all species allowed + var/list/species_restrictions light_system = MOVABLE_LIGHT light_on = FALSE @@ -18,10 +20,30 @@ if(iscarbon(loc)) insert(loc) + if(species_type == /datum/species/diona) AddComponent(/datum/component/diona_internals) +// user = who operates on target. Optional for fail_message, can be null(silent check) +// target = the carbon we're testing for suitability +// fail_message = message that user will recieve if the checks failed. FALSE make it quiet even with "user" +/obj/item/organ/internal/proc/can_insert(mob/living/user, mob/living/carbon/target, fail_message = "Данное существо не способно принять этот орган!") + if(!LAZYLEN(species_restrictions)) + return TRUE + + if(!istype(target) && !target.dna?.species) // only carbons have species + return FALSE + + if(target.dna.species.name in species_restrictions) + return TRUE + + if(user && fail_message) + to_chat(user, span_warning(fail_message)) + + return FALSE + + /obj/item/organ/internal/proc/insert(mob/living/carbon/target, special = ORGAN_MANIPULATION_DEFAULT) if(!iscarbon(target) || owner == target) return FALSE @@ -31,6 +53,7 @@ do_pickup_animation(src, target) var/obj/item/organ/internal/replaced = target.get_organ_slot(slot) + if(replaced) replaced.remove(target, ORGAN_MANIPULATION_NOEFFECT) @@ -45,6 +68,7 @@ stack_trace("[src] attempted to insert into a [parent_organ_zone], but [parent_organ_zone] wasn't an organ! [atom_loc_line(h_target)]") else LAZYOR(parent.internal_organs, src) + h_target.update_int_organs() loc = null @@ -76,10 +100,13 @@ if(iscarbon(organ_owner)) organ_owner.internal_organs -= src + if(organ_owner.internal_organs_slot[slot] == src) organ_owner.internal_organs_slot[slot] = null + if(!special) send_signal = TRUE + if(vital && !special && organ_owner.stat != DEAD) organ_owner.death() @@ -107,6 +134,7 @@ /obj/item/organ/internal/emp_act(severity) if(!is_robotic() || emp_proof) return + switch(severity) if(1) internal_receive_damage(20, silent = TRUE) @@ -138,6 +166,7 @@ /obj/item/organ/internal/proc/prepare_eat() if(is_robotic()) return //no eating cybernetic implants! + var/obj/item/reagent_containers/food/snacks/organ/S = new S.name = name S.desc = desc @@ -151,6 +180,7 @@ /obj/item/organ/internal/attempt_become_organ(obj/item/organ/external/parent, mob/living/carbon/human/target, special = ORGAN_MANIPULATION_DEFAULT) if(parent_organ_zone != parent.limb_zone) return FALSE + insert(target, special) return TRUE @@ -172,6 +202,7 @@ return ..() var/obj/item/reagent_containers/food/snacks/snack = prepare_eat() + if(!snack) return ATTACK_CHAIN_PROCEED @@ -198,9 +229,11 @@ H.icon_base = "[slot]-c" H.dead_icon = "[slot]-c-off" H.update_icon() + else if("[slot]-c" in states) //Give the robotic organ its robotic organ icons if they exist. icon = icon('icons/obj/surgery.dmi') icon_state = "[slot]-c" + name = "cybernetic [slot]" ..() //Go apply all the organ flags/robotic statuses. @@ -217,12 +250,14 @@ for(var/datum/disease/appendicitis/A in M.diseases) A.cure() inflamed = TRUE + update_icon() . = ..() /obj/item/organ/internal/appendix/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() + if(inflamed) var/datum/disease/appendicitis/D = new D.Contract(M) @@ -230,8 +265,10 @@ /obj/item/organ/internal/appendix/prepare_eat() var/obj/S = ..() + if(inflamed) S.reagents.add_reagent("????", 5) + return S @@ -263,8 +300,10 @@ var/light_count = T.get_lumcount()*10 if(light_count > 4 && obj_integrity > 0) //Die in the light obj_integrity-- + else if(light_count < 2 && obj_integrity < max_integrity) //Heal in the dark obj_integrity++ + if(obj_integrity <= 0) visible_message(span_warning("[src] collapses in on itself!")) qdel(src) @@ -287,6 +326,7 @@ /obj/item/organ/internal/honktumor/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) ..() + M.force_gene_block(GLOB.clumsyblock, TRUE) M.force_gene_block(GLOB.comicblock, TRUE) organhonked = world.time @@ -334,6 +374,7 @@ /obj/item/organ/internal/honktumor/cursed/on_life() //No matter what you do, no matter who you are, no matter where you go, you're always going to be a fat, stuttering dimwit. ..() + owner.setBrainLoss(80) owner.set_nutrition(9000) owner.overeatduration = 9000 @@ -379,13 +420,16 @@ if(ishuman(owner)) var/mob/living/carbon/human/H = owner var/obj/item/organ/external/head/head_organ = H.get_organ(BODY_ZONE_HEAD) + if(!(head_organ.h_style == "Very Long Hair" || head_organ.h_style == "Mohawk")) if(prob(10)) head_organ.h_style = "Mohawk" else head_organ.h_style = "Very Long Hair" + head_organ.hair_colour = "#D8C078" H.update_hair() + if(!(head_organ.f_style == "Very Long Beard")) head_organ.f_style = "Very Long Beard" head_organ.facial_colour = "#D8C078" @@ -396,7 +440,9 @@ ..() if(!ishuman(owner)) return + var/germs_mod = owner.dna.species.germs_growth_mod * owner.physiology.germs_growth_mod + if(germ_level >= INFECTION_LEVEL_TWO && prob(3 * germs_mod)) // big message from every 1 damage is not good. If germs growth rate is big, it will spam the chat. internal_receive_damage(1, silent = prob(30 * germs_mod)) @@ -404,16 +450,19 @@ /mob/living/carbon/human/proc/check_infections() var/list/infections = list() + for(var/obj/item/organ/internal/organ as anything in internal_organs) if(organ.germ_level > 0) infections.Add(organ) + return infections /mob/living/carbon/human/proc/check_damaged_organs() var/list/damaged = list() + for(var/obj/item/organ/internal/organ as anything in internal_organs) if(organ.damage > 0) damaged.Add(organ) - return damaged + return damaged diff --git a/code/modules/surgery/organs/subtypes/grey.dm b/code/modules/surgery/organs/subtypes/grey.dm index 19b95cd4831..830978a8919 100644 --- a/code/modules/surgery/organs/subtypes/grey.dm +++ b/code/modules/surgery/organs/subtypes/grey.dm @@ -3,7 +3,7 @@ name = "grey liver" desc = "A small, odd looking liver." icon = 'icons/obj/species_organs/grey.dmi' - alcohol_intensity = 1.6 + alcohol_intensity = 1.4 /obj/item/organ/internal/brain/grey species_type = /datum/species/grey @@ -12,6 +12,7 @@ icon_state = "brain2" mmi_icon = 'icons/obj/species_organs/grey.dmi' mmi_icon_state = "mmi_full" + smart_mind = TRUE // nerd brains show us sci-hud and research scanner /obj/item/organ/internal/brain/grey/insert(mob/living/carbon/M, special = ORGAN_MANIPULATION_DEFAULT) . = ..() @@ -26,7 +27,8 @@ name = "grey eyeballs" desc = "They still look creepy and emotionless." icon = 'icons/obj/species_organs/grey.dmi' - see_in_dark = 5 + see_in_dark = 3 + examine_mod = EXAMINE_INSTANT // Insta carbon examine /obj/item/organ/internal/heart/grey species_type = /datum/species/grey diff --git a/code/modules/surgery/organs/subtypes/wryn.dm b/code/modules/surgery/organs/subtypes/wryn.dm index 2a106bd4d19..30777579d89 100644 --- a/code/modules/surgery/organs/subtypes/wryn.dm +++ b/code/modules/surgery/organs/subtypes/wryn.dm @@ -2,29 +2,43 @@ /obj/item/organ/internal/wryn/hivenode species_type = /datum/species/wryn name = "antennae" - icon = 'icons/mob/human_races/r_wryn.dmi' + icon = 'icons/obj/species_organs/wryn.dmi' icon_state = "antennae" parent_organ_zone = BODY_ZONE_HEAD slot = INTERNAL_ORGAN_HIVENODE + species_restrictions = list(SPECIES_WRYN) + /// Stored hair style, defines only on creation and changes original h_style when inserted + var/hair_style = "Normal antennae" -/obj/item/organ/internal/wryn/hivenode/insert(mob/living/carbon/human/M, special = ORGAN_MANIPULATION_DEFAULT) - ..() - M.add_language(LANGUAGE_WRYN) - var/obj/item/organ/external/head/head_organ = M.get_organ(BODY_ZONE_HEAD) - head_organ.h_style = "Antennae" - M.update_hair() - -/obj/item/organ/internal/wryn/hivenode/remove(mob/living/carbon/human/M, special = ORGAN_MANIPULATION_DEFAULT) - M.remove_language(LANGUAGE_WRYN) - var/obj/item/organ/external/head/head_organ = M.get_organ(BODY_ZONE_HEAD) - head_organ.h_style = "Bald" - M.update_hair() +/obj/item/organ/internal/wryn/hivenode/New(mob/living/carbon/carbon) + if(istype(carbon)) + var/obj/item/organ/external/head/head_organ = carbon.get_organ(BODY_ZONE_HEAD) + hair_style = head_organ.h_style + + return ..(carbon) + +/obj/item/organ/internal/wryn/hivenode/insert(mob/living/carbon/human/human, special = ORGAN_MANIPULATION_DEFAULT) . = ..() + human.add_language(LANGUAGE_WRYN) + var/obj/item/organ/external/head/head_organ = human.get_organ(BODY_ZONE_HEAD) + + head_organ.h_style = hair_style + human.update_hair() + +/obj/item/organ/internal/wryn/hivenode/remove(mob/living/carbon/human/human, special = ORGAN_MANIPULATION_DEFAULT) + human.remove_language(LANGUAGE_WRYN) + var/obj/item/organ/external/head/head_organ = human.get_organ(BODY_ZONE_HEAD) + + head_organ.h_style = "Bald" + human.update_hair() + + return ..() /obj/item/organ/internal/wryn/glands species_type = /datum/species/wryn name = "wryn wax glands" - icon_state = "eggsac" + icon = 'icons/obj/species_organs/wryn.dmi' + icon_state = "waxsac" parent_organ_zone = BODY_ZONE_PRECISE_MOUTH slot = INTERNAL_ORGAN_WAX_GLANDS var/datum/action/innate/honeycomb/honeycomb = new diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index e82c771914b..d62ce9e5a6b 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -186,12 +186,15 @@ GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby")) for(var/V in listeners) var/mob/living/L = V - if(L.mind && L.mind.devilinfo && findtext(message, L.mind.devilinfo.truename)) - var/start = findtext(message, L.mind.devilinfo.truename) - listeners = list(L) //let's be honest you're never going to find two devils with the same name - power_multiplier *= 5 //if you're a devil and god himself addressed you, you fucked up - //Cut out the name so it doesn't trigger commands - message = copytext(message, 0, start)+copytext(message, start + length(L.mind.devilinfo.truename), length(message) + 1) + var/datum/antagonist/devil/devilinfo = L.mind?.has_antag_datum(/datum/antagonist/devil) + + if(devilinfo && findtext(message, devilinfo?.info.truename)) + var/start = findtext(message, devilinfo.info.truename) + listeners = list(L) // let's be honest you're never going to find two devils with the same name + power_multiplier *= 5 // if you're a devil and god himself addressed you, you fucked up + // Cut out the name so it doesn't trigger commands + message = copytext(message, 0, start)+copytext(message, start + length(devilinfo.info.truename), LAZYLEN(message) + 1) + if(findtext(message, L.real_name) == 1) specific_listeners += L //focus on those with the specified name //Cut out the name so it doesn't trigger commands diff --git a/code/modules/surgery/organs/voice_translator.dm b/code/modules/surgery/organs/voice_translator.dm new file mode 100644 index 00000000000..19453d9a5f9 --- /dev/null +++ b/code/modules/surgery/organs/voice_translator.dm @@ -0,0 +1,629 @@ +#define DEFAULT_CHIP_SLOTS 1 +#define UPGRADE_SLOTS_GREY 2 + + + // TRANSLATORS // + +// Translators also fulfil the role of ‘vocal cords’ when there is a TRAIT_NO_VOCAL_CORDS in species inherent traits. +// With translator any mob can speak even muted, unless being emped. +// ANY Duct tape forcing mob with translator to whisper and keeps it off the radio + +/obj/item/organ/internal/cyberimp/mouth/translator // Lets make it some easier to make a new one. Write this if you want to make non-species translator. + name = "Just An Empty Translator" // You cant get it in-game. At least now + desc = "Может быть, учёные NanoTrasen заставят работать его позже..." + //icon = + //icon_state = + //origin_tech = + slot = INTERNAL_ORGAN_SPEECH_TRANSLATOR + w_class = WEIGHT_CLASS_TINY + /// List of languages, stored in this translator + var/list/datum/language/given_languages + /// Russian list of languages, stored in this translator + var/list/given_languages_rus + /// What types of translator storage upgrades can be attached to this translator. Empty = nothing + var/list/upgrade_with + /// List of stored languages chips + var/list/stored_chips + /// You cant place anything without opening lid with screwriver + var/open = FALSE + /// Inactive translator don't give you any languages. Affects by EMP + var/active = TRUE + /// Slot for stored storage upgrade module + var/obj/item/translator_upgrade/stored_upgrade + /// Maximum basic slots for translator without storage upgrade + var/maximum_slots = DEFAULT_CHIP_SLOTS + /// Toggles by decoder action, allows you to speak clearly with Wingdings disability + var/can_wingdings = FALSE + + action_icon = list(/datum/action/item_action/organ_action/translator_select_language = 'icons/mob/actions/actions.dmi',) + action_icon_state = list(/datum/action/item_action/organ_action/translator_select_language = "select_language") + actions_types = list(/datum/action/item_action/organ_action/translator_select_language) + var/datum/action/item_action/organ_action/wingdings_decoder/decoder + + +/obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator + name = "Psionic Voice Retranslator" + desc = "Необычный инопланетный имплант с маленьким экранчиком. Судя по всему, создан специально для греев." + ru_names = list( + NOMINATIVE = "ретранслятор псионического голоса", + GENITIVE = "ретранслятора псионического голоса", + DATIVE = "ретранслятору псионического голоса", + ACCUSATIVE = "ретранслятор псионического голоса", + INSTRUMENTAL = "ретранслятором псионического голоса", + PREPOSITIONAL = "ретрансляторе псионического голоса", + ) + icon = 'icons/obj/voice_translator.dmi' + icon_state = "pvr_implant" + given_languages = list() + upgrade_with = list(/obj/item/translator_upgrade/grey_retraslator) + origin_tech = "materials=2;biotech=3;engineering=3;programming=3;abductor=2" + species_restrictions = list(SPECIES_GREY, SPECIES_ABDUCTOR) + + +/obj/item/organ/internal/cyberimp/mouth/translator/New() + if(!..()) + return + + if(!LAZYLEN(given_languages)) + return + + for(var/lang_name in given_languages) + LAZYADD(given_languages, GLOB.all_languages[lang_name]) + + return TRUE + + +/obj/item/organ/internal/cyberimp/mouth/translator/grey_retraslator/New() + LAZYADD(given_languages, GLOB.all_languages[LANGUAGE_GALACTIC_COMMON]) // basic galcom for greys + LAZYADD(given_languages_rus, "Общегалактический") + + . = ..() + + +/obj/item/organ/internal/cyberimp/mouth/translator/examine(mob/user) + . = ..() + if(!Adjacent(user)) // Too far! + return + + var/message = (open ? "Крышка открыта. " : "Крышка закрыта. ") + message += "Установленные языки: " + message += english_list(given_languages_rus, nothing_text = "Отсутствуют", and_text = "и", final_comma_text = ".") + . += span_notice(message) + + +/obj/item/organ/internal/cyberimp/mouth/translator/can_insert(mob/living/user, mob/living/carbon/target) + if(!..()) + return FALSE + + if(!open) + return TRUE + + if(user) + balloon_alert(user, "крышка открыта!") + + return FALSE + + +/obj/item/organ/internal/cyberimp/mouth/translator/insert(mob/living/carbon/target, special) + . = ..() + + RegisterSignal(target, COMSIG_LANG_PRE_ACT, PROC_REF(check_language)) + + for(var/datum/language/lang as anything in given_languages) + target.add_language(lang.name) + + +/obj/item/organ/internal/cyberimp/mouth/translator/remove(mob/living/carbon/target, special) + if(!istype(target)) + return + + UnregisterSignal(target, COMSIG_LANG_PRE_ACT) + + for(var/datum/language/lang as anything in given_languages) + target.remove_language(lang.name) + + . = ..() + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/check_language(mob/living/carbon/C, language_name) + SIGNAL_HANDLER + + for(var/datum/language/lang as anything in given_languages) + if(language_name == lang.name) + return COMSIG_LANG_SECURED + + +/obj/item/organ/internal/cyberimp/mouth/translator/update_desc(updates) + . = ..() + + if(stored_upgrade) + desc += " Имеет установленный расширитель слотов." + else + desc = initial(desc) + + +/obj/item/organ/internal/cyberimp/mouth/translator/attackby(obj/item/I, mob/user, params) + if((istype(I, /obj/item/translator_chip))) + var/obj/item/translator_chip/chip = I + return install_chip(user, chip, silent = FALSE) + + else if(istype(I, /obj/item/translator_upgrade)) + if(stored_upgrade) + balloon_alert(user, "уже установлено!") + return FALSE + + return install_upgrade(user, I) + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/install_upgrade(mob/living/carbon/human/user, obj/item/translator_upgrade/upgrade) + if(!open) + balloon_alert(user, "крышка закручена!") + return + + if(!LAZYLEN(upgrade_with)) + balloon_alert(user, "не подлежит улучшению!") + return + + if(!(upgrade.type in upgrade_with)) + balloon_alert(user, "несовместимо!") + return + + balloon_alert(user, "установлено") + maximum_slots += upgrade.extra_slots + user.drop_transfer_item_to_loc(upgrade, src) + stored_upgrade = upgrade + update_appearance(UPDATE_DESC) + + +/obj/item/organ/internal/cyberimp/mouth/translator/attack_self(mob/user) + if(!open) + return FALSE + + if(!LAZYLEN(stored_chips)) + balloon_alert(user, "пусто!") + return FALSE + + var/obj/item/translator_chip/chip + if(LAZYLEN(stored_chips) == 1) + chip = stored_chips[1] + else + var/list/chip_languages = list() + for(var/obj/item/translator_chip/check_chip in stored_chips) + chip_languages[check_chip.stored_language_rus] = check_chip + + chip = tgui_input_list(user, "Выберите, чип какого языка вы хотите достать:", "Извлечение чипа", chip_languages) + chip = chip_languages[chip] + + if(!chip) // closed + return FALSE + + remove_chip(user, chip) + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/remove_chip(mob/living/carbon/human/user, obj/item/translator_chip/chip) + // user = who operates or who places the chip into translator + // chip = translator chip we are removing + if(!user || !chip) + return FALSE + + if(owner && chip.stored_language) //if translator inside someone + owner.remove_language(chip.stored_language.name) + + user.put_in_hands(chip) + LAZYREMOVE(stored_chips, chip) + + if(chip.stored_language) + LAZYREMOVE(given_languages, chip.stored_language) + + LAZYREMOVE(given_languages_rus, chip.stored_language_rus) + chip.on_remove(owner, src) + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/install_chip(mob/living/carbon/human/user, obj/item/translator_chip/chip, silent = TRUE, ignore_lid = FALSE) + if(!user || !chip) + return FALSE + + if(!open && !ignore_lid) // Forced installation ignoring the closed lid. Used on after_equip chip installation + if(!silent) + balloon_alert(user, "крышка закрыта!") + + return FALSE + + if(LAZYLEN(stored_chips) >= maximum_slots) + if(!silent) + balloon_alert(user, "нет места под чип!") + + return FALSE + + if(!chip.stored_language_rus) + if(!silent) + balloon_alert(user, "чип пустой!") + + return FALSE + + if(chip.stored_language_rus in given_languages_rus) + if(!silent) + balloon_alert(user, "уже установлено!") + + return FALSE + + if(!silent) + balloon_alert(user, "чип установлен") + + user.drop_transfer_item_to_loc(chip, src) + LAZYADD(stored_chips, chip) + + if(chip.stored_language) + LAZYADD(given_languages, chip.stored_language) + + LAZYADD(given_languages_rus, chip.stored_language_rus) + + if(owner && chip.stored_language) + owner.add_language(chip.stored_language.name) + + chip.on_install(owner, src) + + return TRUE + + +/obj/item/organ/internal/cyberimp/mouth/translator/screwdriver_act(mob/living/user, obj/item/I) + if(I.tool_behaviour != TOOL_SCREWDRIVER) + return + + if(!(I.use_tool(src, user, 2 SECONDS, volume = 50))) + return + + open = !open + balloon_alert(user, "крышка [open ? "откручена" : "закручена"]") + + +/obj/item/organ/internal/cyberimp/mouth/translator/multitool_act(mob/living/user, obj/item/I) + // you can remove an upgrade with multitool + if(!open || (I.tool_behaviour != TOOL_MULTITOOL)) + return + + if(!(I.use_tool(src, user, 2 SECONDS, volume = 50))) + return + + uninstall_upgrade(user) + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/uninstall_upgrade(mob/living/carbon/human/user) + if(!stored_upgrade) + balloon_alert(user, "нечего доставать!") + return FALSE + + if(LAZYLEN(stored_chips) > DEFAULT_CHIP_SLOTS) + balloon_alert(user, "мешают чипы!") + return FALSE + + maximum_slots -= stored_upgrade.extra_slots + user.put_in_hands(stored_upgrade) + balloon_alert(user, "улучшение извлечено") + stored_upgrade = null + + +/obj/item/organ/internal/cyberimp/mouth/translator/emp_act(severity) + if(emp_proof) + return + + if(!owner) + return + + turn_languages_off() + addtimer(CALLBACK(src, PROC_REF(turn_languages_on)), 20 SECONDS) + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/turn_languages_on() + active = TRUE + if(!owner) + return + + to_chat(owner, span_notice("[capitalize(declent_ru(NOMINATIVE))] снова работает!")) + for(var/datum/language/lang as anything in given_languages) + owner.add_language(lang.name) + + decoder.update_button_state() + + +/obj/item/organ/internal/cyberimp/mouth/translator/proc/turn_languages_off() + active = FALSE + can_wingdings = FALSE + to_chat(owner, span_warning("[capitalize(declent_ru(NOMINATIVE))] временно вышел из строя из-за воздействия ЭМИ!")) + do_sparks(3, FALSE, owner) + for(var/datum/language/lang as anything in given_languages) + owner.remove_language(lang.name) + + decoder.update_button_state() + + + // TRANSLATOR ACTION BUTTONS // + +/datum/action/item_action/organ_action/translator_select_language + name = "Выбрать используемый язык" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "select_language" + + +/datum/action/item_action/organ_action/translator_select_language/Trigger(left_click = TRUE) + if(!owner) + return + + owner.check_languages() + + +/datum/action/item_action/organ_action/wingdings_decoder + name = "Переключить дешифратор Вингдингс" + icon_icon = 'icons/mob/actions/actions.dmi' + button_icon_state = "wingdings_off" + use_itemicon = FALSE + + +/datum/action/item_action/organ_action/wingdings_decoder/proc/update_button_state() + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(!translator) + return FALSE + + if(translator.can_wingdings) + button_icon_state = "wingdings_on" + else + button_icon_state = initial(button_icon_state) + + UpdateButtonIcon() + + return TRUE + + +/datum/action/item_action/organ_action/wingdings_decoder/Trigger(left_click = TRUE) + if(!owner) + return FALSE + + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(!translator) + Remove(owner) + + if(!translator.active) + owner.balloon_alert(owner, "дешифратор не работает!") + return FALSE + + translator.can_wingdings = !translator.can_wingdings + owner.balloon_alert(owner, "дешифратор [translator.can_wingdings ? "включён" : "выключен"]") + update_button_state() + + return TRUE + + +/datum/action/item_action/organ_action/wingdings_decoder/IsAvailable() + if(!..()) + return FALSE + + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = owner.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(!translator) + Remove(owner) + return FALSE + + if(!translator.active) + return FALSE + + return TRUE + + + // TRANSLATOR STORAGE UPGRADES // + +/obj/item/translator_upgrade // just adminspawn now + name = "translator upgrade" + desc = "Учёные NanoTrasen ещё не поняли, как он работает. Может быть, позже..." + w_class = WEIGHT_CLASS_TINY + var/extra_slots = 1 + + +/obj/item/translator_upgrade/grey_retraslator + name = "PVR storage upgrade" + desc = "Маленькое инопланетное устройство с мелким экраном, показывающим только помехи. Видимо, что-то из технологий греев." + ru_names = list( + NOMINATIVE = "модуль улучшения РПГ", + GENITIVE = "модуля улучшения РПГ", + DATIVE = "модулю улучшения РПГ", + ACCUSATIVE = "модуль улучшения РПГ", + INSTRUMENTAL = "модулем улучшения РПГ", + PREPOSITIONAL = "модуле улучшения РПГ", + ) + icon = 'icons/obj/voice_translator.dmi' + icon_state = "pvr_upgrade" + origin_tech = "materials=2;programming=3;abductor=1" + extra_slots = UPGRADE_SLOTS_GREY + + + // LANGUAGE TRANSLATOR CHIPS // + +/obj/item/translator_chip + name = "language chip" + desc = "Крошечный чип с мигающим индикатором." + ru_names = list( + NOMINATIVE = "языковой чип", + GENITIVE = "языкового чипа", + DATIVE = "языковому чипу", + ACCUSATIVE = "языковой чип", + INSTRUMENTAL = "языковым чипом", + PREPOSITIONAL = "языковом чипе", + ) + icon = 'icons/obj/voice_translator.dmi' + icon_state = "chip_empty" + w_class = WEIGHT_CLASS_TINY + origin_tech = "materials=1;programming=2" + var/datum/language/stored_language + var/stored_language_rus + + +/obj/item/translator_chip/New() + . = ..() + if(stored_language) + stored_language = GLOB.all_languages[stored_language] + + +/obj/item/translator_chip/proc/on_install(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/translator) + return TRUE + + +/obj/item/translator_chip/proc/on_remove(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/translator) + return TRUE + + +/obj/item/translator_chip/attack_self(mob/living/user) + if(stored_language_rus) + return + + var/list/available_languages = list() + var/obj/item/translator_chip/chip + for(chip as anything in subtypesof(/obj/item/translator_chip)) + available_languages[chip.stored_language_rus] = chip + + var/answer = tgui_input_list(user, "Выберите язык для загрузки в чип:", "Выбор прошивки", available_languages) + if(!answer || stored_language_rus) //double check to prevent multispec + return + + user.drop_item_ground(src, silent = TRUE) + chip = available_languages[answer] + var/obj/item/translator_chip/new_chip = new chip(null) + user.put_in_hands(new_chip, silent = TRUE) + qdel(src) + + +/obj/item/translator_chip/examine(mob/user) + . = ..() + + if(!Adjacent(user)) + return + + if(stored_language_rus) + . += span_notice("Загруженный язык: [stored_language_rus].") + else + . += span_notice("Судя по всему, не активирован.") + + +/obj/item/translator_chip/update_icon_state() + for(var/obj/item/translator_chip/chip as anything in subtypesof(/obj/item/translator_chip)) + if(stored_language_rus != chip.stored_language_rus) + continue + + icon_state = chip.icon_state + return + + + // CHIP SUBTYPES // + +/obj/item/translator_chip/sol + icon_state = "chip_solcom" + stored_language = LANGUAGE_SOL_COMMON + stored_language_rus = "Общесолнечный" + +/obj/item/translator_chip/neorus + icon_state = "chip_neorus" + stored_language = LANGUAGE_NEO_RUSSIAN + stored_language_rus = "Неорусский" + +/obj/item/translator_chip/gutter + icon_state = "chip_gutter" + stored_language = LANGUAGE_GUTTER + stored_language_rus = "Гангстерский" + +/obj/item/translator_chip/clownish + icon_state = "chip_clownish" + stored_language = LANGUAGE_CLOWN + stored_language_rus = "Клоунский" + +/obj/item/translator_chip/tradeband + icon_state = "chip_tradeband" + stored_language = LANGUAGE_TRADER + stored_language_rus = "Торговый" + +/obj/item/translator_chip/canilunzt + icon_state = "chip_canilunzt" + stored_language = LANGUAGE_VULPKANIN + stored_language_rus = "Канилунц" + +/obj/item/translator_chip/sintaunathi + icon_state = "chip_sintaunathi" + stored_language = LANGUAGE_UNATHI + stored_language_rus = "Синта'Унати" + +/obj/item/translator_chip/siiktajr + icon_state = "chip_siiktajr" + stored_language = LANGUAGE_TAJARAN + stored_language_rus = "Сик'тайр" + +/obj/item/translator_chip/skrellian + icon_state = "chip_skrellian" + stored_language = LANGUAGE_SKRELL + stored_language_rus = "Скреллианский" + +/obj/item/translator_chip/bubblish + icon_state = "chip_bubblish" + stored_language = LANGUAGE_SLIME + stored_language_rus = "Пузырчатый" + +/obj/item/translator_chip/voxpidgin + icon_state = "chip_voxpidgin" + stored_language = LANGUAGE_VOX + stored_language_rus = "Вокс-пиджин" + +/obj/item/translator_chip/chittin + icon_state = "chip_chittin" + stored_language = LANGUAGE_KIDAN + stored_language_rus = "Хитин" + +/obj/item/translator_chip/tkachi + icon_state = "chip_tkachi" + stored_language = LANGUAGE_MOTH + stored_language_rus = "Ткачий" + +/obj/item/translator_chip/orluum + icon_state = "chip_orluum" + stored_language = LANGUAGE_DRASK + stored_language_rus = "Орлуум" + + +/obj/item/translator_chip/wingdings + icon_state = "chip_wingdings" + stored_language = null + stored_language_rus = "Вингдингс" + +/obj/item/translator_chip/wingdings/on_install(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/translator) + if(!translator.decoder) + translator.decoder = new(translator) + + if(H) + translator.decoder.Grant(H) + + return TRUE + +/obj/item/translator_chip/wingdings/on_remove(mob/living/carbon/human/H, obj/item/organ/internal/cyberimp/mouth/translator/translator) + translator.can_wingdings = FALSE + + if(!translator.decoder) + return + + if(H) + translator.decoder.Remove(H) + + var/datum/action/item_action/organ_action/wingdings_decoder/used_decoder = locate() in translator.actions + if(used_decoder) + used_decoder.Destroy() + + translator.decoder = null + + return TRUE + + +/* One day it will become a reality + +/obj/item/translator_chip/sintatajr + icon_state = "chip_sintatajr" + stored_language = + stored_language_rus = "Синта'Тайр" + +*/ + + +#undef DEFAULT_CHIP_SLOTS +#undef UPGRADE_SLOTS_GREY diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 77a40efd088..adc2ba96efa 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -179,6 +179,182 @@ if(affected && affected.encased) //no bones no problem. return FALSE +/datum/surgery/translator_manipulations + name = "Translator Manipulations" + possible_locs = list(BODY_ZONE_PRECISE_MOUTH) + restricted_speciestypes = null + + steps = list( + /datum/surgery_step/generic/cut_open, + /datum/surgery_step/generic/clamp_bleeders, + /datum/surgery_step/generic/retract_skin, + /datum/surgery_step/screwdriver_use, + /datum/surgery_step/proxy/manipulate_translator, + /datum/surgery_step/screwdriver_use, + /datum/surgery_step/generic/cauterize + ) + +/datum/surgery/translator_manipulations/can_start(mob/user, mob/living/carbon/target) + if(!..()) + return FALSE + + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + if(!translator) // nothing to maniplate with.. + return FALSE + + return TRUE + + +/datum/surgery_step/screwdriver_use + name = "screw/unscrew translator" + allowed_tools = list(TOOL_SCREWDRIVER = 100) + time = 1 SECONDS + +/datum/surgery_step/screwdriver_use/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + user.visible_message(span_notice("[user] starts [translator.open ? "screwing" : "unscrewing"] the locking mechanism on the speech translator casing."),\ + span_notice("You start [translator.open ? "screwing" : "unscrewing"] the locking mechanism on the speech translator casing.")) + tool.play_tool_sound(target, 30) + + return ..() + +/datum/surgery_step/screwdriver_use/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + user.visible_message(span_notice("[user] [translator.open ? "screwed" : "unscrewed"] the locking mechanism on the speech translator casing."),\ + span_notice("You [translator.open ? "screwed" : "unscrewed"] the locking mechanism on the speech translator casing.")) + translator.open = !translator.open + + return SURGERY_STEP_CONTINUE + + +/datum/surgery_step/proxy/manipulate_translator + name = "Manipulate translator (proxy)" + branches = list( + /datum/surgery/intermediate/manipulate_translator/install, + /datum/surgery/intermediate/manipulate_translator/uninstall, + ) + + +/datum/surgery/intermediate/manipulate_translator + requires_bodypart = TRUE + possible_locs = list(BODY_ZONE_PRECISE_MOUTH) + + +/datum/surgery/intermediate/manipulate_translator/install + steps = list(/datum/surgery_step/internal/manipulate_translator/install) + + +/datum/surgery_step/internal/manipulate_translator/install + name = "install chip/upgrade" + allowed_tools = list( + /obj/item/translator_chip = 100, + /obj/item/translator_upgrade = 100, + ) + time = 5 SECONDS + + +/datum/surgery_step/internal/manipulate_translator/install/begin_step(mob/living/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) + user.visible_message(span_notice("[user] starts connecting [tool] into the speech translator's slot."),\ + span_notice("You start connecting [tool] into the speech translator's slot. ")) + + return ..() + + +/datum/surgery_step/internal/manipulate_translator/install/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + + if(istype(tool, /obj/item/translator_chip)) + var/obj/item/translator_chip/chip = tool + + if(!chip.stored_language_rus) + to_chat(user, span_warning("Chip must be activated to connect with translator!")) + return SURGERY_STEP_INCOMPLETE + + if(LAZYLEN(translator.stored_chips) >= translator.maximum_slots) + to_chat(user, span_warning("There is no place in translator to another language chip!")) + return SURGERY_STEP_INCOMPLETE + + if(chip.stored_language_rus in translator.given_languages_rus) + to_chat(user, span_warning("This language chip already installed!")) + return SURGERY_STEP_INCOMPLETE + + translator.install_chip(user, chip) + + else if(istype(tool, /obj/item/translator_upgrade)) + if(translator.stored_upgrade) + to_chat(user, span_warning("Translator already has an upgrade!")) + return SURGERY_STEP_INCOMPLETE + + translator.install_upgrade(user, tool) + + user.visible_message(span_notice("[user] succesfully connected [tool] into the speech translator's slot."),\ + span_notice("You succesfully connected [tool] into the speech translator's slot. ")) + + return SURGERY_STEP_CONTINUE + + +/datum/surgery/intermediate/manipulate_translator/uninstall + steps = list(/datum/surgery_step/internal/manipulate_translator/uninstall) + + +/datum/surgery_step/internal/manipulate_translator/uninstall + name = "uninstall chip/upgrade" + allowed_tools = list(TOOL_MULTITOOL = 100) + time = 0 + + +/datum/surgery_step/internal/manipulate_translator/uninstall/begin_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool, datum/surgery/surgery) + var/obj/item/organ/internal/cyberimp/mouth/translator/translator = target.get_organ_slot(INTERNAL_ORGAN_SPEECH_TRANSLATOR) + var/list/choises = list() + + if(translator.stored_upgrade) + choises["Улучшение"] = image(icon = translator.stored_upgrade.icon, icon_state = translator.stored_upgrade.icon_state) + + for(var/obj/item/translator_chip/chip in translator.stored_chips) + choises[chip.stored_language_rus] = image(icon = chip.icon, icon_state = chip.icon_state) + + if(!choises) + to_chat(user, span_notice("You can't find anything to uninstall from the speech translator.")) + return SURGERY_STEP_INCOMPLETE + + var/choise + if(LAZYLEN(choises) == 1) + choise = choises[1] + else + choise = show_radial_menu(user, target, choises, require_near = TRUE) + + if(!choise) //closed + return SURGERY_STEP_INCOMPLETE + + user.visible_message(span_notice("[user] starts disconnecting wires from the speech translator."),\ + span_notice("You start disconnecting wires from the speech translator. ")) + + if(!do_after(user, 4 SECONDS, target)) + return SURGERY_STEP_INCOMPLETE + + var/add_msg = "" + if(choise == "Улучшение") + if(LAZYLEN(translator.stored_chips) > initial(translator.maximum_slots)) + to_chat(user, span_warning("You need to remove the extra chips first!")) + return SURGERY_STEP_INCOMPLETE + + translator.uninstall_upgrade(user) + add_msg = "upgrade" + + else + var/obj/item/translator_chip/chip + for(chip in translator.stored_chips) + if(chip.stored_language_rus == choise) + break + + add_msg = "chip" + translator.remove_chip(user, chip) + + user.visible_message(span_notice("[user] successfully removed the [add_msg] from the speech translator."),\ + span_notice("You successfully removed the [add_msg] from the speech translator. ")) + + return ..() + // Intermediate steps for branching organ manipulation. /datum/surgery/intermediate/manipulate @@ -516,8 +692,7 @@ // dunno how you got here but okay return SURGERY_BEGINSTEP_SKIP - if(istype(organ, /obj/item/organ/internal/wryn/hivenode) && !iswryn(target)) // If they make more "unique" organs, I'll make some vars and a separate proc, but now.. - to_chat(user, span_warning("Данное существо не способно принять этот орган!")) + if(!organ.can_insert(user, target)) // checks species whitelist and special organ restrictions return SURGERY_BEGINSTEP_SKIP if(target_zone != organ.parent_organ_zone || target.get_organ_slot(organ.slot)) @@ -853,7 +1028,7 @@ /obj/item/shard = 60, /obj/item/scissors = 12, /obj/item/twohanded/chainsaw = 1, - /obj/item/claymore = 6, + /obj/item/melee/claymore = 6, /obj/item/melee/energy = 6, /obj/item/pen/edagger = 6 ) diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 7983d32b064..3f357536dfa 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -72,8 +72,8 @@ if("skin_color") if(can_change_skin_color()) - var/new_skin = input(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) as color|null - if(new_skin && (!..()) && owner.change_skin_color(new_skin)) + var/new_skin = tgui_input_color(usr, "Choose your character's skin colour: ", "Skin Color", owner.skin_colour) + if(!isnull(new_skin) && (!..()) && owner.change_skin_color(new_skin)) update_dna() if("hair") @@ -83,14 +83,14 @@ if("hair_color") if(can_change(APPEARANCE_HAIR_COLOR)) - var/new_hair = input("Please select hair color.", "Hair Color", head_organ.hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair)) + var/new_hair = tgui_input_color(usr, "Please select hair color.", "Hair Color", head_organ.hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair)) update_dna() if("secondary_hair_color") if(can_change(APPEARANCE_SECONDARY_HAIR_COLOR)) - var/new_hair = input("Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) as color|null - if(new_hair && (!..()) && owner.change_hair_color(new_hair, 1)) + var/new_hair = tgui_input_color(usr, "Please select secondary hair color.", "Secondary Hair Color", head_organ.sec_hair_colour) + if(!isnull(new_hair) && (!..()) && owner.change_hair_color(new_hair, 1)) update_dna() if("hair_gradient") @@ -124,21 +124,21 @@ if("facial_hair_color") if(can_change(APPEARANCE_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial)) + var/new_facial = tgui_input_color(usr, "Please select facial hair color.", "Facial Hair Color", head_organ.facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial)) update_dna() if("secondary_facial_hair_color") if(can_change(APPEARANCE_SECONDARY_FACIAL_HAIR_COLOR)) - var/new_facial = input("Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) as color|null - if(new_facial && (!..()) && owner.change_facial_hair_color(new_facial, 1)) + var/new_facial = tgui_input_color(usr, "Please select secondary facial hair color.", "Secondary Facial Hair Color", head_organ.sec_facial_colour) + if(!isnull(new_facial) && (!..()) && owner.change_facial_hair_color(new_facial, 1)) update_dna() if("eye_color") if(can_change(APPEARANCE_EYE_COLOR)) var/obj/item/organ/internal/eyes/eyes_organ = owner.get_int_organ(/obj/item/organ/internal/eyes) - var/new_eyes = input("Please select eye color.", "Eye Color", eyes_organ.eye_colour) as color|null - if(new_eyes && (!..()) && owner.change_eye_color(new_eyes)) + var/new_eyes = tgui_input_color(usr, "Please select eye color.", "Eye Color", eyes_organ.eye_colour) + if(!isnull(new_eyes) && (!..()) && owner.change_eye_color(new_eyes)) update_dna() if("head_accessory") @@ -148,8 +148,8 @@ if("head_accessory_color") if(can_change_head_accessory()) - var/new_head_accessory = input("Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) as color|null - if(new_head_accessory && (!..()) && owner.change_head_accessory_color(new_head_accessory)) + var/new_head_accessory = tgui_input_color(usr, "Please select head accessory color.", "Head Accessory Color", head_organ.headacc_colour) + if(!isnull(new_head_accessory) && (!..()) && owner.change_head_accessory_color(new_head_accessory)) update_dna() if("head_marking") @@ -159,8 +159,8 @@ if("head_marking_color") if(can_change_markings("head")) - var/new_markings = input("Please select head marking color.", "Marking Color", owner.m_colours["head"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "head")) + var/new_markings = tgui_input_color(usr, "Please select head marking color.", "Marking Color", owner.m_colours["head"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "head")) update_dna() if("body_marking") @@ -170,8 +170,8 @@ if("body_marking_color") if(can_change_markings("body")) - var/new_markings = input("Please select body marking color.", "Marking Color", owner.m_colours["body"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "body")) + var/new_markings = tgui_input_color(usr, "Please select body marking color.", "Marking Color", owner.m_colours["body"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "body")) update_dna() if("tail_marking") @@ -181,8 +181,8 @@ if("tail_marking_color") if(can_change_markings("tail")) - var/new_markings = input("Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) as color|null - if(new_markings && (!..()) && owner.change_marking_color(new_markings, "tail")) + var/new_markings = tgui_input_color(usr, "Please select tail marking color.", "Marking Color", owner.m_colours["tail"]) + if(!isnull(new_markings) && (!..()) && owner.change_marking_color(new_markings, "tail")) update_dna() if("body_accessory") diff --git a/code/modules/tgui/modules/law_manager.dm b/code/modules/tgui/modules/law_manager.dm index 12550323958..6bc052d8f45 100644 --- a/code/modules/tgui/modules/law_manager.dm +++ b/code/modules/tgui/modules/law_manager.dm @@ -137,7 +137,7 @@ var/datum/ai_laws/ALs = locate(params["transfer_laws"]) in (is_admin(usr) ? admin_laws : player_laws) if(ALs) log_and_message_admins("has transfered the [ALs.name] laws to [owner].") - ALs.sync(owner, 0, TRUE) + ALs.sync(owner, FALSE, TRUE) current_view = 0 SSticker?.score?.save_silicon_laws(owner, usr, "admin/malf used law manager, '[ALs.name]' laws set was loaded", log_all_laws = TRUE) diff --git a/code/modules/tgui/tgui_datum.dm b/code/modules/tgui/tgui_datum.dm index 76b393420f2..960930d03b3 100644 --- a/code/modules/tgui/tgui_datum.dm +++ b/code/modules/tgui/tgui_datum.dm @@ -103,6 +103,8 @@ /datum/tgui/proc/send_assets() var/flushqueue = window.send_asset(get_asset_datum( /datum/asset/simple/namespaced/fontawesome)) + flushqueue |= window.send_asset(get_asset_datum( + /datum/asset/json/icon_ref_map)) for(var/datum/asset/asset in src_object.ui_assets(user)) flushqueue |= window.send_asset(asset) if(flushqueue) @@ -224,9 +226,9 @@ "locked" = FALSE, ), "client" = list( - "ckey" = user.client.ckey, - "address" = user.client.address, - "computer_id" = user.client.computer_id, + "ckey" = user.client?.ckey, + "address" = user.client?.address, + "computer_id" = user.client?.computer_id, ), "user" = list( "name" = "[user]", @@ -238,11 +240,15 @@ var/data = custom_data || with_data && src_object.ui_data(user) if(data) json_data["data"] = data + var/static_data = with_static_data && src_object.ui_static_data(user) + if(static_data) json_data["static_data"] = static_data + if(src_object.tgui_shared_states) json_data["shared"] = src_object.tgui_shared_states + return json_data /** diff --git a/code/modules/tgui/tgui_input/color_input.dm b/code/modules/tgui/tgui_input/color_input.dm new file mode 100644 index 00000000000..7ba4b9ed3e0 --- /dev/null +++ b/code/modules/tgui/tgui_input/color_input.dm @@ -0,0 +1,132 @@ +/** + * Creates a TGUI color picker window and returns the user's response. + * + * This proc should be used to create a color picker that the caller will wait for a response from. + * Arguments: + * * user - The user to show the picker to. + * * title - The of the picker modal, shown on the top of the TGUI window. + * * timeout - The timeout of the picker, after which the modal will close and qdel itself. Set to zero for no timeout. + * * autofocus - The bool that controls if this picker should grab window focus. + */ +/proc/tgui_input_color(mob/user, message, title, default = "#000000", timeout = 0, autofocus = TRUE, ui_state = GLOB.always_state) + if(!user) + user = usr + if(!istype(user)) + if(!isclient(user)) + CRASH("We passed something that wasn't a user/client in a TGUI Input Color! The passed thing was [user]!") + var/client/client = user + user = client.mob + + if(isnull(user.client)) + return + + // Client does NOT have tgui_input on: Returns regular input + if(user.client?.prefs?.toggles2 & PREFTOGGLE_2_DISABLE_TGUI_INPUT) + return input(user, message, title, default) as color|null + + var/datum/tgui_input_color/picker = new(user, message, title, default, timeout, autofocus, ui_state) + picker.ui_interact(user) + picker.wait() + if(picker) + . = picker.choice + qdel(picker) + +/** + * tgui_input_color + * + * Datum used for instantiating and using a TGUI-controlled color picker. + */ +/datum/tgui_input_color + /// The title of the TGUI window + var/title + /// The message to show the user + var/message + /// The default choice, used if there is an existing value + var/default + /// The color the user selected, null if no selection has been made + var/choice + /// The time at which the tgui_input_color was created, for displaying timeout progress. + var/start_time + /// The lifespan of the tgui_input_color, after which the window will close and delete itself. + var/timeout + /// The bool that controls if this modal should grab window focus + var/autofocus + /// Boolean field describing if the tgui_input_color was closed by the user. + var/closed + /// The attached timer that handles this objects timeout deletion + var/deletion_timer + /// The TGUI UI state that will be returned in ui_state(). Default: always_state + var/datum/ui_state/state + +/datum/tgui_input_color/New(mob/user, message, title, default, timeout, autofocus, ui_state) + src.autofocus = autofocus + src.title = title + src.default = default + src.message = message + src.state = ui_state + + if(timeout) + src.timeout = timeout + start_time = world.time + deletion_timer = QDEL_IN(src, timeout) + +/datum/tgui_input_color/Destroy(force, ...) + SStgui.close_uis(src) + state = null + deltimer(deletion_timer) + return ..() + +/** + * Waits for a user's response to the tgui_input_color's prompt before returning. Returns early if + * the window was closed by the user. + */ +/datum/tgui_input_color/proc/wait() + while(!choice && !closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_input_color/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ColorPickerModal") + ui.open() + ui.set_autoupdate(timeout > 0) + +/datum/tgui_input_color/ui_close(mob/user) + closed = TRUE + +/datum/tgui_input_color/ui_state(mob/user) + return state + +/datum/tgui_input_color/ui_static_data(mob/user) + var/list/data = list() + data["autofocus"] = autofocus + data["large_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_LARGE_INPUT_BUTTONS) + data["swapped_buttons"] = !user.client?.prefs || (user.client.prefs.toggles2 & PREFTOGGLE_2_SWAP_INPUT_BUTTONS) + data["title"] = title + data["default_color"] = default + data["message"] = message + return data + +/datum/tgui_input_color/ui_data(mob/user) + var/list/data = list() + if(timeout) + data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS)) + return data + +/datum/tgui_input_color/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("submit") + if(!findtext(params["entry"], GLOB.is_color)) + return + choice = params["entry"] + closed = TRUE + SStgui.close_uis(src) + return TRUE + if("cancel") + closed = TRUE + SStgui.close_uis(src) + return TRUE diff --git a/code/modules/tgui/tgui_input/input_checkbox.dm b/code/modules/tgui/tgui_input/input_checkbox.dm new file mode 100644 index 00000000000..d7bb27f49eb --- /dev/null +++ b/code/modules/tgui/tgui_input/input_checkbox.dm @@ -0,0 +1,73 @@ +/** + * Creates a TGUI input list window and returns the user's response in a ranked order. + * + * Arguments: + * * user - The user to show the input box to. + * * message - The content of the input box, shown in the body of the TGUI window. + * * title - The title of the input box, shown on the top of the TGUI window. + * * items - The options that can be chosen by the user, each string is assigned a button on the UI. + * * default - If an option is already preselected on the UI. Current values, etc. + * * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout. + */ +/proc/tgui_input_checkbox_list(mob/user, message, title = "Select", list/items, default, timeout = 0, ui_state = GLOB.always_state) + if(!user) + user = usr + + if(!length(items)) + CRASH("[user] tried to open an empty TGUI Input Checkbox List. Contents are: [items]") + + if(!istype(user)) + if(!isclient(user)) + CRASH("We passed something that wasn't a user/client in a TGUI Input Checkbox List! The passed user was [user]!") + var/client/client = user + user = client.mob + + if(isnull(user.client)) + return + + var/datum/tgui_list_input/checkbox/input = new(user, message, title, items, default, timeout, ui_state) + + if(input.invalid) + qdel(input) + return + + input.ui_interact(user) + input.wait() + if(input) + . = input.choice + qdel(input) + +/** + * # tgui_list_input/ranked + * + * Datum used for allowing a user to sort a TGUI-controlled list input that prompts the user with + * a message and shows a list of rankable options + */ +/datum/tgui_list_input/checkbox + modal_type = "CheckboxListInputModal" + +/datum/tgui_list_input/checkbox/handle_new_items(list/_items) + var/list/repeat_items = list() + // Gets rid of illegal characters + var/static/regex/blacklisted_words = regex(@{"([^\u0020-\u8000]+)"}) + + for(var/key in _items) + var/string_key = blacklisted_words.Replace("[key]", "") + + // Avoids duplicated keys E.g: when areas have the same name + string_key = avoid_assoc_duplicate_keys(string_key, repeat_items) + src.items += list(list( + "key" = string_key, + "checked" = (_items[key] ? TRUE : FALSE) + )) + src.items_map = _items // we use this differently + +/datum/tgui_list_input/checkbox/handle_submit_action(params) + var/list/associated = list() + for(var/list/sublist in params["entry"]) + associated[sublist["key"]] = (sublist["checked"] in list(1, "1", "true")) + + if(!lists_equal_unordered(associated, items_map)) + return FALSE + set_choice(associated) + return TRUE diff --git a/code/modules/tgui/tgui_input/list_input.dm b/code/modules/tgui/tgui_input/list_input.dm index f43ceffdaaf..639e3df359c 100644 --- a/code/modules/tgui/tgui_input/list_input.dm +++ b/code/modules/tgui/tgui_input/list_input.dm @@ -73,26 +73,18 @@ var/datum/ui_state/state /// Whether the tgui list input is invalid or not (i.e. due to all list entries being null) var/invalid = FALSE + /// The TGUI modal to use for this popup + var/modal_type = "ListInputModal" -/datum/tgui_list_input/New(mob/user, message, title, list/items, default, timeout, ui_state) +/datum/tgui_list_input/New(mob/user, message, title, list/_items, default, timeout, ui_state) src.title = title src.message = message src.items = list() src.items_map = list() src.default = default src.state = ui_state - var/list/repeat_items = list() - - // Gets rid of illegal characters - var/static/regex/whitelistedWords = regex(@{"([^\u0020-\u8000]+)"}) - for(var/i in items) - var/string_key = whitelistedWords.Replace("[i]", "") - - // Avoids duplicated keys E.g: when areas have the same name - string_key = avoid_assoc_duplicate_keys(string_key, repeat_items) - src.items += string_key - src.items_map[string_key] = i + handle_new_items(_items) if(length(src.items) == 0) invalid = TRUE @@ -122,7 +114,7 @@ /datum/tgui_list_input/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "ListInputModal") + ui = new(user, src, modal_type) ui.set_autoupdate(FALSE) ui.open() @@ -152,9 +144,8 @@ switch(action) if("submit") - if(!(params["entry"] in items)) + if(!handle_submit_action(params)) return - set_choice(items_map[params["entry"]]) closed = TRUE SStgui.close_uis(src) return TRUE @@ -163,5 +154,24 @@ SStgui.close_uis(src) return TRUE +/datum/tgui_list_input/proc/handle_submit_action(params) + if(!(params["entry"] in items)) + return FALSE + set_choice(items_map[params["entry"]]) + return TRUE + /datum/tgui_list_input/proc/set_choice(choice) src.choice = choice + +/datum/tgui_list_input/proc/handle_new_items(list/_items) + var/list/repeat_items = list() + // Gets rid of illegal characters + var/static/regex/blacklisted_words = regex(@{"([^\u0020-\u8000]+)"}) + + for(var/i in _items) + var/string_key = blacklisted_words.Replace("[i]", "") + + // Avoids duplicated keys E.g: when areas have the same name + string_key = avoid_assoc_duplicate_keys(string_key, repeat_items) + items += string_key + items_map[string_key] = i diff --git a/code/modules/tgui/tgui_input/ranked_list_input.dm b/code/modules/tgui/tgui_input/ranked_list_input.dm new file mode 100644 index 00000000000..80631d56823 --- /dev/null +++ b/code/modules/tgui/tgui_input/ranked_list_input.dm @@ -0,0 +1,56 @@ +/** + * Creates a TGUI input list window and returns the user's response. + * + * This proc should be used to create alerts that the caller will wait for a response from. + * Arguments: + * * user - The user to show the input box to. + * * message - The content of the input box, shown in the body of the TGUI window. + * * title - The title of the input box, shown on the top of the TGUI window. + * * items - The options that can be chosen by the user, each string is assigned a button on the UI. + * * default - If an option is already preselected on the UI. Current values, etc. + * * timeout - The timeout of the input box, after which the menu will close and qdel itself. Set to zero for no timeout. + */ +/proc/tgui_input_ranked_list(mob/user, message, title = "Select", list/items, default, timeout = 0, ui_state = GLOB.always_state) + if(!user) + user = usr + + if(!length(items)) + CRASH("[user] tried to open an empty TGUI Input List. Contents are: [items]") + + if(!istype(user)) + if(!isclient(user)) + CRASH("We passed something that wasn't a user/client in a TGUI Input List! The passed user was [user]!") + var/client/client = user + user = client.mob + + if(isnull(user.client)) + return + + // We don't support disabled TGUI input (PREFTOGGLE_2_DISABLE_TGUI_INPUT), get with the times old man + + var/datum/tgui_list_input/ranked/input = new(user, message, title, items, default, timeout, ui_state) + + if(input.invalid) + qdel(input) + return + + input.ui_interact(user) + input.wait() + if(input) + . = input.choice + qdel(input) + +/** + * # tgui_list_input/ranked + * + * Datum used for allowing a user to sort a TGUI-controlled list input that prompts the user with + * a message and shows a list of rankable options + */ +/datum/tgui_list_input/ranked + modal_type = "RankedListInputModal" + +/datum/tgui_list_input/ranked/handle_submit_action(params) + if(!lists_equal_unordered(params["entry"], items)) + return FALSE + set_choice(params["entry"]) + return TRUE diff --git a/code/modules/tgui/tgui_input/text_input.dm b/code/modules/tgui/tgui_input/text_input.dm index b3a7d32b090..128232df71a 100644 --- a/code/modules/tgui/tgui_input/text_input.dm +++ b/code/modules/tgui/tgui_input/text_input.dm @@ -171,4 +171,4 @@ return var/converted_entry = encode ? html_encode(entry) : entry - src.entry = trim(converted_entry, max_length) + src.entry = trim(converted_entry, max_length + 1) diff --git a/code/modules/tgui/tgui_panel/tgui_panel_external.dm b/code/modules/tgui/tgui_panel/tgui_panel_external.dm index f51c974ae4a..fb021cc75a6 100644 --- a/code/modules/tgui/tgui_panel/tgui_panel_external.dm +++ b/code/modules/tgui/tgui_panel/tgui_panel_external.dm @@ -19,21 +19,18 @@ // Failed to fix action = alert(src, "Did that work?", "", "Yes", "No, switch to old ui") if(action == "No, switch to old ui") - winset(src, "output", "on-show=&is-disabled=0&is-visible=1") - winset(src, "chat_panel", "is-disabled=1;is-visible=0") + winset(src, "legacy_output_selector", "left=output_legacy") log_tgui(src, "Failed to fix.") /client/proc/nuke_chat() // Catch all solution (kick the whole thing in the pants) - winset(src, "output", "on-show=&is-disabled=0&is-visible=1") - winset(src, "chat_panel", "is-disabled=1;is-visible=0") + winset(src, "legacy_output_selector", "left=output_legacy") if(!tgui_panel || !istype(tgui_panel)) log_tgui(src, "tgui_panel datum is missing") tgui_panel = new(src, "chat_panel") tgui_panel.initialize(force = TRUE) // Force show the panel to see if there are any errors - winset(src, "output", "is-disabled=1&is-visible=0") - winset(src, "chat_panel", "is-disabled=0;is-visible=1") + winset(src, "legacy_output_selector", "left=output_legacy") /client/verb/refresh_tgui() set name = "Refresh TGUI" diff --git a/icons/_nanomaps/Celestation_nanomap_z2.png b/icons/_nanomaps/Celestation_nanomap_z2.png index c2bc8f31bde..3339d6728af 100644 Binary files a/icons/_nanomaps/Celestation_nanomap_z2.png and b/icons/_nanomaps/Celestation_nanomap_z2.png differ diff --git a/icons/_nanomaps/Cerestation_nanomap_z1.png b/icons/_nanomaps/Cerestation_nanomap_z1.png index 7e49b7db91e..e0ab63984a5 100644 Binary files a/icons/_nanomaps/Cerestation_nanomap_z1.png and b/icons/_nanomaps/Cerestation_nanomap_z1.png differ diff --git a/icons/_nanomaps/Cyberiad_nanomap_z1.png b/icons/_nanomaps/Cyberiad_nanomap_z1.png index 2aade9d253a..8950fdd2a2a 100644 Binary files a/icons/_nanomaps/Cyberiad_nanomap_z1.png and b/icons/_nanomaps/Cyberiad_nanomap_z1.png differ diff --git a/icons/_nanomaps/Delta_nanomap_z1.png b/icons/_nanomaps/Delta_nanomap_z1.png index 4fdb7243124..dc650a6f93b 100644 Binary files a/icons/_nanomaps/Delta_nanomap_z1.png and b/icons/_nanomaps/Delta_nanomap_z1.png differ diff --git a/icons/_nanomaps/Nova_nanomap_z1.png b/icons/_nanomaps/Nova_nanomap_z1.png index e3b86ddbadf..24e2d58aec6 100644 Binary files a/icons/_nanomaps/Nova_nanomap_z1.png and b/icons/_nanomaps/Nova_nanomap_z1.png differ diff --git a/icons/_nanomaps/Nova_nanomap_z2.png b/icons/_nanomaps/Nova_nanomap_z2.png index 5edd5a29f1b..cb359d4a128 100644 Binary files a/icons/_nanomaps/Nova_nanomap_z2.png and b/icons/_nanomaps/Nova_nanomap_z2.png differ diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index e825e855573..e87749c7242 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/effects/light_overlays/light_480.dmi b/icons/effects/light_overlays/light_480.dmi new file mode 100644 index 00000000000..ee754dc3b4b Binary files /dev/null and b/icons/effects/light_overlays/light_480.dmi differ diff --git a/icons/effects/light_overlays/light_544.dmi b/icons/effects/light_overlays/light_544.dmi new file mode 100644 index 00000000000..c592d192536 Binary files /dev/null and b/icons/effects/light_overlays/light_544.dmi differ diff --git a/icons/effects/mouse_pointers/supplypod_down_target.dmi b/icons/effects/mouse_pointers/supplypod_down_target.dmi new file mode 100644 index 00000000000..53a3bee0a78 Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_down_target.dmi differ diff --git a/icons/effects/mouse_pointers/supplypod_pickturf.dmi b/icons/effects/mouse_pointers/supplypod_pickturf.dmi new file mode 100644 index 00000000000..3ca1131e1a8 Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf.dmi differ diff --git a/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi new file mode 100644 index 00000000000..113fe47540c Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_pickturf_down.dmi differ diff --git a/icons/effects/mouse_pointers/supplypod_target.dmi b/icons/effects/mouse_pointers/supplypod_target.dmi new file mode 100644 index 00000000000..94401d7a8ae Binary files /dev/null and b/icons/effects/mouse_pointers/supplypod_target.dmi differ diff --git a/icons/effects/particles/bonfire.dmi b/icons/effects/particles/bonfire.dmi new file mode 100644 index 00000000000..e8e2e36346d Binary files /dev/null and b/icons/effects/particles/bonfire.dmi differ diff --git a/icons/effects/particles/echo.dmi b/icons/effects/particles/echo.dmi new file mode 100644 index 00000000000..60a243a8a7b Binary files /dev/null and b/icons/effects/particles/echo.dmi differ diff --git a/icons/effects/particles/generic.dmi b/icons/effects/particles/generic.dmi new file mode 100644 index 00000000000..41776efdbfd Binary files /dev/null and b/icons/effects/particles/generic.dmi differ diff --git a/icons/effects/particles/goop.dmi b/icons/effects/particles/goop.dmi new file mode 100644 index 00000000000..673c1a7ad5b Binary files /dev/null and b/icons/effects/particles/goop.dmi differ diff --git a/icons/effects/particles/pollen.dmi b/icons/effects/particles/pollen.dmi new file mode 100644 index 00000000000..559c4d1846f Binary files /dev/null and b/icons/effects/particles/pollen.dmi differ diff --git a/icons/effects/particles/smoke.dmi b/icons/effects/particles/smoke.dmi new file mode 100644 index 00000000000..99123beeb59 Binary files /dev/null and b/icons/effects/particles/smoke.dmi differ diff --git a/icons/effects/particles/stink.dmi b/icons/effects/particles/stink.dmi new file mode 100644 index 00000000000..29b92acbe67 Binary files /dev/null and b/icons/effects/particles/stink.dmi differ diff --git a/icons/effects/particles/voidwalker.dmi b/icons/effects/particles/voidwalker.dmi new file mode 100644 index 00000000000..d7f94c98797 Binary files /dev/null and b/icons/effects/particles/voidwalker.dmi differ diff --git a/icons/effects/weather_effects.dmi b/icons/effects/weather_effects.dmi index 00083c464a2..7cc1ce758a3 100644 Binary files a/icons/effects/weather_effects.dmi and b/icons/effects/weather_effects.dmi differ diff --git a/icons/hud/blob.dmi b/icons/hud/blob.dmi new file mode 100644 index 00000000000..552f511004f Binary files /dev/null and b/icons/hud/blob.dmi differ diff --git a/icons/misc/vampire_tgui.dmi b/icons/misc/vampire_tgui.dmi new file mode 100644 index 00000000000..0222b6d4cbe Binary files /dev/null and b/icons/misc/vampire_tgui.dmi differ diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi index f2db1779130..8170c7a2713 100644 Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ diff --git a/icons/mob/blob.dmi b/icons/mob/blob.dmi index 3a73ccf0994..6313a92db0f 100644 Binary files a/icons/mob/blob.dmi and b/icons/mob/blob.dmi differ diff --git a/icons/mob/clothing/body_accessory.dmi b/icons/mob/clothing/body_accessory.dmi index e79c0a12143..fc3f57629ef 100644 Binary files a/icons/mob/clothing/body_accessory.dmi and b/icons/mob/clothing/body_accessory.dmi differ diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi index 601e7162ee6..a18e9d2fc36 100644 Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ diff --git a/icons/mob/clothing/hands.dmi b/icons/mob/clothing/hands.dmi index 8ff67b1f4a7..5cfeccfa9c9 100644 Binary files a/icons/mob/clothing/hands.dmi and b/icons/mob/clothing/hands.dmi differ diff --git a/icons/mob/clothing/species/drask/gloves.dmi b/icons/mob/clothing/species/drask/gloves.dmi index b8320dcc8b5..c7be0d0f961 100644 Binary files a/icons/mob/clothing/species/drask/gloves.dmi and b/icons/mob/clothing/species/drask/gloves.dmi differ diff --git a/icons/mob/clothing/species/drask/shoes.dmi b/icons/mob/clothing/species/drask/shoes.dmi index 287b8b3b905..f64d6359692 100644 Binary files a/icons/mob/clothing/species/drask/shoes.dmi and b/icons/mob/clothing/species/drask/shoes.dmi differ diff --git a/icons/mob/clothing/species/monkey/gloves.dmi b/icons/mob/clothing/species/monkey/gloves.dmi index 4d51121aed5..98c738ff3af 100644 Binary files a/icons/mob/clothing/species/monkey/gloves.dmi and b/icons/mob/clothing/species/monkey/gloves.dmi differ diff --git a/icons/mob/clothing/species/monkey/shoes.dmi b/icons/mob/clothing/species/monkey/shoes.dmi index 5e9f38cfb99..7f08b7132f9 100644 Binary files a/icons/mob/clothing/species/monkey/shoes.dmi and b/icons/mob/clothing/species/monkey/shoes.dmi differ diff --git a/icons/mob/clothing/species/unathi/shoes.dmi b/icons/mob/clothing/species/unathi/shoes.dmi index 2d428a7d85b..8dcf1748cef 100644 Binary files a/icons/mob/clothing/species/unathi/shoes.dmi and b/icons/mob/clothing/species/unathi/shoes.dmi differ diff --git a/icons/mob/clothing/species/vox/gloves.dmi b/icons/mob/clothing/species/vox/gloves.dmi index 30f5c61de26..56320852d9b 100644 Binary files a/icons/mob/clothing/species/vox/gloves.dmi and b/icons/mob/clothing/species/vox/gloves.dmi differ diff --git a/icons/mob/clothing/species/vox/shoes.dmi b/icons/mob/clothing/species/vox/shoes.dmi index c5460f4b8f1..766d3a4c916 100644 Binary files a/icons/mob/clothing/species/vox/shoes.dmi and b/icons/mob/clothing/species/vox/shoes.dmi differ diff --git a/icons/mob/clothing/ties.dmi b/icons/mob/clothing/ties.dmi index c15d89bc040..9cd9e8f538a 100644 Binary files a/icons/mob/clothing/ties.dmi and b/icons/mob/clothing/ties.dmi differ diff --git a/icons/mob/gondolas.dmi b/icons/mob/gondolas.dmi new file mode 100644 index 00000000000..c8540fbac0b Binary files /dev/null and b/icons/mob/gondolas.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index 876057298bf..ba5464f9ddb 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/icons/mob/human_races/r_wryn.dmi b/icons/mob/human_races/r_wryn.dmi index 22cf2918108..7d1d4666f1d 100644 Binary files a/icons/mob/human_races/r_wryn.dmi and b/icons/mob/human_races/r_wryn.dmi differ diff --git a/icons/mob/inhands/chaplain_lefthand.dmi b/icons/mob/inhands/chaplain_lefthand.dmi new file mode 100644 index 00000000000..448b4aac44b Binary files /dev/null and b/icons/mob/inhands/chaplain_lefthand.dmi differ diff --git a/icons/mob/inhands/chaplain_righthand.dmi b/icons/mob/inhands/chaplain_righthand.dmi new file mode 100644 index 00000000000..120ee0bdee8 Binary files /dev/null and b/icons/mob/inhands/chaplain_righthand.dmi differ diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi index b7af09ade06..ff13a5f65f8 100644 Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi index 10fc756e2aa..71303398149 100644 Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ diff --git a/icons/mob/inhands/fluff_lefthand.dmi b/icons/mob/inhands/fluff_lefthand.dmi index daed1b89256..694c58776ce 100644 Binary files a/icons/mob/inhands/fluff_lefthand.dmi and b/icons/mob/inhands/fluff_lefthand.dmi differ diff --git a/icons/mob/inhands/fluff_righthand.dmi b/icons/mob/inhands/fluff_righthand.dmi index 83666d38747..2fdd42b0435 100644 Binary files a/icons/mob/inhands/fluff_righthand.dmi and b/icons/mob/inhands/fluff_righthand.dmi differ diff --git a/icons/mob/inhands/foods_lefthand.dmi b/icons/mob/inhands/foods_lefthand.dmi index d5a0c01b1d1..46107265e3d 100644 Binary files a/icons/mob/inhands/foods_lefthand.dmi and b/icons/mob/inhands/foods_lefthand.dmi differ diff --git a/icons/mob/inhands/foods_righthand.dmi b/icons/mob/inhands/foods_righthand.dmi index fff5fbee21d..e2a56894e56 100644 Binary files a/icons/mob/inhands/foods_righthand.dmi and b/icons/mob/inhands/foods_righthand.dmi differ diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi index 47e1359325f..dacf05b3e1c 100644 Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi index 005f11926b3..15cf25a4a5c 100644 Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ diff --git a/icons/mob/inhands/id_lefthand.dmi b/icons/mob/inhands/id_lefthand.dmi new file mode 100644 index 00000000000..8d7f7871bd4 Binary files /dev/null and b/icons/mob/inhands/id_lefthand.dmi differ diff --git a/icons/mob/inhands/id_righthand.dmi b/icons/mob/inhands/id_righthand.dmi new file mode 100644 index 00000000000..9db0a696b49 Binary files /dev/null and b/icons/mob/inhands/id_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 96176eec0f5..af2f9015785 100755 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index 6a85c0bdcc6..4e9f2e4a9ce 100755 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/inhands/melee_lefthand.dmi b/icons/mob/inhands/melee_lefthand.dmi new file mode 100644 index 00000000000..2adec08f235 Binary files /dev/null and b/icons/mob/inhands/melee_lefthand.dmi differ diff --git a/icons/mob/inhands/melee_righthand.dmi b/icons/mob/inhands/melee_righthand.dmi new file mode 100644 index 00000000000..19bd0038c6b Binary files /dev/null and b/icons/mob/inhands/melee_righthand.dmi differ diff --git a/icons/mob/inhands/pda_lefthand.dmi b/icons/mob/inhands/pda_lefthand.dmi new file mode 100644 index 00000000000..9f875f6e2d6 Binary files /dev/null and b/icons/mob/inhands/pda_lefthand.dmi differ diff --git a/icons/mob/inhands/pda_righthand.dmi b/icons/mob/inhands/pda_righthand.dmi new file mode 100644 index 00000000000..adff23b47f6 Binary files /dev/null and b/icons/mob/inhands/pda_righthand.dmi differ diff --git a/icons/mob/inhands/sheet_lefthand.dmi b/icons/mob/inhands/sheet_lefthand.dmi new file mode 100644 index 00000000000..fb1e7df92c9 Binary files /dev/null and b/icons/mob/inhands/sheet_lefthand.dmi differ diff --git a/icons/mob/inhands/sheet_righthand.dmi b/icons/mob/inhands/sheet_righthand.dmi new file mode 100644 index 00000000000..16e01936de3 Binary files /dev/null and b/icons/mob/inhands/sheet_righthand.dmi differ diff --git a/icons/mob/inhands/staff_lefthand.dmi b/icons/mob/inhands/staff_lefthand.dmi new file mode 100644 index 00000000000..5f4cf88d0c3 Binary files /dev/null and b/icons/mob/inhands/staff_lefthand.dmi differ diff --git a/icons/mob/inhands/staff_righthand.dmi b/icons/mob/inhands/staff_righthand.dmi new file mode 100644 index 00000000000..378da59edad Binary files /dev/null and b/icons/mob/inhands/staff_righthand.dmi differ diff --git a/icons/mob/inhands/tools_lefthand.dmi b/icons/mob/inhands/tools_lefthand.dmi new file mode 100644 index 00000000000..d2c95848c26 Binary files /dev/null and b/icons/mob/inhands/tools_lefthand.dmi differ diff --git a/icons/mob/inhands/tools_righthand.dmi b/icons/mob/inhands/tools_righthand.dmi new file mode 100644 index 00000000000..011c9e94a9c Binary files /dev/null and b/icons/mob/inhands/tools_righthand.dmi differ diff --git a/icons/mob/inhands/twohanded_lefthand.dmi b/icons/mob/inhands/twohanded_lefthand.dmi new file mode 100644 index 00000000000..1845bbb24be Binary files /dev/null and b/icons/mob/inhands/twohanded_lefthand.dmi differ diff --git a/icons/mob/inhands/twohanded_righthand.dmi b/icons/mob/inhands/twohanded_righthand.dmi new file mode 100644 index 00000000000..77aaf5016cd Binary files /dev/null and b/icons/mob/inhands/twohanded_righthand.dmi differ diff --git a/icons/mob/inhands/zippo_lefthand.dmi b/icons/mob/inhands/zippo_lefthand.dmi new file mode 100644 index 00000000000..15a3cb63d85 Binary files /dev/null and b/icons/mob/inhands/zippo_lefthand.dmi differ diff --git a/icons/mob/inhands/zippo_righthand.dmi b/icons/mob/inhands/zippo_righthand.dmi new file mode 100644 index 00000000000..8faea7d03ea Binary files /dev/null and b/icons/mob/inhands/zippo_righthand.dmi differ diff --git a/icons/mob/sprite_accessories/wryn/wryn_face.dmi b/icons/mob/sprite_accessories/wryn/wryn_face.dmi deleted file mode 100644 index f826b969c57..00000000000 Binary files a/icons/mob/sprite_accessories/wryn/wryn_face.dmi and /dev/null differ diff --git a/icons/mob/sprite_accessories/wryn/wryn_facial_hair.dmi b/icons/mob/sprite_accessories/wryn/wryn_facial_hair.dmi new file mode 100644 index 00000000000..d1658362b7f Binary files /dev/null and b/icons/mob/sprite_accessories/wryn/wryn_facial_hair.dmi differ diff --git a/icons/mob/sprite_accessories/wryn/wryn_head_accessories.dmi b/icons/mob/sprite_accessories/wryn/wryn_head_accessories.dmi new file mode 100644 index 00000000000..b33cf2b4fec Binary files /dev/null and b/icons/mob/sprite_accessories/wryn/wryn_head_accessories.dmi differ diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi index 4785f7b04b6..41e2553a558 100644 Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index ee7b3449452..37faeb4a436 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi index b2829a92ec0..2ad0edbd285 100644 Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ diff --git a/icons/obj/clothing/shoes.dmi b/icons/obj/clothing/shoes.dmi index 2f796a0ca27..52cf675c5d3 100644 Binary files a/icons/obj/clothing/shoes.dmi and b/icons/obj/clothing/shoes.dmi differ diff --git a/icons/obj/clothing/ties.dmi b/icons/obj/clothing/ties.dmi index 927b109e4ce..36108946fed 100644 Binary files a/icons/obj/clothing/ties.dmi and b/icons/obj/clothing/ties.dmi differ diff --git a/icons/obj/clothing/ties_overlay.dmi b/icons/obj/clothing/ties_overlay.dmi index ad4e4de039a..101285d6040 100644 Binary files a/icons/obj/clothing/ties_overlay.dmi and b/icons/obj/clothing/ties_overlay.dmi differ diff --git a/icons/obj/decorations.dmi b/icons/obj/decorations.dmi index 4a38c63a5fd..aae1281dfbe 100644 Binary files a/icons/obj/decorations.dmi and b/icons/obj/decorations.dmi differ diff --git a/icons/obj/economy.dmi b/icons/obj/economy.dmi index ea1bab2622f..34a9422cb68 100644 Binary files a/icons/obj/economy.dmi and b/icons/obj/economy.dmi differ diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi index f5191611985..bc70bbe144d 100644 Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ diff --git a/icons/obj/mecha/lockermech.dmi b/icons/obj/mecha/lockermech.dmi index ea99827ee41..a2ad9c80a1b 100644 Binary files a/icons/obj/mecha/lockermech.dmi and b/icons/obj/mecha/lockermech.dmi differ diff --git a/icons/obj/mecha/mecha.dmi b/icons/obj/mecha/mecha.dmi index 379e9875dea..d3315d18e7a 100644 Binary files a/icons/obj/mecha/mecha.dmi and b/icons/obj/mecha/mecha.dmi differ diff --git a/icons/obj/mecha/mecha_equipment.dmi b/icons/obj/mecha/mecha_equipment.dmi index d31ccb93fa1..b8112616f58 100644 Binary files a/icons/obj/mecha/mecha_equipment.dmi and b/icons/obj/mecha/mecha_equipment.dmi differ diff --git a/icons/obj/species_organs/wryn.dmi b/icons/obj/species_organs/wryn.dmi new file mode 100644 index 00000000000..38d775ca2bd Binary files /dev/null and b/icons/obj/species_organs/wryn.dmi differ diff --git a/icons/obj/statue.dmi b/icons/obj/statue.dmi index c55db97561f..d59456ef187 100644 Binary files a/icons/obj/statue.dmi and b/icons/obj/statue.dmi differ diff --git a/icons/obj/supplypods.dmi b/icons/obj/supplypods.dmi new file mode 100644 index 00000000000..156d3cea245 Binary files /dev/null and b/icons/obj/supplypods.dmi differ diff --git a/icons/obj/supplypods_32x32.dmi b/icons/obj/supplypods_32x32.dmi new file mode 100644 index 00000000000..855132f6494 Binary files /dev/null and b/icons/obj/supplypods_32x32.dmi differ diff --git a/icons/obj/voice_translator.dmi b/icons/obj/voice_translator.dmi new file mode 100644 index 00000000000..312563b8efa Binary files /dev/null and b/icons/obj/voice_translator.dmi differ diff --git a/icons/obj/weapons/energy.dmi b/icons/obj/weapons/energy.dmi index 76045167bac..2e2bbe0ef48 100644 Binary files a/icons/obj/weapons/energy.dmi and b/icons/obj/weapons/energy.dmi differ diff --git a/icons/turf/areas.dmi b/icons/turf/areas.dmi index 1d56391f9d3..3bd88590db8 100755 Binary files a/icons/turf/areas.dmi and b/icons/turf/areas.dmi differ diff --git a/interface/skin.dmf b/interface/skin.dmf index 7c6631bc3bf..794975c6ad8 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -219,7 +219,7 @@ window "mapwindow" window "outputwindow" elem "outputwindow" type = MAIN - pos = 281,0 + pos = 0,0 size = 640x480 anchor1 = -1,-1 anchor2 = -1,-1 @@ -271,15 +271,25 @@ window "outputwindow" command = ".winset \"mebutton.is-checked=true ? input.command=\"!me \\\"\" : input.command=\"\"mebutton.is-checked=true ? saybutton.is-checked=false\"\"mebutton.is-checked=true ? oocbutton.is-checked=false\"" is-flat = true button-type = pushbox - elem "chat_panel" - type = BROWSER + elem "legacy_output_selector" + type = CHILD pos = 0,0 size = 640x456 anchor1 = 0,0 anchor2 = 100,100 - is-visible = false - is-disabled = true - saved-params = "" + saved-params = "splitter" + left = "output_legacy" + is-vert = false +window "output_legacy" + elem "output_legacy" + type = MAIN + pos = 0,0 + size = 640x456 + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true elem "output" type = OUTPUT pos = 0,0 @@ -287,6 +297,25 @@ window "outputwindow" anchor1 = 0,0 anchor2 = 100,100 is-default = true + saved-params = "max-lines" + +window "output_browser" + elem "output_browser" + type = MAIN + pos = 0,0 + size = 640x456 + anchor1 = -1,-1 + anchor2 = -1,-1 + background-color = none + saved-params = "pos;size;is-minimized;is-maximized" + is-pane = true + elem "chat_panel" + type = BROWSER + pos = 0,0 + size = 640x456 + anchor1 = 0,0 + anchor2 = 100,100 + background-color = none saved-params = "" diff --git a/paradise.dme b/paradise.dme index 3a819c87632..2593ce0b4b0 100644 --- a/paradise.dme +++ b/paradise.dme @@ -41,6 +41,7 @@ #include "code\__DEFINES\bots.dm" #include "code\__DEFINES\byond_tracy.dm" #include "code\__DEFINES\callbacks.dm" +#include "code\__DEFINES\cargo.dm" #include "code\__DEFINES\cargo_quests.dm" #include "code\__DEFINES\chat.dm" #include "code\__DEFINES\chat_box_defines.dm" @@ -56,6 +57,7 @@ #include "code\__DEFINES\crafting.dm" #include "code\__DEFINES\criminal_status.dm" #include "code\__DEFINES\cult.dm" +#include "code\__DEFINES\devil.dm" #include "code\__DEFINES\directional.dm" #include "code\__DEFINES\diseases.dm" #include "code\__DEFINES\dmjit.dm" @@ -68,6 +70,7 @@ #include "code\__DEFINES\footstep.dm" #include "code\__DEFINES\game.dm" #include "code\__DEFINES\gamemode.dm" +#include "code\__DEFINES\generators.dm" #include "code\__DEFINES\genetics.dm" #include "code\__DEFINES\gravity.dm" #include "code\__DEFINES\hud.dm" @@ -104,6 +107,7 @@ #include "code\__DEFINES\obj_flags.dm" #include "code\__DEFINES\organ_defines.dm" #include "code\__DEFINES\overlays.dm" +#include "code\__DEFINES\particles.dm" #include "code\__DEFINES\path.dm" #include "code\__DEFINES\pda.dm" #include "code\__DEFINES\pipes.dm" @@ -118,8 +122,10 @@ #include "code\__DEFINES\rituals.dm" #include "code\__DEFINES\role_preferences.dm" #include "code\__DEFINES\rolebans.dm" +#include "code\__DEFINES\ru_lang_rules.dm" #include "code\__DEFINES\rust_g.dm" #include "code\__DEFINES\rust_g_overrides.dm" +#include "code\__DEFINES\say.dm" #include "code\__DEFINES\secret_documents.dm" #include "code\__DEFINES\sensor_devices.dm" #include "code\__DEFINES\shuttle.dm" @@ -156,6 +162,7 @@ #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\mapping.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals_blob.dm" #include "code\__DEFINES\dcs\signals_lazy_templates.dm" #include "code\__DEFINES\dcs\signals_object.dm" #include "code\__DEFINES\dcs\signals_turf.dm" @@ -171,6 +178,8 @@ #include "code\__HELPERS\atmospherics.dm" #include "code\__HELPERS\atoms.dm" #include "code\__HELPERS\bitflag_lists.dm" +#include "code\__HELPERS\bitflags.dm" +#include "code\__HELPERS\chat.dm" #include "code\__HELPERS\cmp.dm" #include "code\__HELPERS\constants.dm" #include "code\__HELPERS\experimental.dm" @@ -192,6 +201,7 @@ #include "code\__HELPERS\pronouns.dm" #include "code\__HELPERS\qdel.dm" #include "code\__HELPERS\reagents_helpers.dm" +#include "code\__HELPERS\ref.dm" #include "code\__HELPERS\russian.dm" #include "code\__HELPERS\sanitize_values.dm" #include "code\__HELPERS\shell.dm" @@ -202,6 +212,7 @@ #include "code\__HELPERS\time.dm" #include "code\__HELPERS\tool_helpers.dm" #include "code\__HELPERS\traits.dm" +#include "code\__HELPERS\turfs.dm" #include "code\__HELPERS\type2type.dm" #include "code\__HELPERS\typelists.dm" #include "code\__HELPERS\unique_ids.dm" @@ -216,7 +227,6 @@ #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" #include "code\_globalvars\_regexes.dm" -#include "code\_globalvars\bitfields.dm" #include "code\_globalvars\configuration.dm" #include "code\_globalvars\game_modes.dm" #include "code\_globalvars\genetics.dm" @@ -225,6 +235,16 @@ #include "code\_globalvars\misc.dm" #include "code\_globalvars\sensitive.dm" #include "code\_globalvars\traits.dm" +#include "code\_globalvars\bitfields\admin.dm" +#include "code\_globalvars\bitfields\bitfields.dm" +#include "code\_globalvars\bitfields\declarations.dm" +#include "code\_globalvars\bitfields\food.dm" +#include "code\_globalvars\bitfields\icon_smoothing.dm" +#include "code\_globalvars\bitfields\jobs.dm" +#include "code\_globalvars\bitfields\mecha.dm" +#include "code\_globalvars\bitfields\mobs.dm" +#include "code\_globalvars\bitfields\objs.dm" +#include "code\_globalvars\bitfields\sight.dm" #include "code\_globalvars\lists\flavor_misc.dm" #include "code\_globalvars\lists\fortunes.dm" #include "code\_globalvars\lists\keybindings.dm" @@ -255,6 +275,7 @@ #include "code\_onclick\hud\alien.dm" #include "code\_onclick\hud\alien_larva.dm" #include "code\_onclick\hud\blob_overmind.dm" +#include "code\_onclick\hud\blobbernaut.dm" #include "code\_onclick\hud\bot.dm" #include "code\_onclick\hud\cogscarab.dm" #include "code\_onclick\hud\constructs.dm" @@ -309,6 +330,7 @@ #include "code\controllers\subsystem\early_assets.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\fires.dm" +#include "code\controllers\subsystem\fluids.dm" #include "code\controllers\subsystem\game_events.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\ghost_spawns.dm" @@ -403,6 +425,7 @@ #include "code\datums\mutable_appearance.dm" #include "code\datums\periodic_news.dm" #include "code\datums\pipe_datums.dm" +#include "code\datums\pod_style.dm" #include "code\datums\position_point_vector.dm" #include "code\datums\progressbar.dm" #include "code\datums\radio.dm" @@ -439,18 +462,22 @@ #include "code\datums\components\after_attacks_hub.dm" #include "code\datums\components\animal_temperature.dm" #include "code\datums\components\aura_healing.dm" +#include "code\datums\components\blob_minion.dm" +#include "code\datums\components\blob_turf_consuming.dm" #include "code\datums\components\boomerang.dm" #include "code\datums\components\boss_music.dm" #include "code\datums\components\caltrop.dm" #include "code\datums\components\chasm.dm" #include "code\datums\components\codeword_hearing.dm" #include "code\datums\components\combo_attacks.dm" +#include "code\datums\components\connect_containers.dm" #include "code\datums\components\connect_loc_behalf.dm" #include "code\datums\components\connect_mob_behalf.dm" #include "code\datums\components\contsruction_regenerate.dm" #include "code\datums\components\conveyor_movement.dm" #include "code\datums\components\cross_shock.dm" #include "code\datums\components\decal.dm" +#include "code\datums\components\death_linked.dm" #include "code\datums\components\defibrillator.dm" #include "code\datums\components\drift.dm" #include "code\datums\components\ducttape.dm" @@ -459,14 +486,18 @@ #include "code\datums\components\examine_override.dm" #include "code\datums\components\force_move.dm" #include "code\datums\components\fullauto.dm" +#include "code\datums\components\ghost_direct_control.dm" #include "code\datums\components\hide_highest_offset.dm" +#include "code\datums\components\holderloving.dm" #include "code\datums\components\jackboots.dm" #include "code\datums\components\jetpack.dm" #include "code\datums\components\label.dm" #include "code\datums\components\material_container.dm" #include "code\datums\components\overlay_lighting.dm" #include "code\datums\components\paintable.dm" +#include "code\datums\components\pellet_cloud.dm" #include "code\datums\components\persistent_overlay.dm" +#include "code\datums\components\pref_viewer.dm" #include "code\datums\components\proximity_monitor.dm" #include "code\datums\components\radioactivity.dm" #include "code\datums\components\ritual_object.dm" @@ -475,6 +506,7 @@ #include "code\datums\components\spawner.dm" #include "code\datums\components\spooky.dm" #include "code\datums\components\squeak.dm" +#include "code\datums\components\stationloving.dm" #include "code\datums\components\surgery_initiator.dm" #include "code\datums\components\swarming.dm" #include "code\datums\components\transforming.dm" @@ -556,6 +588,8 @@ #include "code\datums\diseases\viruses\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" #include "code\datums\elements\connect_loc.dm" +#include "code\datums\elements\devil_regen.dm" +#include "code\datums\elements\devil_banishment.dm" #include "code\datums\elements\falling_hazard.dm" #include "code\datums\elements\footstep.dm" #include "code\datums\elements\give_turf_traits.dm" @@ -563,6 +597,7 @@ #include "code\datums\elements\movetype_handler.dm" #include "code\datums\elements\openspace_item_click_handler.dm" #include "code\datums\elements\ridable.dm" +#include "code\datums\elements\reagent_attack.dm" #include "code\datums\elements\simple_flying.dm" #include "code\datums\elements\squish.dm" #include "code\datums\elements\strippable.dm" @@ -610,6 +645,7 @@ #include "code\datums\spell_cooldown\spell_charges.dm" #include "code\datums\spell_cooldown\spell_cooldown.dm" #include "code\datums\spell_handler\alien_spell_handler.dm" +#include "code\datums\spell_handler\devil_spell_handler.dm" #include "code\datums\spell_handler\morph_spell_handler.dm" #include "code\datums\spell_handler\spell_handler.dm" #include "code\datums\spell_handler\vampire_spell_handler.dm" @@ -688,8 +724,10 @@ #include "code\datums\status_effects\debuffs.dm" #include "code\datums\status_effects\gas.dm" #include "code\datums\status_effects\neutral.dm" +#include "code\datums\status_effects\screwy_hud.dm" #include "code\datums\status_effects\status_effect.dm" #include "code\datums\status_effects\status_effects_absorption.dm" +#include "code\datums\status_effects\wet_stacks.dm" #include "code\datums\weather\weather.dm" #include "code\datums\weather\weather_types\ash_storm.dm" #include "code\datums\weather\weather_types\blob_storm.dm" @@ -760,17 +798,6 @@ #include "code\game\gamemodes\blob\blob.dm" #include "code\game\gamemodes\blob\blob_finish.dm" #include "code\game\gamemodes\blob\blob_report.dm" -#include "code\game\gamemodes\blob\overmind.dm" -#include "code\game\gamemodes\blob\powers.dm" -#include "code\game\gamemodes\blob\theblob.dm" -#include "code\game\gamemodes\blob\blobs\blob_mobs.dm" -#include "code\game\gamemodes\blob\blobs\captured_nuke.dm" -#include "code\game\gamemodes\blob\blobs\core.dm" -#include "code\game\gamemodes\blob\blobs\factory.dm" -#include "code\game\gamemodes\blob\blobs\node.dm" -#include "code\game\gamemodes\blob\blobs\resource.dm" -#include "code\game\gamemodes\blob\blobs\shield.dm" -#include "code\game\gamemodes\blob\blobs\storage.dm" #include "code\game\gamemodes\changeling\changeling.dm" #include "code\game\gamemodes\changeling\thief_chan.dm" #include "code\game\gamemodes\changeling\traitor_chan.dm" @@ -795,16 +822,8 @@ #include "code\game\gamemodes\cult\cult_structures.dm" #include "code\game\gamemodes\cult\ritual.dm" #include "code\game\gamemodes\cult\runes.dm" -#include "code\game\gamemodes\devil\devil.dm" -#include "code\game\gamemodes\devil\devil_game_mode.dm" -#include "code\game\gamemodes\devil\devilinfo.dm" #include "code\game\gamemodes\devil\game_mode.dm" #include "code\game\gamemodes\devil\objectives.dm" -#include "code\game\gamemodes\devil\contracts\friend.dm" -#include "code\game\gamemodes\devil\devil_agent\devil_agent.dm" -#include "code\game\gamemodes\devil\imp\imp.dm" -#include "code\game\gamemodes\devil\true_devil\_true_devil.dm" -#include "code\game\gamemodes\devil\true_devil\inventory.dm" #include "code\game\gamemodes\extended\extended.dm" #include "code\game\gamemodes\heist\heist.dm" #include "code\game\gamemodes\malfunction\Malf_Modules.dm" @@ -1087,6 +1106,7 @@ #include "code\game\objects\effects\mines.dm" #include "code\game\objects\effects\misc.dm" #include "code\game\objects\effects\overlays.dm" +#include "code\game\objects\effects\particle_holder.dm" #include "code\game\objects\effects\portals.dm" #include "code\game\objects\effects\snowcloud.dm" #include "code\game\objects\effects\spiders.dm" @@ -1117,6 +1137,9 @@ #include "code\game\objects\effects\effect_system\effects_smoke.dm" #include "code\game\objects\effects\effect_system\effects_sparks.dm" #include "code\game\objects\effects\effect_system\effects_water.dm" +#include "code\game\objects\effects\effect_system\fluid_spread\_fluid_spread.dm" +#include "code\game\objects\effects\effect_system\fluid_spread\effects_smoke.dm" +#include "code\game\objects\effects\particles\water.dm" #include "code\game\objects\effects\spawners\airlock_spawner.dm" #include "code\game\objects\effects\spawners\bombspawner.dm" #include "code\game\objects\effects\spawners\gibspawner.dm" @@ -1297,6 +1320,7 @@ #include "code\game\objects\items\weapons\twohanded.dm" #include "code\game\objects\items\weapons\vending_items.dm" #include "code\game\objects\items\weapons\weaponry.dm" +#include "code\game\objects\items\weapons\welder_sword.dm" #include "code\game\objects\items\weapons\whetstone.dm" #include "code\game\objects\items\weapons\grenades\atmosgrenade.dm" #include "code\game\objects\items\weapons\grenades\bananade.dm" @@ -1547,6 +1571,7 @@ #include "code\modules\admin\verbs\atmosdebug.dm" #include "code\modules\admin\verbs\borgpanel.dm" #include "code\modules\admin\verbs\BrokenInhands.dm" +#include "code\modules\admin\verbs\cantcomm_cargo.dm" #include "code\modules\admin\verbs\cinematic.dm" #include "code\modules\admin\verbs\custom_event.dm" #include "code\modules\admin\verbs\deadsay.dm" @@ -1571,6 +1596,7 @@ #include "code\modules\admin\verbs\possess.dm" #include "code\modules\admin\verbs\pray.dm" #include "code\modules\admin\verbs\randomverbs.dm" +#include "code\modules\admin\verbs\reagents_editor.dm" #include "code\modules\admin\verbs\requests.dm" #include "code\modules\admin\verbs\serialization.dm" #include "code\modules\admin\verbs\space_transitions.dm" @@ -1590,11 +1616,49 @@ #include "code\modules\antagonists\_common\antag_team.dm" #include "code\modules\antagonists\blob\blob_actions.dm" #include "code\modules\antagonists\blob\blob_infected_datum.dm" +#include "code\modules\antagonists\blob\blob_minion.dm" #include "code\modules\antagonists\blob\blob_overmind_datum.dm" +#include "code\modules\antagonists\blob\blobs_attack.dm" +#include "code\modules\antagonists\blob\overmind.dm" +#include "code\modules\antagonists\blob\powers.dm" +#include "code\modules\antagonists\blob\powers_verbs.dm" +#include "code\modules\antagonists\blob\blob_minions\blob_mob.dm" +#include "code\modules\antagonists\blob\blob_minions\blob_spore.dm" +#include "code\modules\antagonists\blob\blob_minions\blob_zombie.dm" +#include "code\modules\antagonists\blob\blob_minions\blobbernaut.dm" +#include "code\modules\antagonists\blob\blobstrains\_blobstrain.dm" +#include "code\modules\antagonists\blob\blobstrains\_reagent.dm" +#include "code\modules\antagonists\blob\blobstrains\blazing_oil.dm" +#include "code\modules\antagonists\blob\blobstrains\blob_sorium.dm" +#include "code\modules\antagonists\blob\blobstrains\cryogenic_poison.dm" +#include "code\modules\antagonists\blob\blobstrains\debris_devourer.dm" +#include "code\modules\antagonists\blob\blobstrains\distributed_neurons.dm" +#include "code\modules\antagonists\blob\blobstrains\electromagnetic_web.dm" +#include "code\modules\antagonists\blob\blobstrains\energized_jelly.dm" +#include "code\modules\antagonists\blob\blobstrains\explosive_lattice.dm" +#include "code\modules\antagonists\blob\blobstrains\multiplex.dm" +#include "code\modules\antagonists\blob\blobstrains\networked_fibers.dm" +#include "code\modules\antagonists\blob\blobstrains\pressurized_slime.dm" +#include "code\modules\antagonists\blob\blobstrains\radioactive_gel.dm" +#include "code\modules\antagonists\blob\blobstrains\reactive_spines.dm" +#include "code\modules\antagonists\blob\blobstrains\regenerative_materia.dm" +#include "code\modules\antagonists\blob\blobstrains\replicating_foam.dm" +#include "code\modules\antagonists\blob\blobstrains\shifting_fragments.dm" +#include "code\modules\antagonists\blob\blobstrains\synchronous_mesh.dm" +#include "code\modules\antagonists\blob\structures\_blob.dm" +#include "code\modules\antagonists\blob\structures\captured_nuke.dm" +#include "code\modules\antagonists\blob\structures\core.dm" +#include "code\modules\antagonists\blob\structures\factory.dm" +#include "code\modules\antagonists\blob\structures\node.dm" +#include "code\modules\antagonists\blob\structures\normal.dm" +#include "code\modules\antagonists\blob\structures\resource.dm" +#include "code\modules\antagonists\blob\structures\shield.dm" +#include "code\modules\antagonists\blob\structures\special.dm" +#include "code\modules\antagonists\blob\structures\storage.dm" #include "code\modules\antagonists\borer\borer_action.dm" #include "code\modules\antagonists\borer\borer_datum.dm" -#include "code\modules\antagonists\borer\borer_focus.dm" #include "code\modules\antagonists\borer\borer_rank.dm" +#include "code\modules\antagonists\borer\borer_focus.dm" #include "code\modules\antagonists\borer\borer_reagent.dm" #include "code\modules\antagonists\borer\borer_spell.dm" #include "code\modules\antagonists\changeling\changeling_datum.dm" @@ -1623,6 +1687,21 @@ #include "code\modules\antagonists\changeling\powers\swap_form.dm" #include "code\modules\antagonists\changeling\powers\tiny_prick.dm" #include "code\modules\antagonists\changeling\powers\transform.dm" +#include "code\modules\antagonists\devil\devil.dm" +#include "code\modules\antagonists\devil\sintouched.dm" +#include "code\modules\antagonists\devil\devil_banish.dm" +#include "code\modules\antagonists\devil\devil_bane.dm" +#include "code\modules\antagonists\devil\devil_ban.dm" +#include "code\modules\antagonists\devil\devil_info.dm" +#include "code\modules\antagonists\devil\devil_pawn.dm" +#include "code\modules\antagonists\devil\imp\imp.dm" +#include "code\modules\antagonists\devil\devil_obligation.dm" +#include "code\modules\antagonists\devil\devil_rank.dm" +#include "code\modules\antagonists\devil\devil_ritual.dm" +#include "code\modules\antagonists\devil\devil_outfit.dm" +#include "code\modules\antagonists\devil\helper_procs.dm" +#include "code\modules\antagonists\devil\contracts\friend.dm" +#include "code\modules\antagonists\devil\true_devil\_true_devil.dm" #include "code\modules\antagonists\malf_ai\malf_ai_datum.dm" #include "code\modules\antagonists\space_dragon\action.dm" #include "code\modules\antagonists\space_dragon\carp.dm" @@ -1758,6 +1837,7 @@ #include "code\modules\asset_cache\assets\asset_cloning.dm" #include "code\modules\asset_cache\assets\asset_common.dm" #include "code\modules\asset_cache\assets\asset_emoji.dm" +#include "code\modules\asset_cache\assets\asset_icon_ref_map.dm" #include "code\modules\asset_cache\assets\asset_id_card.dm" #include "code\modules\asset_cache\assets\asset_jquery.dm" #include "code\modules\asset_cache\assets\asset_lobby.dm" @@ -1772,6 +1852,7 @@ #include "code\modules\asset_cache\assets\asset_seeds.dm" #include "code\modules\asset_cache\assets\asset_strip.dm" #include "code\modules\asset_cache\assets\asset_tgui.dm" +#include "code\modules\asset_cache\assets\supplypods.dm" #include "code\modules\asset_cache\transports\asset_transport.dm" #include "code\modules\asset_cache\transports\webroot_transport.dm" #include "code\modules\atmospherics\enviromental\LINDA_fire.dm" @@ -1874,6 +1955,8 @@ #include "code\modules\buildmode\submodes\save.dm" #include "code\modules\buildmode\submodes\throwing.dm" #include "code\modules\buildmode\submodes\variable_edit.dm" +#include "code\modules\cargo\centcom_podlauncher.dm" +#include "code\modules\cargo\supplypod.dm" #include "code\modules\client\client_defines.dm" #include "code\modules\client\client_procs.dm" #include "code\modules\client\geoip.dm" @@ -1881,6 +1964,7 @@ #include "code\modules\client\ping.dm" #include "code\modules\client\view.dm" #include "code\modules\client\preference\preferences.dm" +#include "code\modules\client\preference\preference_info.dm" #include "code\modules\client\preference\preferences_mysql.dm" #include "code\modules\client\preference\preferences_spawnpoints.dm" #include "code\modules\client\preference\preferences_toggles.dm" @@ -1899,6 +1983,7 @@ #include "code\modules\client\preference\loadout\loadout_racial.dm" #include "code\modules\client\preference\loadout\loadout_shoes.dm" #include "code\modules\client\preference\loadout\loadout_suit.dm" +#include "code\modules\client\preference\loadout\loadout_tgui.dm" #include "code\modules\client\preference\loadout\loadout_uniform.dm" #include "code\modules\clothing\clothing.dm" #include "code\modules\clothing\chameleon\_chameleon_actions.dm" @@ -2133,6 +2218,7 @@ #include "code\modules\games\cards.dm" #include "code\modules\games\tarot.dm" #include "code\modules\games\unum.dm" +#include "code\modules\hallucination\_hallucination.dm" #include "code\modules\holiday\christmas.dm" #include "code\modules\holiday\holiday.dm" #include "code\modules\holiday\new_year.dm" @@ -2345,6 +2431,7 @@ #include "code\modules\mob\mob_defines.dm" #include "code\modules\mob\mob_emote.dm" #include "code\modules\mob\mob_helpers.dm" +#include "code\modules\mob\mob_lists.dm" #include "code\modules\mob\mob_movement.dm" #include "code\modules\mob\mob_say.dm" #include "code\modules\mob\mob_transformation_simple.dm" @@ -2363,6 +2450,7 @@ #include "code\modules\mob\dead\observer\observer_say.dm" #include "code\modules\mob\dead\observer\orbit.dm" #include "code\modules\mob\dead\observer\spells.dm" +#include "code\modules\mob\living\alpha.dm" #include "code\modules\mob\living\autohiss.dm" #include "code\modules\mob\living\damage_procs.dm" #include "code\modules\mob\living\death.dm" @@ -2599,6 +2687,8 @@ #include "code\modules\mob\living\simple_animal\friendly\snake.dm" #include "code\modules\mob\living\simple_animal\friendly\snake_stripping.dm" #include "code\modules\mob\living\simple_animal\friendly\spiderbot.dm" +#include "code\modules\mob\living\simple_animal\gondolas\gondola.dm" +#include "code\modules\mob\living\simple_animal\gondolas\gondolapod.dm" #include "code\modules\mob\living\simple_animal\hostile\alien.dm" #include "code\modules\mob\living\simple_animal\hostile\bat.dm" #include "code\modules\mob\living\simple_animal\hostile\bear.dm" @@ -2744,7 +2834,8 @@ #include "code\modules\mob\new_player\sprite_accessories\vulpkanin\vulpkanin_head_accessories.dm" #include "code\modules\mob\new_player\sprite_accessories\vulpkanin\vulpkanin_head_markings.dm" #include "code\modules\mob\new_player\sprite_accessories\vulpkanin\vulpkanin_tail_markings.dm" -#include "code\modules\mob\new_player\sprite_accessories\wryn\wryn_face.dm" +#include "code\modules\mob\new_player\sprite_accessories\wryn\wryn_facial_hair.dm" +#include "code\modules\mob\new_player\sprite_accessories\wryn\wryn_hair.dm" #include "code\modules\movespeed\_movespeed_modifier.dm" #include "code\modules\movespeed\modifiers\components.dm" #include "code\modules\movespeed\modifiers\innate.dm" @@ -2895,6 +2986,7 @@ #include "code\modules\projectiles\projectile\force.dm" #include "code\modules\projectiles\projectile\magic.dm" #include "code\modules\projectiles\projectile\reusable.dm" +#include "code\modules\projectiles\projectile\shrapnel.dm" #include "code\modules\projectiles\projectile\special.dm" #include "code\modules\projectiles\sibyl\sibyl_system_mod.dm" #include "code\modules\projectiles\sibyl\sibyl_weapons.dm" @@ -2913,7 +3005,6 @@ #include "code\modules\reagents\chemistry\machinery\reagentgrinder.dm" #include "code\modules\reagents\chemistry\reagents\admin.dm" #include "code\modules\reagents\chemistry\reagents\alcohol.dm" -#include "code\modules\reagents\chemistry\reagents\blob.dm" #include "code\modules\reagents\chemistry\reagents\disease.dm" #include "code\modules\reagents\chemistry\reagents\drink_base.dm" #include "code\modules\reagents\chemistry\reagents\drink_cold.dm" @@ -3100,6 +3191,7 @@ #include "code\modules\surgery\organs\robolimbs.dm" #include "code\modules\surgery\organs\skeleton.dm" #include "code\modules\surgery\organs\vocal_cords.dm" +#include "code\modules\surgery\organs\voice_translator.dm" #include "code\modules\surgery\organs\subtypes\abductor.dm" #include "code\modules\surgery\organs\subtypes\diona.dm" #include "code\modules\surgery\organs\subtypes\drask.dm" @@ -3169,9 +3261,12 @@ #include "code\modules\tgui\states\strippable_state.dm" #include "code\modules\tgui\states\zlevel.dm" #include "code\modules\tgui\tgui_input\alert_input.dm" +#include "code\modules\tgui\tgui_input\input_checkbox.dm" +#include "code\modules\tgui\tgui_input\color_input.dm" #include "code\modules\tgui\tgui_input\keycombo_input.dm" #include "code\modules\tgui\tgui_input\list_input.dm" #include "code\modules\tgui\tgui_input\number_input.dm" +#include "code\modules\tgui\tgui_input\ranked_list_input.dm" #include "code\modules\tgui\tgui_input\text_input.dm" #include "code\modules\tgui\tgui_panel\audio.dm" #include "code\modules\tgui\tgui_panel\telemetry.dm" diff --git a/sound/effects/podwoosh.ogg b/sound/effects/podwoosh.ogg new file mode 100644 index 00000000000..6edcba62737 Binary files /dev/null and b/sound/effects/podwoosh.ogg differ diff --git a/sound/effects/vending_hit.ogg b/sound/effects/vending_hit.ogg new file mode 100644 index 00000000000..a9438c26a7d Binary files /dev/null and b/sound/effects/vending_hit.ogg differ diff --git a/sound/machines/generator/generator_end.ogg b/sound/machines/generator/generator_end.ogg new file mode 100644 index 00000000000..2b2c97ee744 Binary files /dev/null and b/sound/machines/generator/generator_end.ogg differ diff --git a/sound/machines/generator/generator_mid1.ogg b/sound/machines/generator/generator_mid1.ogg new file mode 100644 index 00000000000..332b5af9a0e Binary files /dev/null and b/sound/machines/generator/generator_mid1.ogg differ diff --git a/sound/machines/generator/generator_mid2.ogg b/sound/machines/generator/generator_mid2.ogg new file mode 100644 index 00000000000..d71c7b2ae0a Binary files /dev/null and b/sound/machines/generator/generator_mid2.ogg differ diff --git a/sound/machines/generator/generator_mid3.ogg b/sound/machines/generator/generator_mid3.ogg new file mode 100644 index 00000000000..7ee161824d0 Binary files /dev/null and b/sound/machines/generator/generator_mid3.ogg differ diff --git a/sound/machines/generator/generator_start.ogg b/sound/machines/generator/generator_start.ogg new file mode 100644 index 00000000000..a9087bd3a7a Binary files /dev/null and b/sound/machines/generator/generator_start.ogg differ diff --git a/sound/weapons/gunshots/lasergatling.ogg b/sound/weapons/gunshots/lasergatling.ogg new file mode 100644 index 00000000000..02504bf94ec Binary files /dev/null and b/sound/weapons/gunshots/lasergatling.ogg differ diff --git a/sound/weapons/mortar_long_whistle.ogg b/sound/weapons/mortar_long_whistle.ogg new file mode 100644 index 00000000000..646d37d8ab6 Binary files /dev/null and b/sound/weapons/mortar_long_whistle.ogg differ diff --git a/sound/weapons/mortar_whistle.ogg b/sound/weapons/mortar_whistle.ogg new file mode 100644 index 00000000000..2d7e19d85da Binary files /dev/null and b/sound/weapons/mortar_whistle.ogg differ diff --git a/tgui/docs/component-reference.md b/tgui/docs/component-reference.md index af744b61793..c02ec546d68 100644 --- a/tgui/docs/component-reference.md +++ b/tgui/docs/component-reference.md @@ -31,6 +31,7 @@ Make sure to add new items to this list if you document new components. - [`Icon.Stack`](#iconstack) - [`ImageButton`](#imagebutton) - [`ImageButton.Item`](#imagebuttonitem) + - [`ImageButtonTS`](#imagebuttonts) - [`Input`](#input) - [`Knob`](#knob) - [`LabeledControls`](#labeledcontrols) @@ -70,16 +71,16 @@ Event handlers are callbacks that you can attack to various element to listen for browser events. Inferno supports camelcase (`onClick`) and lowercase (`onclick`) event names. -- Camel case names are what's called *synthetic* events, and are the -**preferred way** of handling events in React, for efficiency and -performance reasons. Please read -[Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) -to understand what this is about. +- Camel case names are what's called _synthetic_ events, and are the + **preferred way** of handling events in React, for efficiency and + performance reasons. Please read + [Inferno Event Handling](https://infernojs.org/docs/guides/event-handling) + to understand what this is about. - Lower case names are native browser events and should be used sparingly, -for example when you need an explicit IE8 support. **DO NOT** use -lowercase event handlers unless you really know what you are doing. + for example when you need an explicit IE8 support. **DO NOT** use + lowercase event handlers unless you really know what you are doing. - [Button](#button) component does not support the lowercase `onclick` event. -Use the camel case `onClick` instead. + Use the camel case `onClick` instead. ## `tgui/components` @@ -91,13 +92,13 @@ This component provides animations for numeric values. - `value: number` - Value to animate. - `initial: number` - Initial value to use in animation when element -first appears. If you set initial to `0` for example, number will always -animate starting from `0`, and if omitted, it will not play an initial -animation. + first appears. If you set initial to `0` for example, number will always + animate starting from `0`, and if omitted, it will not play an initial + animation. - `format: value => value` - Output formatter. - Example: `value => Math.round(value)`. - `children: (formattedValue, rawValue) => any` - Pull the animated number to -animate more complex things deeper in the DOM tree. + animate more complex things deeper in the DOM tree. - Example: `(_, value) => ` ### `BlockQuote` @@ -133,9 +134,7 @@ To workaround this problem, the Box children accept a render props function. This way, `Button` can pull out the `className` generated by the `Box`. ```jsx - - {props => + ``` @@ -407,17 +402,17 @@ effectively places the last flex item to the very end of the flex container. - See inherited props: [Box](#box) - ~~`spacing: number`~~ - **Removed in tgui 4.3**, -use [Stack](#stack) instead. + use [Stack](#stack) instead. - `inline: boolean` - Makes flexbox container inline, with similar behavior -to an `inline` property on a `Box`. + to an `inline` property on a `Box`. - `direction: string` - This establishes the main-axis, thus defining the -direction flex items are placed in the flex container. + direction flex items are placed in the flex container. - `row` (default) - left to right. - `row-reverse` - right to left. - `column` - top to bottom. - `column-reverse` - bottom to top. - `wrap: string` - By default, flex items will all try to fit onto one line. -You can change that and allow the items to wrap as needed with this property. + You can change that and allow the items to wrap as needed with this property. - `nowrap` (default) - all flex items will be on one line - `wrap` - flex items will wrap onto multiple lines, from top to bottom. - `wrap-reverse` - flex items will wrap onto multiple lines from bottom to top. @@ -428,22 +423,22 @@ You can change that and allow the items to wrap as needed with this property. - `center` - items are centered on the cross axis. - `baseline` - items are aligned such as their baselines align. - `justify: string` - This defines the alignment along the main axis. -It helps distribute extra free space leftover when either all the flex -items on a line are inflexible, or are flexible but have reached their -maximum size. It also exerts some control over the alignment of items -when they overflow the line. + It helps distribute extra free space leftover when either all the flex + items on a line are inflexible, or are flexible but have reached their + maximum size. It also exerts some control over the alignment of items + when they overflow the line. - `flex-start` (default) - items are packed toward the start of the - flex-direction. + flex-direction. - `flex-end` - items are packed toward the end of the flex-direction. - `space-between` - items are evenly distributed in the line; first item is - on the start line, last item on the end line + on the start line, last item on the end line - `space-around` - items are evenly distributed in the line with equal space - around them. Note that visually the spaces aren't equal, since all the items - have equal space on both sides. The first item will have one unit of space - against the container edge, but two units of space between the next item - because that next item has its own spacing that applies. + around them. Note that visually the spaces aren't equal, since all the items + have equal space on both sides. The first item will have one unit of space + against the container edge, but two units of space between the next item + because that next item has its own spacing that applies. - `space-evenly` - items are distributed so that the spacing between any two - items (and the space to the edges) is equal. + items (and the space to the edges) is equal. - TBD (not all properties are supported in IE11). ### `Flex.Item` @@ -452,24 +447,24 @@ when they overflow the line. - See inherited props: [Box](#box) - `order: number` - By default, flex items are laid out in the source order. -However, the order property controls the order in which they appear in the -flex container. + However, the order property controls the order in which they appear in the + flex container. - `grow: number | boolean` - This defines the ability for a flex item to grow -if necessary. It accepts a unitless value that serves as a proportion. It -dictates what amount of the available space inside the flex container the -item should take up. This number is unit-less and is relative to other -siblings. + if necessary. It accepts a unitless value that serves as a proportion. It + dictates what amount of the available space inside the flex container the + item should take up. This number is unit-less and is relative to other + siblings. - `shrink: number | boolean` - This defines the ability for a flex item to -shrink if necessary. Inverse of `grow`. + shrink if necessary. Inverse of `grow`. - `basis: number | string` - This defines the default size of an element -before any flex-related calculations are done. Has to be a length -(e.g. `20%`, `5rem`), an `auto` or `content` keyword. + before any flex-related calculations are done. Has to be a length + (e.g. `20%`, `5rem`), an `auto` or `content` keyword. - **Important:** IE11 flex is buggy, and auto width/height calculations - can sometimes end up in a circular dependency. This usually happens, when - working with tables inside flex (they have wacky internal widths and such). - Setting basis to `0` breaks the loop and fixes all of the problems. + can sometimes end up in a circular dependency. This usually happens, when + working with tables inside flex (they have wacky internal widths and such). + Setting basis to `0` breaks the loop and fixes all of the problems. - `align: string` - This allows the default alignment (or the one specified by -align-items) to be overridden for individual flex items. See: [Flex](#flex). + align-items) to be overridden for individual flex items. See: [Flex](#flex). ### `Grid` @@ -485,14 +480,10 @@ Example: ```jsx -
- Hello world! -
+
Hello world!
-
- Hello world! -
+
Hello world!
``` @@ -518,6 +509,7 @@ Renders one of the FontAwesome icons of your choice. To smoothen the transition from v4 to v5, we have added a v4 semantic to transform names with `-o` suffixes to FA Regular icons. For example: + - `square` will get transformed to `fas square` - `square-o` will get transformed to `far square` @@ -526,10 +518,10 @@ transform names with `-o` suffixes to FA Regular icons. For example: - See inherited props: [Box](#box) - `name: string` - Icon name. - `size: number` - Icon size. `1` is normal size, `2` is two times bigger. -Fractional numbers are supported. + Fractional numbers are supported. - `rotation: number` - Icon rotation, in degrees. - `spin: boolean` - Whether an icon should be spinning. Good for load -indicators. + indicators. ### `Icon.Stack` @@ -558,26 +550,26 @@ Has support for base64, spritesheets and URLs. - `asset: boolean` - Enables spritesheets support. - `vertical: boolean` - Makes the button a inlined vertical rectangle. - `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. + all colours are available in KitchenSink in the corresponding section. - `title: string` - The top text, it will always be bold, and also adds a divider between title and content. -Disabled if there is no content. + Disabled if there is no content. - `content: string|any` - All main content, usually text, but you can put in other components if you like. -Makes the vertical button square if empty. + Makes the vertical button square if empty. - `selected: boolean` - Makes button selected (green) if true. - `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. - `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. - `image: string` - Base64 image, simple. Disabled if asset support enabled. - `imageUrl: string` - PNG image or other asset. Make sure you use existing simple asset! Example: imageUrl={'image.png'} - `imageAsset: string` - If you have enabled asset support, write here which spritesheet to use. -Example: imageAsset={'spritesheet_name64x64'} + Example: imageAsset={'spritesheet_name64x64'} - `imageSize: string` - Sets the size of the image and adjusts the size of the button itself accordingly. -Example: imageSize={'64px'} + Example: imageSize={'64px'} - `tooltip: string` - A fancy, boxy tooltip, which appears when hovering -over the button. + over the button. - `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. - `ellipsis: boolean` - If button width is constrained, button text will -be truncated with an ellipsis. Be careful however, because this prop breaks -the baseline alignment. + be truncated with an ellipsis. Be careful however, because this prop breaks + the baseline alignment. - `children: ImageButton.Item|any` - Items that are added to the right of the horizontal button. - `onClick: function` - Called when element is clicked. Also enables hover effects. @@ -589,25 +581,60 @@ Additional button/s for ImageButton. > Available only in horizontal mode, if you try add it to vertical, you're gonna be disappointed **Props:** + - See inherited props: [Box](#box) - `color: string` - By default, the button is semi-transparent. You can change the overall colour, -all colours are available in KitchenSink in the corresponding section. + all colours are available in KitchenSink in the corresponding section. - `content: string|any` - All main content, usually text, but you can put in other components if you like. -Try to not make it too long. + Try to not make it too long. - `selected: boolean` - Makes button selected (green) if true. - `disabled: boolean` - Makes button disabled (red) if true. Also disables onClick. - `disabledContent: string` - If button disabled and disabledContent filled, it will be used instead content. - `tooltip: string` - A fancy, boxy tooltip, which appears when hovering -over the button. + over the button. - `tooltipPosition: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options. - `icon: string` - Adds an icon to the button. By default it will be under content. - `iconColor: string` - Paints icon if it used. - `iconPosition: string` - You can make an icon above the content. -Example: iconPosition={'top'} + Example: iconPosition={'top'} - `iconSize: number` - Adjusts the size of the icon. - `children: any` - Similar to content. - `onClick: function` - Called when element is clicked. +### `ImageButtonTS` + +A Robust button is specifically for sticking a picture in it. + +**Props:** + +- See inherited props: [Box](#box) +- `asset: string[]` - Asset cache. Example: `asset={`assetname32x32, ${thing.key}`}` +- `base64: string` - Classic way to put images. Example: `base64={thing.image}` +- `buttons: any` - Special section for any component, or, content. + Quite a small area at the bottom of the image in non-fluid mode. + Has a style overrides, best to use [Button](#button) inside. +- `buttonsAlt: boolean` - Enables alternative buttons layout. + With fluid, makes buttons like a humburger. + Without, moves it to top, and disables pointer-events. +- `children: any` - Content under image. +- `className: string` - Applies a CSS class to the element. +- `color: string` - Color of the button, but without `transparent`; see [Button](#button) +- `disabled: boolean` - Makes button disabled and dark red if true. + Also disables onClick & onRightClick. +- `selected: boolean` - Makes button selected and green if true. +- `dmFallback: any` - Optional. Adds a "stub" when loading DmIcon. +- `dmIcon: string` - Parameter `icon` of component `DmIcon`. +- `dmIconState: string` - Parameter `icon_state` of component `DmIcon`. + For proper work of `DmIcon` it is necessary that both parameters are filled in! +- `fluid: boolean` - Changes the layout of the button, making it fill the entire horizontally available space. + Allows the use of `title` +- `imageSize: number` - Parameter responsible for the size of the image, component and standard "stubs". + Measured in pixels. `imageSize={64}` = 64px. +- `imageSrc: string` - Prop `src` of . Example: `imageSrc={resolveAsset(thing.image)}` +- `onClick: (e) => void` - Called when button is clicked with LMB. +- `onRightClick: (e) => void` - Called when button is clicked with RMB. +- `title: string` - Requires `fluid` for work. Bold text with divider betwen content. + ### `Input` A basic text input, which allow users to enter text into a UI. @@ -620,12 +647,12 @@ A basic text input, which allow users to enter text into a UI. - See inherited props: [Box](#box) - `value: string` - Value of an input. - `placeholder: string` - Text placed into Input box when it's empty, -otherwise nothing. Clears automatically when focused. + otherwise nothing. Clears automatically when focused. - `fluid: boolean` - Fill all available horizontal space. - `selfClear: boolean` - Clear after hitting enter, as well as remain focused -when this happens. Useful for things like chat inputs. + when this happens. Useful for things like chat inputs. - `onChange: (e, value) => void` - An event, which fires when you commit -the text by either unfocusing the input box, or by pressing the Enter key. + the text by either unfocusing the input box, or by pressing the Enter key. - `onInput: (e, value) => void` - An event, which fires on every keypress. ### `Knob` @@ -641,30 +668,30 @@ Single click opens an input box to manually type in a number. - `animated: boolean` - Animates the value if it was changed externally. - `bipolar: boolean` - Knob can be bipolar or unipolar. - `size: number` - Relative size of the knob. `1` is normal size, `2` is two -times bigger. Fractional numbers are supported. + times bigger. Fractional numbers are supported. - `color: string` - Color of the outer ring around the knob. - `value: number` - Value itself, controls the position of the cursor. - `unit: string` - Unit to display to the right of value. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `fillValue: number` - If set, this value will be used to set the fill -percentage of the outer ring independently of the main value. + percentage of the outer ring independently of the main value. - `ranges: { color: [from, to] }` - Applies a `color` to the outer ring around -the knob based on whether the value lands in the range between `from` and `to`. -See an example of this prop in [ProgressBar](#progressbar). + the knob based on whether the value lands in the range between `from` and `to`. + See an example of this prop in [ProgressBar](#progressbar). - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `Popper` @@ -702,9 +729,7 @@ column is labels, and second column is content. ```jsx - - Content - + Content ``` @@ -713,13 +738,7 @@ to perform some sort of action), there is a way to do that: ```jsx - - Click me! - - )}> + Click me!}> Content @@ -746,9 +765,7 @@ Example: ```jsx - - Content - + Content ``` @@ -794,22 +811,22 @@ to fine tune the value, or single click it to manually type a number. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `width: string|number` - Width of the element, in `Box` units or pixels. - `height: string|numer` - Height of the element, in `Box` units or pixels. - `lineHeight: string|number` - lineHeight of the element, in `Box` units or pixels. - `fontSize: string|number` - fontSize of the element, in `Box` units or pixels. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `ProgressBar` @@ -828,18 +845,19 @@ Usage of `ranges` prop: average: [0.25, 0.5], bad: [-Infinity, 0.25], }} - value={0.6} /> + value={0.6} +/> ``` **Props:** - `value: number` - Current progress as a floating point number between -`minValue` (default: 0) and `maxValue` (default: 1). Determines the -percentage and how filled the bar is. + `minValue` (default: 0) and `maxValue` (default: 1). Determines the + percentage and how filled the bar is. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `ranges: { color: [from, to] }` - Applies a `color` to the progress bar -based on whether the value lands in the range between `from` and `to`. + based on whether the value lands in the range between `from` and `to`. - `color: string` - Color of the progress bar. - `children: any` - Content to render inside the progress bar. @@ -853,13 +871,14 @@ The RoundGauge component provides a visual representation of a single metric, as value={tankPressure} minValue={0} maxValue={pressureLimit} - alertAfter={pressureLimit * 0.70} + alertAfter={pressureLimit * 0.7} ranges={{ - "good": [0, pressureLimit * 0.70], - "average": [pressureLimit * 0.70, pressureLimit * 0.85], - "bad": [pressureLimit * 0.85, pressureLimit], + 'good': [0, pressureLimit * 0.7], + 'average': [pressureLimit * 0.7, pressureLimit * 0.85], + 'bad': [pressureLimit * 0.85, pressureLimit], }} - format={formatPressure} /> + format={formatPressure} +/> ``` The alert on the gauge is optional, and will only be shown if the `alertAfter` prop is defined. When defined, the alert will begin to flash the respective color upon which the needle currently rests, as defined in the `ranges` prop. @@ -886,22 +905,14 @@ clearly indicates hierarchy. Section can also be titled to clearly define its purpose. ```jsx -
- Here you can order supply crates. -
+
Here you can order supply crates.
``` If you want to have a button on the right side of an section title (for example, to perform some sort of action), there is a way to do that: ```jsx -
- Send shuttle - - )}> +
Send shuttle}> Here you can order supply crates.
``` @@ -909,7 +920,7 @@ If you want to have a button on the right side of an section title - See inherited props: [Box](#box) - `title: string` - Title of the section. - `level: number` - Section level in hierarchy. Default is 1, higher number -means deeper level of nesting. Must be an integer number. + means deeper level of nesting. Must be an integer number. - `buttons: any` - Buttons to render aside the section title. - `fill: boolean` - If true, fills all available vertical space. - `fitted: boolean` - If true, removes all section padding. @@ -933,23 +944,23 @@ Single click opens an input box to manually type in a number. - `minValue: number` - Lowest possible value. - `maxValue: number` - Highest possible value. - `fillValue: number` - If set, this value will be used to set the fill -percentage of the progress bar filler independently of the main value. + percentage of the progress bar filler independently of the main value. - `ranges: { color: [from, to] }` - Applies a `color` to the slider -based on whether the value lands in the range between `from` and `to`. -See an example of this prop in [ProgressBar](#progressbar). + based on whether the value lands in the range between `from` and `to`. + See an example of this prop in [ProgressBar](#progressbar). - `step: number` (default: 1) - Adjust value by this amount when -dragging the input. + dragging the input. - `stepPixelSize: number` (default: 1) - Screen distance mouse needs -to travel to adjust value by one `step`. + to travel to adjust value by one `step`. - `format: value => value` - Format value using this function before -displaying it. + displaying it. - `suppressFlicker: number` - A number in milliseconds, for which the input -will hold off from updating while events propagate through the backend. -Default is about 250ms, increase it if you still see flickering. + will hold off from updating while events propagate through the backend. + Default is about 250ms, increase it if you still see flickering. - `onChange: (e, value) => void` - An event, which fires when you release -the input, or successfully enter a number. + the input, or successfully enter a number. - `onDrag: (e, value) => void` - An event, which fires about every 500ms -when you drag the input up and down, on release and on manual editing. + when you drag the input up and down, on release and on manual editing. ### `Stack` @@ -965,13 +976,9 @@ Stacks can be vertical by adding a `vertical` property. ```jsx - - Button description - + Button description - + ``` @@ -986,9 +993,7 @@ Make sure to use the `fill` property. -
- Sidebar -
+
Sidebar
@@ -998,9 +1003,7 @@ Make sure to use the `fill` property.
-
- Bottom pane -
+
Bottom pane
@@ -1032,9 +1035,7 @@ Example: ```jsx - - Hello world! - + Hello world! Label @@ -1063,7 +1064,7 @@ A straight forward mapping to `
` element. - See inherited props: [Box](#box) - `collapsing: boolean` - Collapses table cell to the smallest possible size, -and stops any text inside from wrapping. + and stops any text inside from wrapping. ### `Tabs` @@ -1099,9 +1100,7 @@ Tabs also support a vertical configuration. This is usually paired with a ```jsx - - ... - + ... Tab content. @@ -1113,7 +1112,7 @@ Tabs also support a vertical configuration. This is usually paired with a - See inherited props: [Box](#box) - `vertical: boolean` - Use a vertical configuration, where tabs will be -stacked vertically. + stacked vertically. - `children: Tab[]` - This component only accepts tabs as its children. ### `Tabs.Tab` @@ -1125,8 +1124,8 @@ a lot of `Button` props. - See inherited props: [Button](#button) - `altSelection` - Whether the tab buttons select via standard select (color -change) or by adding a white indicator to the selected tab. -Intended for usage on interfaces where tab color has relevance. + change) or by adding a white indicator to the selected tab. + Intended for usage on interfaces where tab color has relevance. - `icon: string` - Tab icon. - `children: any` - Tab text. - `onClick: function` - Called when element is clicked. @@ -1143,9 +1142,7 @@ Usage: ```jsx - - Sample text. - + Sample text. ``` @@ -1153,7 +1150,7 @@ Usage: - `position?: string` - Tooltip position. See [`Popper`](#Popper) for valid options. Defaults to "auto". - `content: string` - Content of the tooltip. Must be a plain string. -Fragments or other elements are **not** supported. + Fragments or other elements are **not** supported. ## `tgui/layouts` @@ -1167,9 +1164,7 @@ Example: ```jsx - - Hello, world! - + Hello, world! ``` @@ -1184,9 +1179,9 @@ Example: - `height: number` - Window height. - `noClose: boolean` - Controls the ability to close the window. - `children: any` - Child elements, which are rendered directly inside the -window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI, -they should be put as direct childs of a Window, otherwise you should be -putting your content into [Window.Content](#windowcontent). + window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI, + they should be put as direct childs of a Window, otherwise you should be + putting your content into [Window.Content](#windowcontent). ### `Window.Content` diff --git a/tgui/global.d.ts b/tgui/global.d.ts index b99d358cf48..225d1a9dd39 100644 --- a/tgui/global.d.ts +++ b/tgui/global.d.ts @@ -143,6 +143,11 @@ type ByondType = { */ parseJson(text: string): any; + /** + * Downloads a blob, platform-agnostic + */ + saveBlob(blob: Blob, filename: string, ext: string): void; + /** * Sends a message to `/datum/tgui_window` which hosts this window instance. */ @@ -169,6 +174,11 @@ type ByondType = { * Loads a script into the document. */ loadJs(url: string): void; + + /** + * Maps icons to their ref + */ + iconRefMap: Record; }; /** diff --git a/tgui/packages/common/color.js b/tgui/packages/common/color.js deleted file mode 100644 index 913f50747af..00000000000 --- a/tgui/packages/common/color.js +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -const EPSILON = 0.0001; - -export class Color { - constructor(r = 0, g = 0, b = 0, a = 1) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } - - toString() { - return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; - } -} - -/** - * Creates a color from the CSS hex color notation. - */ -Color.fromHex = (hex) => - new Color( - parseInt(hex.substr(1, 2), 16), - parseInt(hex.substr(3, 2), 16), - parseInt(hex.substr(5, 2), 16) - ); - -/** - * Linear interpolation of two colors. - */ -Color.lerp = (c1, c2, n) => - new Color( - (c2.r - c1.r) * n + c1.r, - (c2.g - c1.g) * n + c1.g, - (c2.b - c1.b) * n + c1.b, - (c2.a - c1.a) * n + c1.a - ); - -/** - * Loops up the color in the provided list of colors - * with linear interpolation. - */ -Color.lookup = (value, colors = []) => { - const len = colors.length; - if (len < 2) { - throw new Error('Needs at least two colors!'); - } - const scaled = value * (len - 1); - if (value < EPSILON) { - return colors[0]; - } - if (value >= 1 - EPSILON) { - return colors[len - 1]; - } - const ratio = scaled % 1; - const index = scaled | 0; - return Color.lerp(colors[index], colors[index + 1], ratio); -}; diff --git a/tgui/packages/common/color.ts b/tgui/packages/common/color.ts new file mode 100644 index 00000000000..9022cccfdc2 --- /dev/null +++ b/tgui/packages/common/color.ts @@ -0,0 +1,359 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +const EPSILON = 0.0001; + +export class Color { + r: number; + g: number; + b: number; + a: number; + + constructor(r = 0, g = 0, b = 0, a = 1) { + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } + + toString() { + return `rgba(${this.r | 0}, ${this.g | 0}, ${this.b | 0}, ${this.a | 0})`; + } + + /** + * Creates a color from the CSS hex color notation. + */ + static fromHex(hex: string): Color { + return new Color( + parseInt(hex.substr(1, 2), 16), + parseInt(hex.substr(3, 2), 16), + parseInt(hex.substr(5, 2), 16) + ); + } + + /** + * Linear interpolation of two colors. + */ + static lerp(c1: Color, c2: Color, n: number): Color { + return new Color( + (c2.r - c1.r) * n + c1.r, + (c2.g - c1.g) * n + c1.g, + (c2.b - c1.b) * n + c1.b, + (c2.a - c1.a) * n + c1.a + ); + } + + /** + * Loops up the color in the provided list of colors + * with linear interpolation. + */ + static lookup(value: number, colors: Color[] = []): Color { + const len = colors.length; + if (len < 2) { + throw new Error('Needs at least two colors!'); + } + const scaled = value * (len - 1); + if (value < EPSILON) { + return colors[0]; + } + if (value >= 1 - EPSILON) { + return colors[len - 1]; + } + const ratio = scaled % 1; + const index = scaled | 0; + return Color.lerp(colors[index], colors[index + 1], ratio); + } +} + +/* + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +const round = ( + number: number, + digits = 0, + base = Math.pow(10, digits) +): number => { + return Math.round(base * number) / base; +}; + +export interface RgbColor { + r: number; + g: number; + b: number; +} + +export interface RgbaColor extends RgbColor { + a: number; +} + +export interface HslColor { + h: number; + s: number; + l: number; +} + +export interface HslaColor extends HslColor { + a: number; +} + +export interface HsvColor { + h: number; + s: number; + v: number; +} + +export interface HsvaColor extends HsvColor { + a: number; +} + +export type ObjectColor = + | RgbColor + | HslColor + | HsvColor + | RgbaColor + | HslaColor + | HsvaColor; + +export type AnyColor = string | ObjectColor; + +/** + * Valid CSS units. + * https://developer.mozilla.org/en-US/docs/Web/CSS/angle + */ +const angleUnits: Record = { + grad: 360 / 400, + turn: 360, + rad: 360 / (Math.PI * 2), +}; + +export const hexToHsva = (hex: string): HsvaColor => rgbaToHsva(hexToRgba(hex)); + +export const hexToRgba = (hex: string): RgbaColor => { + if (hex[0] === '#') hex = hex.substring(1); + + if (hex.length < 6) { + return { + r: parseInt(hex[0] + hex[0], 16), + g: parseInt(hex[1] + hex[1], 16), + b: parseInt(hex[2] + hex[2], 16), + a: hex.length === 4 ? round(parseInt(hex[3] + hex[3], 16) / 255, 2) : 1, + }; + } + + return { + r: parseInt(hex.substring(0, 2), 16), + g: parseInt(hex.substring(2, 4), 16), + b: parseInt(hex.substring(4, 6), 16), + a: hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 2) : 1, + }; +}; + +export const parseHue = (value: string, unit = 'deg'): number => { + return Number(value) * (angleUnits[unit] || 1); +}; + +export const hslaStringToHsva = (hslString: string): HsvaColor => { + const matcher = + /hsla?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hslString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return hslaToHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + l: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hslStringToHsva = hslaStringToHsva; + +export const hslaToHsva = ({ h, s, l, a }: HslaColor): HsvaColor => { + s *= (l < 50 ? l : 100 - l) / 100; + + return { + h: h, + s: s > 0 ? ((2 * s) / (l + s)) * 100 : 0, + v: l + s, + a, + }; +}; + +export const hsvaToHex = (hsva: HsvaColor): string => + rgbaToHex(hsvaToRgba(hsva)); + +export const hsvaToHsla = ({ h, s, v, a }: HsvaColor): HslaColor => { + const hh = ((200 - s) * v) / 100; + + return { + h: round(h), + s: round( + hh > 0 && hh < 200 + ? ((s * v) / 100 / (hh <= 100 ? hh : 200 - hh)) * 100 + : 0 + ), + l: round(hh / 2), + a: round(a, 2), + }; +}; + +export const hsvaToHslString = (hsva: HsvaColor): string => { + const { h, s, l } = hsvaToHsla(hsva); + return `hsl(${h}, ${s}%, ${l}%)`; +}; + +export const hsvaToHsvString = (hsva: HsvaColor): string => { + const { h, s, v } = roundHsva(hsva); + return `hsv(${h}, ${s}%, ${v}%)`; +}; + +export const hsvaToHsvaString = (hsva: HsvaColor): string => { + const { h, s, v, a } = roundHsva(hsva); + return `hsva(${h}, ${s}%, ${v}%, ${a})`; +}; + +export const hsvaToHslaString = (hsva: HsvaColor): string => { + const { h, s, l, a } = hsvaToHsla(hsva); + return `hsla(${h}, ${s}%, ${l}%, ${a})`; +}; + +export const hsvaToRgba = ({ h, s, v, a }: HsvaColor): RgbaColor => { + h = (h / 360) * 6; + s = s / 100; + v = v / 100; + + const hh = Math.floor(h), + b = v * (1 - s), + c = v * (1 - (h - hh) * s), + d = v * (1 - (1 - h + hh) * s), + module = hh % 6; + + return { + r: [v, c, b, b, d, v][module] * 255, + g: [d, v, v, c, b, b][module] * 255, + b: [b, b, d, v, v, c][module] * 255, + a: round(a, 2), + }; +}; + +export const hsvaToRgbString = (hsva: HsvaColor): string => { + const { r, g, b } = hsvaToRgba(hsva); + return `rgb(${round(r)}, ${round(g)}, ${round(b)})`; +}; + +export const hsvaToRgbaString = (hsva: HsvaColor): string => { + const { r, g, b, a } = hsvaToRgba(hsva); + return `rgba(${round(r)}, ${round(g)}, ${round(b)}, ${round(a, 2)})`; +}; + +export const hsvaStringToHsva = (hsvString: string): HsvaColor => { + const matcher = + /hsva?\(?\s*(-?\d*\.?\d+)(deg|rad|grad|turn)?[,\s]+(-?\d*\.?\d+)%?[,\s]+(-?\d*\.?\d+)%?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(hsvString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return roundHsva({ + h: parseHue(match[1], match[2]), + s: Number(match[3]), + v: Number(match[4]), + a: match[5] === undefined ? 1 : Number(match[5]) / (match[6] ? 100 : 1), + }); +}; + +export const hsvStringToHsva = hsvaStringToHsva; + +export const rgbaStringToHsva = (rgbaString: string): HsvaColor => { + const matcher = + /rgba?\(?\s*(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?[,\s]+(-?\d*\.?\d+)(%)?,?\s*[/\s]*(-?\d*\.?\d+)?(%)?\s*\)?/i; + const match = matcher.exec(rgbaString); + + if (!match) return { h: 0, s: 0, v: 0, a: 1 }; + + return rgbaToHsva({ + r: Number(match[1]) / (match[2] ? 100 / 255 : 1), + g: Number(match[3]) / (match[4] ? 100 / 255 : 1), + b: Number(match[5]) / (match[6] ? 100 / 255 : 1), + a: match[7] === undefined ? 1 : Number(match[7]) / (match[8] ? 100 : 1), + }); +}; + +export const rgbStringToHsva = rgbaStringToHsva; + +const format = (number: number) => { + const hex = number.toString(16); + return hex.length < 2 ? '0' + hex : hex; +}; + +export const rgbaToHex = ({ r, g, b, a }: RgbaColor): string => { + const alphaHex = a < 1 ? format(round(a * 255)) : ''; + return ( + '#' + format(round(r)) + format(round(g)) + format(round(b)) + alphaHex + ); +}; + +export const rgbaToHsva = ({ r, g, b, a }: RgbaColor): HsvaColor => { + const max = Math.max(r, g, b); + const delta = max - Math.min(r, g, b); + + // prettier-ignore + const hh = delta + ? max === r + ? (g - b) / delta + : max === g + ? 2 + (b - r) / delta + : 4 + (r - g) / delta + : 0; + + return { + h: 60 * (hh < 0 ? hh + 6 : hh), + s: max ? (delta / max) * 100 : 0, + v: (max / 255) * 100, + a, + }; +}; + +export const roundHsva = (hsva: HsvaColor): HsvaColor => ({ + h: round(hsva.h), + s: round(hsva.s), + v: round(hsva.v), + a: round(hsva.a, 2), +}); + +export const rgbaToRgb = ({ r, g, b }: RgbaColor): RgbColor => ({ r, g, b }); + +export const hslaToHsl = ({ h, s, l }: HslaColor): HslColor => ({ h, s, l }); + +export const hsvaToHsv = (hsva: HsvaColor): HsvColor => { + const { h, s, v } = roundHsva(hsva); + return { h, s, v }; +}; + +const hexMatcher = /^#?([0-9A-F]{3,8})$/i; + +export const validHex = (value: string, alpha?: boolean): boolean => { + const match = hexMatcher.exec(value); + const length = match ? match[1].length : 0; + + return ( + length === 3 || // '#rgb' format + length === 6 || // '#rrggbb' format + (!!alpha && length === 4) || // '#rgba' format + (!!alpha && length === 8) // '#rrggbbaa' format + ); +}; diff --git a/tgui/packages/tgui-panel/chat/renderer.js b/tgui/packages/tgui-panel/chat/renderer.js index f3d1a43b496..5447222f188 100644 --- a/tgui/packages/tgui-panel/chat/renderer.js +++ b/tgui/packages/tgui-panel/chat/renderer.js @@ -555,13 +555,13 @@ class ChatRenderer { + '\n' + '\n'; // Create and send a nice blob - const blob = new Blob([pageHtml]); + const blob = new Blob([pageHtml], { type: 'text/plain' }); const timestamp = new Date() .toISOString() .substring(0, 19) .replace(/[-:]/g, '') .replace('T', '-'); - window.navigator.msSaveBlob(blob, `ss13-chatlog-${timestamp}.html`); + Byond.saveBlob(blob, `ss13-paradise-chatlog-${timestamp}.html`, '.html'); } } diff --git a/tgui/packages/tgui-panel/index.js b/tgui/packages/tgui-panel/index.js index 0cb89a160f0..51932a3f3eb 100644 --- a/tgui/packages/tgui-panel/index.js +++ b/tgui/packages/tgui-panel/index.js @@ -77,18 +77,9 @@ const setupApp = () => { // Dispatch incoming messages as store actions Byond.subscribe((type, payload) => store.dispatch({ type, payload })); - // Hide output - Byond.winset('output', { - 'is-visible': false, - 'is-disabled': true, - }); - // Unhide the panel - Byond.winset('chat_panel', { - 'is-visible': true, - 'is-disabled': false, - 'pos': '0x0', - 'size': '0x0', + Byond.winset('legacy_output_selector', { + left: 'output_browser', }); // Resize the panel to match the non-browser output diff --git a/tgui/packages/tgui/components/ByondUi.js b/tgui/packages/tgui/components/ByondUi.js index ca974f69910..5e39410743a 100644 --- a/tgui/packages/tgui/components/ByondUi.js +++ b/tgui/packages/tgui/components/ByondUi.js @@ -25,6 +25,16 @@ const createByondUiElement = (elementId) => { // Return a control structure return { render: (params) => { + /** + * Note: We unmount and render because there is currently unfixable bug with + * how cameras are rendered on first occurence. That came with TGUI 4 and + * I have no idea how to fix this correctly. + */ + logger.log(`unmounting '${id}'`); + byondUiStack[index] = null; + Byond.winset(id, { + parent: '', + }); logger.log(`rendering '${id}'`); byondUiStack[index] = id; Byond.winset(id, params); @@ -54,13 +64,17 @@ window.addEventListener('beforeunload', () => { }); /** - * Get the bounding box of the DOM element. + * Get the bounding box of the DOM element in display-pixels. */ const getBoundingBox = (element) => { + const pixelRatio = window.devicePixelRatio ?? 1; const rect = element.getBoundingClientRect(); return { - pos: [rect.left, rect.top], - size: [rect.right - rect.left, rect.bottom - rect.top], + pos: [rect.left * pixelRatio, rect.top * pixelRatio], + size: [ + (rect.right - rect.left) * pixelRatio, + (rect.bottom - rect.top) * pixelRatio, + ], }; }; diff --git a/tgui/packages/tgui/components/DmIcon.tsx b/tgui/packages/tgui/components/DmIcon.tsx new file mode 100644 index 00000000000..c77dc8a8ff9 --- /dev/null +++ b/tgui/packages/tgui/components/DmIcon.tsx @@ -0,0 +1,92 @@ +import { Component, InfernoNode } from 'inferno'; +import { resolveAsset } from '../assets'; +import { fetchRetry } from '../http'; +import { BoxProps } from './Box'; +import { Image } from './Image'; + +enum Direction { + NORTH = 1, + SOUTH = 2, + EAST = 4, + WEST = 8, + NORTHEAST = NORTH | EAST, + NORTHWEST = NORTH | WEST, + SOUTHEAST = SOUTH | EAST, + SOUTHWEST = SOUTH | WEST, +} + +type Props = { + /** Required: The path of the icon */ + icon: string; + /** Required: The state of the icon */ + icon_state: string; +} & Partial<{ + /** Facing direction. See direction enum. Default is South */ + direction: Direction; + /** Fallback icon. */ + fallback: InfernoNode; + /** Frame number. Default is 1 */ + frame: number; + /** Movement state. Default is false */ + movement: any; +}> & + BoxProps; + +let refMap: Record | undefined; + +export class DmIcon extends Component { + constructor(props: Props) { + super(props); + this.state = { + iconRef: '', + }; + } + + async fetchRefMap() { + try { + const response = await fetchRetry(resolveAsset('icon_ref_map.json')); + const data = await response.json(); + refMap = data; + this.setState({ iconRef: data[this.props.icon] || '' }); + } catch (err) { + return; + } + } + + componentDidMount() { + if (!refMap) { + this.fetchRefMap(); + } else { + this.setState({ iconRef: refMap[this.props.icon] }); + } + } + + componentDidUpdate(prevProps: Props) { + if (prevProps.icon !== this.props.icon) { + if (refMap) { + this.setState({ iconRef: refMap[this.props.icon] }); + } else { + this.fetchRefMap(); + } + } + } + + render() { + const { + className, + direction = Direction.SOUTH, + fallback, + frame = 1, + icon_state, + movement = false, + ...rest + } = this.props; + const { iconRef } = this.state; + + const query = `${iconRef}?state=${icon_state}&dir=${direction}&movement=${!!movement}&frame=${frame}`; + + if (!iconRef) return fallback || null; + + return ; + } +} diff --git a/tgui/packages/tgui/components/Image.tsx b/tgui/packages/tgui/components/Image.tsx new file mode 100644 index 00000000000..40730da594d --- /dev/null +++ b/tgui/packages/tgui/components/Image.tsx @@ -0,0 +1,70 @@ +import { Component } from 'inferno'; +import { BoxProps, computeBoxProps } from './Box'; + +type Props = Partial<{ + /** True is default, this fixes an ie thing */ + fixBlur: boolean; + /** False by default. Good if you're fetching images on UIs that do not auto update. This will attempt to fix the 'x' icon 5 times. */ + fixErrors: boolean; + /** Fill is default. */ + objectFit: 'contain' | 'cover'; +}> & + IconUnion & + BoxProps; + +// at least one of these is required +type IconUnion = + | { + className?: string; + src: string; + } + | { + className: string; + src?: string; + }; + +const maxAttempts = 5; + +/** Image component. Use this instead of Box as="img". */ +export class Image extends Component { + attempts: number = 0; + + handleError = (event) => { + const { fixErrors, src } = this.props; + if (fixErrors && this.attempts < maxAttempts) { + const imgElement = event.currentTarget; + + setTimeout(() => { + imgElement.src = `${src}?attempt=${this.attempts}`; + this.attempts++; + }, 1000); + } + }; + + render() { + const { + fixBlur = true, + fixErrors = false, + objectFit = 'fill', + src, + ...rest + } = this.props; + + /* Remove -ms-interpolation-mode with Byond 516. -webkit-optimize-contrast is better than pixelated */ + const computedProps = computeBoxProps({ + style: { + '-ms-interpolation-mode': `${fixBlur ? 'nearest-neighbor' : 'auto'}`, + 'image-rendering': `${fixBlur ? 'pixelated' : 'auto'}`, + 'object-fit': `${objectFit}`, + }, + ...rest, + }); + + /* Use div instead img if used asset, cause img with class leaves white border on 516 */ + if (computedProps.className) { + return
; + } + + return ; + } +} diff --git a/tgui/packages/tgui/components/ImageButtonTS.tsx b/tgui/packages/tgui/components/ImageButtonTS.tsx new file mode 100644 index 00000000000..565f31a2d58 --- /dev/null +++ b/tgui/packages/tgui/components/ImageButtonTS.tsx @@ -0,0 +1,243 @@ +/** + * @file + * @copyright 2024 Aylong (https://github.com/AyIong) + * @license MIT + */ + +import { Placement } from '@popperjs/core'; + +import { InfernoNode } from 'inferno'; +import { BooleanLike, classes } from 'common/react'; +import { BoxProps, computeBoxProps } from './Box'; +import { Icon } from './Icon'; +import { Image } from './Image'; +import { DmIcon } from './DmIcon'; +import { Stack } from './Stack'; +import { Tooltip } from './Tooltip'; + +type Props = Partial<{ + /** Asset cache. Example: `asset={`assetname32x32, ${thing.key}`}` */ + asset: string[]; + /** Classic way to put images. Example: `base64={thing.image}` */ + base64: string; + /** + * Special container for buttons. + * You can put any other component here. + * Has some special stylings! + * Example: `buttons={}` + */ + buttons: InfernoNode; + /** + * Same as buttons, but. Have disabled pointer-events on content inside if non-fluid. + * Fluid version have humburger layout. + */ + buttonsAlt: InfernoNode; + /** Content under image. Or on the right if fluid. */ + children: InfernoNode; + /** Applies a CSS class to the element. */ + className: string; + /** Color of the button. See [Button](#button) but without `transparent`. */ + color: string; + /** Makes button disabled and dark red if true. Also disables onClick. */ + disabled: BooleanLike; + /** Optional. Adds a "stub" when loading DmIcon. */ + dmFallback: InfernoNode; + /** Parameter `icon` of component `DmIcon`. */ + dmIcon: string | null; + /** Parameter `icon_state` of component `DmIcon`. */ + dmIconState: string | null; + /** Parameter `direction` of component `DmIcon`. */ + dmDirection: number | null; + /** + * Changes the layout of the button, making it fill the entire horizontally available space. + * Allows the use of `title` + */ + fluid: boolean; + /** Parameter responsible for the size of the image, component and standard "stubs". */ + imageSize: number; + /** Prop `src` of . Example: `imageSrc={resolveAsset(thing.image}` */ + imageSrc: string; + /** Called when button is clicked with LMB. */ + onClick: (e: any) => void; + /** Called when button is clicked with RMB. */ + onRightClick: (e: any) => void; + /** Makes button selected and green if true. */ + selected: BooleanLike; + /** Requires `fluid` for work. Bold text with divider betwen content. */ + title: string; + /** A fancy, boxy tooltip, which appears when hovering over the button */ + tooltip: InfernoNode; + /** Position of the tooltip. See [`Popper`](#Popper) for valid options. */ + tooltipPosition: Placement; +}> & + BoxProps; + +export const ImageButtonTS = (props: Props) => { + const { + asset, + base64, + buttons, + buttonsAlt, + children, + className, + color, + disabled, + dmFallback, + dmDirection, + dmIcon, + dmIconState, + fluid, + imageSize = 64, + imageSrc, + onClick, + onRightClick, + selected, + title, + tooltip, + tooltipPosition, + ...rest + } = props; + + const getFallback = (iconName: string, iconSpin: boolean) => { + return ( + + + + + + ); + }; + + let buttonContent = ( +
{ + if (!disabled && onClick) { + onClick(event); + } + }} + onContextMenu={(event) => { + event.preventDefault(); + if (!disabled && onRightClick) { + onRightClick(event); + } + }} + style={{ width: !fluid ? `calc(${imageSize}px + 0.5em + 2px)` : 'auto' }} + > +
+ {base64 || asset || imageSrc ? ( + + ) : dmIcon && dmIconState ? ( + + ) : ( + getFallback('question', false) + )} +
+ {fluid ? ( +
+ {title && ( + + {title} + + )} + {children && ( + {children} + )} +
+ ) : ( + children && ( + + {children} + + ) + )} +
+ ); + + if (tooltip) { + buttonContent = ( + + {buttonContent} + + ); + } + + return ( +
+ {buttonContent} + {buttons && ( +
+ {buttons} +
+ )} + {buttonsAlt && ( +
+ {buttonsAlt} +
+ )} +
+ ); +}; diff --git a/tgui/packages/tgui/components/Interactive.tsx b/tgui/packages/tgui/components/Interactive.tsx new file mode 100644 index 00000000000..f0a0dfe1389 --- /dev/null +++ b/tgui/packages/tgui/components/Interactive.tsx @@ -0,0 +1,153 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { clamp } from 'common/math'; +import { Component, InfernoNode, createRef, RefObject } from 'inferno'; + +export interface Interaction { + left: number; + top: number; +} + +// Finds the proper window object to fix iframe embedding issues +const getParentWindow = (node?: HTMLDivElement | null): Window => { + return (node && node.ownerDocument.defaultView) || self; +}; + +// Returns a relative position of the pointer inside the node's bounding box +const getRelativePosition = ( + node: HTMLDivElement, + event: MouseEvent +): Interaction => { + const rect = node.getBoundingClientRect(); + const pointer = event as MouseEvent; + return { + left: clamp( + (pointer.pageX - (rect.left + getParentWindow(node).pageXOffset)) / + rect.width, + 0, + 1 + ), + top: clamp( + (pointer.pageY - (rect.top + getParentWindow(node).pageYOffset)) / + rect.height, + 0, + 1 + ), + }; +}; + +export interface InteractiveProps { + onMove: (interaction: Interaction) => void; + onKey: (offset: Interaction) => void; + children: InfernoNode[]; + style?: any; +} + +export class Interactive extends Component { + containerRef: RefObject; + props: InteractiveProps; + + constructor(props: InteractiveProps) { + super(); + this.props = props; + this.containerRef = createRef(); + } + + handleMoveStart = (event: MouseEvent) => { + const el = this.containerRef?.current; + if (!el) return; + + // Prevent text selection + event.preventDefault(); + el.focus(); + this.props.onMove(getRelativePosition(el, event)); + this.toggleDocumentEvents(true); + }; + + handleMove = (event: MouseEvent) => { + // Prevent text selection + event.preventDefault(); + + // If user moves the pointer outside of the window or iframe bounds and release it there, + // `mouseup`/`touchend` won't be fired. In order to stop the picker from following the cursor + // after the user has moved the mouse/finger back to the document, we check `event.buttons` + // and `event.touches`. It allows us to detect that the user is just moving his pointer + // without pressing it down + const isDown = event.buttons > 0; + + if (isDown && this.containerRef?.current) { + this.props.onMove(getRelativePosition(this.containerRef.current, event)); + } else { + this.toggleDocumentEvents(false); + } + }; + + handleMoveEnd = () => { + this.toggleDocumentEvents(false); + }; + + handleKeyDown = (event: KeyboardEvent) => { + const keyCode = event.which || event.keyCode; + + // Ignore all keys except arrow ones + if (keyCode < 37 || keyCode > 40) return; + // Do not scroll page by arrow keys when document is focused on the element + event.preventDefault(); + // Send relative offset to the parent component. + // We use codes (37←, 38↑, 39→, 40↓) instead of keys ('ArrowRight', 'ArrowDown', etc) + // to reduce the size of the library + this.props.onKey({ + left: keyCode === 39 ? 0.05 : keyCode === 37 ? -0.05 : 0, + top: keyCode === 40 ? 0.05 : keyCode === 38 ? -0.05 : 0, + }); + }; + + toggleDocumentEvents(state?: boolean) { + const el = this.containerRef?.current; + const parentWindow = getParentWindow(el); + + // Add or remove additional pointer event listeners + const toggleEvent = state + ? parentWindow.addEventListener + : parentWindow.removeEventListener; + toggleEvent('mousemove', this.handleMove); + toggleEvent('mouseup', this.handleMoveEnd); + } + + componentDidMount() { + this.toggleDocumentEvents(true); + } + + componentWillUnmount() { + this.toggleDocumentEvents(false); + } + + render() { + return ( +
+ {this.props.children} +
+ ); + } +} diff --git a/tgui/packages/tgui/components/Pointer.tsx b/tgui/packages/tgui/components/Pointer.tsx new file mode 100644 index 00000000000..409972a7dfc --- /dev/null +++ b/tgui/packages/tgui/components/Pointer.tsx @@ -0,0 +1,46 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { classes } from 'common/react'; +import { InfernoNode } from 'inferno'; + +interface PointerProps { + className?: string; + top?: number; + left: number; + color: string; +} + +export const Pointer = ({ + className, + color, + left, + top = 0.5, +}: PointerProps): InfernoNode => { + const nodeClassName = classes(['react-colorful__pointer', className]); + + const style = { + top: `${top * 100}%`, + left: `${left * 100}%`, + }; + + return ( +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 8876792dc99..1e300e75d4a 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -17,12 +17,16 @@ export { ColorBox } from './ColorBox'; export { Countdown } from './Countdown'; export { Dimmer } from './Dimmer'; export { Divider } from './Divider'; +export { DmIcon } from './DmIcon'; export { DraggableControl } from './DraggableControl'; export { Dropdown } from './Dropdown'; export { Flex } from './Flex'; export { Grid } from './Grid'; +export { Image } from './Image'; +export { Interactive } from './Interactive'; export { Icon } from './Icon'; export { ImageButton } from './ImageButton'; +export { ImageButtonTS } from './ImageButtonTS'; export { Input } from './Input'; export { Knob } from './Knob'; export { LabeledControls } from './LabeledControls'; @@ -31,6 +35,7 @@ export { Modal } from './Modal'; export { NanoMap } from './NanoMap'; export { NoticeBox } from './NoticeBox'; export { NumberInput } from './NumberInput'; +export { Pointer } from './Pointer'; export { Popper } from './Popper'; export { ProgressBar } from './ProgressBar'; export { RestrictedInput } from './RestrictedInput'; diff --git a/tgui/packages/tgui/drag.js b/tgui/packages/tgui/drag.js index 9ffa8bb952d..6b1766dbf52 100644 --- a/tgui/packages/tgui/drag.js +++ b/tgui/packages/tgui/drag.js @@ -5,10 +5,11 @@ */ import { storage } from 'common/storage'; -import { vecAdd, vecInverse, vecMultiply, vecScale } from 'common/vector'; +import { vecAdd, vecSubtract, vecMultiply, vecScale } from 'common/vector'; import { createLogger } from './logging'; const logger = createLogger('drag'); +const pixelRatio = window.devicePixelRatio ?? 1; let windowKey = Byond.windowId; let dragging = false; @@ -24,31 +25,34 @@ export const setWindowKey = (key) => { windowKey = key; }; -export const getWindowPosition = () => [window.screenLeft, window.screenTop]; +const getWindowPosition = () => [ + window.screenLeft * pixelRatio, + window.screenTop * pixelRatio, +]; -export const getWindowSize = () => [window.innerWidth, window.innerHeight]; +const getWindowSize = () => [ + window.innerWidth * pixelRatio, + window.innerHeight * pixelRatio, +]; -export const setWindowPosition = (vec) => { +const setWindowPosition = (vec) => { const byondPos = vecAdd(vec, screenOffset); return Byond.winset(Byond.windowId, { pos: byondPos[0] + ',' + byondPos[1], }); }; -export const setWindowSize = (vec) => { +const setWindowSize = (vec) => { return Byond.winset(Byond.windowId, { size: vec[0] + 'x' + vec[1], }); }; -export const getScreenPosition = () => [ - 0 - screenOffset[0], - 0 - screenOffset[1], -]; +const getScreenPosition = () => [0 - screenOffset[0], 0 - screenOffset[1]]; -export const getScreenSize = () => [ - window.screen.availWidth, - window.screen.availHeight, +const getScreenSize = () => [ + window.screen.availWidth * pixelRatio, + window.screen.availHeight * pixelRatio, ]; /** @@ -76,7 +80,7 @@ const touchRecents = (recents, touchedItem, limit = 50) => { return [nextRecents, trimmedItem]; }; -export const storeWindowGeometry = async () => { +const storeWindowGeometry = async () => { logger.log('storing geometry'); const geometry = { pos: getWindowPosition(), @@ -100,11 +104,16 @@ export const recallWindowGeometry = async (options = {}) => { if (geometry) { logger.log('recalled geometry:', geometry); } + // options.pos is assumed to already be in display-pixels let pos = geometry?.pos || options.pos; let size = options.size; + // Convert size from css-pixels to display-pixels + if (size) { + size = [size[0] * pixelRatio, size[1] * pixelRatio]; + } // Wait until screen offset gets resolved await screenOffsetPromise; - const areaAvailable = [window.screen.availWidth, window.screen.availHeight]; + const areaAvailable = getScreenSize(); // Set window size if (size) { // Constraint size to not exceed available screen area. @@ -135,9 +144,11 @@ export const recallWindowGeometry = async (options = {}) => { export const setupDrag = async () => { // Calculate screen offset caused by the windows taskbar + let windowPosition = getWindowPosition(); + screenOffsetPromise = Byond.winget(Byond.windowId, 'pos').then((pos) => [ - pos.x - window.screenLeft, - pos.y - window.screenTop, + pos.x - windowPosition[0], + pos.y - windowPosition[1], ]); screenOffset = await screenOffsetPromise; logger.debug('screen offset', screenOffset); @@ -169,10 +180,11 @@ const constraintPosition = (pos, size) => { export const dragStartHandler = (event) => { logger.log('drag start'); dragging = true; - dragPointOffset = [ - window.screenLeft - event.screenX, - window.screenTop - event.screenY, - ]; + let windowPosition = getWindowPosition(); + dragPointOffset = vecSubtract( + [event.screenX, event.screenY], + getWindowPosition() + ); document.addEventListener('mousemove', dragMoveHandler); document.addEventListener('mouseup', dragEndHandler); dragMoveHandler(event); @@ -192,18 +204,20 @@ const dragMoveHandler = (event) => { return; } event.preventDefault(); - setWindowPosition(vecAdd([event.screenX, event.screenY], dragPointOffset)); + setWindowPosition( + vecSubtract([event.screenX, event.screenY], dragPointOffset) + ); }; export const resizeStartHandler = (x, y) => (event) => { resizeMatrix = [x, y]; logger.log('resize start', resizeMatrix); resizing = true; - dragPointOffset = [ - window.screenLeft - event.screenX, - window.screenTop - event.screenY, - ]; - initialSize = [window.innerWidth, window.innerHeight]; + dragPointOffset = vecSubtract( + [event.screenX, event.screenY], + getWindowPosition() + ); + initialSize = getWindowSize(); document.addEventListener('mousemove', resizeMoveHandler); document.addEventListener('mouseup', resizeEndHandler); resizeMoveHandler(event); @@ -223,20 +237,15 @@ const resizeMoveHandler = (event) => { return; } event.preventDefault(); - size = vecAdd( - initialSize, - vecMultiply( - resizeMatrix, - vecAdd( - [event.screenX, event.screenY], - vecInverse([window.screenLeft, window.screenTop]), - dragPointOffset, - [1, 1] - ) - ) + const currentOffset = vecSubtract( + [event.screenX, event.screenY], + getWindowPosition() ); + const delta = vecSubtract(currentOffset, dragPointOffset); + // Extra 1x1 area is added to ensure the browser can see the cursor + size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]); // Sane window size values - size[0] = Math.max(size[0], 150); - size[1] = Math.max(size[1], 50); + size[0] = Math.max(size[0], 150 * pixelRatio); + size[1] = Math.max(size[1], 50 * pixelRatio); setWindowSize(size); }; diff --git a/tgui/packages/tgui/http.ts b/tgui/packages/tgui/http.ts new file mode 100644 index 00000000000..a0ea97c3b63 --- /dev/null +++ b/tgui/packages/tgui/http.ts @@ -0,0 +1,16 @@ +/** + * An equivalent to `fetch`, except will automatically retry. + */ +export const fetchRetry = ( + url: string, + options?: RequestInit, + retryTimer: number = 1000 +): Promise => { + return fetch(url, options).catch(() => { + return new Promise((resolve) => { + setTimeout(() => { + fetchRetry(url, options, retryTimer).then(resolve); + }, retryTimer); + }); + }); +}; diff --git a/tgui/packages/tgui/icons.ts b/tgui/packages/tgui/icons.ts new file mode 100644 index 00000000000..fb53a610f17 --- /dev/null +++ b/tgui/packages/tgui/icons.ts @@ -0,0 +1,14 @@ +import { resolveAsset } from './assets'; +import { fetchRetry } from './http'; +import { logger } from './logging'; + +export const loadIconRefMap = function () { + if (Object.keys(Byond.iconRefMap).length > 0) { + return; + } + + fetchRetry(resolveAsset('icon_ref_map.json')) + .then((res) => res.json()) + .then((data) => (Byond.iconRefMap = data)) + .catch((error) => logger.log(error)); +}; diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index ed7d20d819c..8996dc5ee75 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -36,6 +36,7 @@ import './styles/themes/ntOS95.scss'; import { perf } from 'common/perf'; import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; import { setupHotKeys } from './hotkeys'; +import { loadIconRefMap } from './icons'; import { captureExternalLinks } from './links'; import { createRenderer } from './renderer'; import { configureStore, StoreProvider } from './store'; @@ -47,6 +48,8 @@ perf.mark('init'); const store = configureStore(); const renderApp = createRenderer(() => { + loadIconRefMap(); + const { getRoutedComponent } = require('./routes'); const Component = getRoutedComponent(store); return ( diff --git a/tgui/packages/tgui/interfaces/Autolathe.js b/tgui/packages/tgui/interfaces/Autolathe.js index 28dedb611f8..1a936dfe5f7 100644 --- a/tgui/packages/tgui/interfaces/Autolathe.js +++ b/tgui/packages/tgui/interfaces/Autolathe.js @@ -9,6 +9,7 @@ import { Section, Stack, Dropdown, + DmIcon, } from '../components'; import { Window } from '../layouts'; import { createSearch, toTitleCase } from 'common/string'; @@ -129,8 +130,9 @@ export const Autolathe = (props, context) => { /> {recipesToShow.map((recipe) => ( - { + const { act, data } = useBackend(context); + const { delays, rev_delays } = data; + const { delay_list, reverse = false } = props; + + return ( + + {delay_list.map((delay, i) => ( + + 10 + ? 'orange' + : 'default' + } + format={(value) => toFixed(value, 2)} + maxValue={10} + minValue={0} + inline + onDrag={(e, value) => { + act('editTiming', { + reverse: reverse, + timer: '' + (i + 1), + value: Math.max(value, 0), + }); + }} + size={1} + step={0.02} + unclamped + unit="s" + value={(reverse ? rev_delays[i + 1] : delays[i + 1]) / 10} + /> + + ))} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodBays.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodBays.tsx new file mode 100644 index 00000000000..df22de0451d --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodBays.tsx @@ -0,0 +1,52 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { BAYS } from './constants'; +import { PodLauncherData } from './types'; + +export const PodBays = (props, context) => { + const { act, data } = useBackend(context); + const { bayNumber } = data; + + return ( +
+ + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodLaunch.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodLaunch.tsx new file mode 100644 index 00000000000..23221ae6523 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodLaunch.tsx @@ -0,0 +1,28 @@ +import { useBackend } from '../../backend'; +import { Box, Button } from '../../components'; +import { useCompact } from './hooks'; +import { PodLauncherData } from './types'; + +export const PodLaunch = (props, context) => { + const { act, data } = useBackend(context); + const { giveLauncher } = data; + + const [compact] = useCompact(context); + + return ( + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodSounds.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodSounds.tsx new file mode 100644 index 00000000000..1c290dd306d --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodSounds.tsx @@ -0,0 +1,40 @@ +import { useBackend } from '../../backend'; +import { Button, Section } from '../../components'; +import { SOUNDS } from './constants'; +import { PodLauncherData } from './types'; + +export const PodSounds = (props, context) => { + const { act, data } = useBackend(context); + const { defaultSoundVolume, soundVolume } = data; + + return ( +
act('soundVolume')} + selected={soundVolume !== defaultSoundVolume} + tooltip={ + ` + Громкость Зука:` + soundVolume + } + /> + } + fill + title="Звуки" + > + {SOUNDS.map((sound, i) => ( + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodStatusPage.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodStatusPage.tsx new file mode 100644 index 00000000000..2b65f8337ad --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/PodStatusPage.tsx @@ -0,0 +1,132 @@ +import { Fragment } from 'inferno'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section, Stack } from '../../components'; +import { EFFECTS_ALL, POD_GREY } from './constants'; +import { useCompact } from './hooks'; +import { PodEffect, PodLauncherData } from './types'; + +export const PodStatusPage = (props, context) => { + const [compact] = useCompact(context); + + return ( +
+ + {EFFECTS_ALL.map((effectType, typeIdx) => ( + + + + {!compact && (effectType.alt_label || effectType.label)}: + + + {effectType.list.map((effect, effectIdx) => ( + 1} + index={effectIdx} + key={effectIdx} + /> + ))} + + + {typeIdx < EFFECTS_ALL.length && } + {typeIdx === EFFECTS_ALL.length - 1 && } + + ))} + +
+ ); +}; + +const EffectDisplay = (props, context) => { + const { effect, hasMargin, index } = props; + const { act, data } = useBackend(context); + const { effectShrapnel, payload, shrapnelMagnitude, shrapnelType } = data; + + if (effect.divider || !('icon' in effect)) { + return ( + + | + + ); + } + + return ( + + ); +}; + +const Extras = (props, context) => { + const { act } = useBackend(context); + const [compact, setCompact] = useCompact(context); + + return ( + + + Экстра: + + + + ))} + getPresets()}> +
+
+ ПРИМЕЧАНИЕ. Пользовательские звуки, не входящие в файлы базовой игры, не + сохраняются! :( +
+ + ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/ReverseMenu.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/ReverseMenu.tsx new file mode 100644 index 00000000000..9c339f2b456 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/ReverseMenu.tsx @@ -0,0 +1,99 @@ +import { useBackend } from '../../backend'; +import { Button, Section, Stack } from '../../components'; +import { REVERSE_OPTIONS } from './constants'; +import { useTab } from './hooks'; +import { PodLauncherData } from './types'; + +export const ReverseMenu = (props, context) => { + const { act, data } = useBackend(context); + const { + customDropoff, + effectReverse, + picking_dropoff_turf, + reverse_option_list, + } = data; + + const [tab, setTab] = useTab(context); + + return ( +
{ + act('effectReverse'); + if (tab === 2) { + setTab(1); + act('tabSwitch', { tabIndex: 1 }); + } + }} + selected={effectReverse} + tooltip={` + Не отправляет товары. + После приземления возвращается в + стартовый турф (или ангар + если ничего не указано).`} + /> + } + fill + title="Возвращаемый" + > + {!!effectReverse && ( + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/StylePage.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/StylePage.tsx new file mode 100644 index 00000000000..e18727cb6d8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/StylePage.tsx @@ -0,0 +1,65 @@ +import { classes } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Box, Button, Section } from '../../components'; +import { PodLauncherData } from './types'; + +export const StylePage = (props, context) => { + const { act, data } = useBackend(context); + const { effectName, styleChoice, podStyles } = data; + + return ( +
act('effectName')} + selected={effectName} + tooltip={` + Edit pod's + .id/desc.`} + tooltipPosition="bottom-start" + > + Имя + + } + fill + scrollable + title="Стиль" + > + {podStyles.map((page, i) => ( + + ))} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/Tabs.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/Tabs.tsx new file mode 100644 index 00000000000..56ce27cefda --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/Tabs.tsx @@ -0,0 +1,87 @@ +import { useBackend, useLocalState } from '../../backend'; +import { Box, Button } from '../../components'; +import { PodLauncherData } from './types'; + +export const TabPod = (props, context) => { + const { act, data } = useBackend(context); + const { oldArea } = data; + + return ( + <> + + + + ); +}; + +export const TabBay = (props, context) => { + const { act, data } = useBackend(context); + const [teleported, setTeleported] = useLocalState( + context, + 'teleported', + false + ); + const { oldArea } = data; + + return ( + <> + + + + ); +}; + +export const TabDrop = (props, context) => { + const { act, data } = useBackend(context); + const [teleported, setTeleported] = useLocalState( + context, + 'teleported', + false + ); + const { oldArea } = data; + + return ( + <> + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/Timing.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/Timing.tsx new file mode 100644 index 00000000000..c7ae48b882e --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/Timing.tsx @@ -0,0 +1,47 @@ +import { useBackend } from '../../backend'; +import { Button, Divider, Section } from '../../components'; +import { DELAYS, REV_DELAYS } from './constants'; +import { DelayHelper } from './DelayHelper'; +import { PodLauncherData } from './types'; + +export const Timing = (props, context) => { + const { act, data } = useBackend(context); + const { custom_rev_delay, effectReverse } = data; + + return ( +
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/ViewTabHolder.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/ViewTabHolder.tsx new file mode 100644 index 00000000000..1732f3b7ff6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/ViewTabHolder.tsx @@ -0,0 +1,100 @@ +import { useBackend } from '../../backend'; +import { Button, ByondUi, Section, Stack } from '../../components'; +import { POD_GREY, TABPAGES } from './constants'; +import { useTab } from './hooks'; +import { PodLauncherData } from './types'; + +export const ViewTabHolder = (props, context) => { + const { act, data } = useBackend(context); + const { mapRef, customDropoff, effectReverse, renderLighting } = data; + + const [tab, setTab] = useTab(context); + + const TabPageComponent = TABPAGES[tab].component; + + return ( +
+ {!!customDropoff && !!effectReverse && ( +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/constants.ts b/tgui/packages/tgui/interfaces/CentcomPodLauncher/constants.ts new file mode 100644 index 00000000000..0b01e1b3e2c --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/constants.ts @@ -0,0 +1,314 @@ +import { Placement } from '@popperjs/core'; + +import { TabBay, TabDrop, TabPod } from './Tabs'; +import { PodDelay, PodEffect } from './types'; + +export const POD_GREY = { + color: 'grey', +} as const; + +export const TABPAGES = [ + { + title: 'Просмотр капсулы', + component: TabPod, + }, + { + title: 'Просмотр ангара', + component: TabBay, + }, + { + title: 'Просмотр места выгрузки.', + component: TabDrop, + }, +] as const; + +type Option = { + title: string; + key?: string; + icon?: string; +}; + +export const REVERSE_OPTIONS: Option[] = [ + { + title: 'Мобы', + key: 'Mobs', + icon: 'user', + }, + { + title: 'Не закреплённые\nОбъекты', + key: 'Unanchored', + icon: 'cube', + }, + { + title: 'Закреплённые\nОбъекты', + key: 'Anchored', + icon: 'anchor', + }, + { + title: 'Мехи', + key: 'Mecha', + icon: 'truck', + }, +]; + +export const DELAYS: PodDelay[] = [ + { + title: 'Pre', + tooltip: 'Время до прибытия капсулы на станцию', + }, + { + title: 'Fall', + tooltip: 'Продолжительность анимации\n падения капсул', + }, + { + title: 'Open', + tooltip: 'Время, необходимое капсуле для открытия после приземления', + }, + { + title: 'Exit', + tooltip: 'Время до отлета капсулы\nпосле открытия', + }, +]; + +export const REV_DELAYS: PodDelay[] = [ + { + title: 'Pre', + tooltip: 'Время до появления капсулы над точкой высадки', + }, + { + title: 'Fall', + tooltip: 'Продолжительность анимации\n падения капсул', + }, + { + title: 'Open', + tooltip: 'Время, необходимое капсуле для открытия после приземления', + }, + { + title: 'Exit', + tooltip: 'Время до отлета капсулы\nпосле открытия', + }, +]; + +export const SOUNDS = [ + { + title: 'Fall', + act: 'fallingSound', + tooltip: + 'Воспроизводится, пока капсула падает, и заканчивается\nкогда капсула приземляется', + }, + { + title: 'Land', + act: 'landingSound', + tooltip: 'Воспроизводится после приземления капсулы', + }, + { + title: 'Open', + act: 'openingSound', + tooltip: 'Воспроизводится при открытии капсулы', + }, + { + title: 'Exit', + act: 'leavingSound', + tooltip: 'Воспроизводится, когда капсула улетает', + }, +]; + +export const BAYS = [ + { title: '1' }, + { title: '2' }, + { title: '3' }, + { title: '4' }, + { title: 'ЕРТ' }, +] as const; + +export const EFFECTS_LOAD: PodEffect[] = [ + { + act: 'launchAll', + choiceNumber: 0, + icon: 'globe', + selected: 'launchChoice', + title: 'Запустить со всех турфов', + }, + { + act: 'launchOrdered', + choiceNumber: 1, + icon: 'sort-amount-down-alt', + selected: 'launchChoice', + title: 'Запустить с турфов по порядку', + }, + { + act: 'launchRandomTurf', + choiceNumber: 2, + icon: 'dice', + selected: 'launchChoice', + title: 'Выбрать рандомный турф', + }, + { + divider: true, + }, + { + act: 'launchWholeTurf', + choiceNumber: 0, + icon: 'expand', + selected: 'launchRandomItem', + title: 'Запустить все содержимое турфа', + }, + { + act: 'launchRandomItem', + choiceNumber: 1, + icon: 'dice', + selected: 'launchRandomItem', + title: 'Выбрать случайный объект', + }, + { + divider: true, + }, + { + act: 'launchClone', + icon: 'clone', + soloSelected: 'launchClone', + title: 'Копировать объект', + }, +]; + +export const EFFECTS_NORMAL: PodEffect[] = [ + { + act: 'effectTarget', + icon: 'user-check', + soloSelected: 'effectTarget', + title: 'Особая цель', + }, + { + act: 'effectBluespace', + choiceNumber: 0, + icon: 'hand-paper', + selected: 'effectBluespace', + title: 'Капсула остается', + }, + { + act: 'effectStealth', + icon: 'user-ninja', + soloSelected: 'effectStealth', + title: 'Скрытно', + }, + { + act: 'effectQuiet', + icon: 'volume-mute', + soloSelected: 'effectQuiet', + title: 'Тихо', + }, + { + act: 'effectMissile', + icon: 'rocket', + soloSelected: 'effectMissile', + title: 'Режим ракеты', + }, + { + act: 'effectBurst', + icon: 'certificate', + soloSelected: 'effectBurst', + title: 'Запуск кластера', + }, + { + act: 'effectCircle', + icon: 'ruler-combined', + soloSelected: 'effectCircle', + title: 'Любой угол спуска', + }, + { + act: 'effectAnnounce', + choiceNumber: 0, + icon: 'ghost', + selected: 'effectAnnounce', + title: + 'Нет оповещения призраков\n(если вы не хотите\nразвлекать скучающих призраков)', + }, +]; + +export const EFFECTS_HARM: PodEffect[] = [ + { + act: 'explosionCustom', + choiceNumber: 1, + icon: 'bomb', + selected: 'explosionChoice', + title: 'Настраиваемый взрыв', + }, + { + act: 'explosionBus', + choiceNumber: 2, + icon: 'bomb', + selected: 'explosionChoice', + title: 'Админабуз-взрыв\nИ что они сделают, забанят тебя?', + }, + { + divider: true, + }, + { + act: 'damageCustom', + choiceNumber: 1, + icon: 'skull', + selected: 'damageChoice', + title: 'Настраиваемый урон', + }, + { + act: 'damageGib', + choiceNumber: 2, + icon: 'skull-crossbones', + selected: 'damageChoice', + title: 'Гиб', + }, + { + divider: true, + }, + { + act: 'effectShrapnel', + details: true, + icon: 'cloud-meatball', + soloSelected: 'effectShrapnel', + title: 'Облако снарядов', + }, + { + act: 'effectStun', + icon: 'sun', + soloSelected: 'effectStun', + title: 'Стан', + }, + { + act: 'effectLimb', + icon: 'socks', + soloSelected: 'effectLimb', + title: 'Потеря конечности', + }, + { + act: 'effectOrgans', + icon: 'book-dead', + soloSelected: 'effectOrgans', + title: 'Разлет всех органов', + }, +]; + +type Effect = { + list: typeof EFFECTS_LOAD | typeof EFFECTS_NORMAL | typeof EFFECTS_HARM; + label: string; + alt_label?: string; + tooltipPosition: Placement; +}; + +export const EFFECTS_ALL: Effect[] = [ + { + list: EFFECTS_LOAD, + label: 'Загрузить из', + alt_label: 'Загрузка', + tooltipPosition: 'right', + }, + { + list: EFFECTS_NORMAL, + label: 'Обычные Эффекты', + tooltipPosition: 'bottom', + }, + { + list: EFFECTS_HARM, + label: 'Вредные эффекты', + tooltipPosition: 'bottom', + }, +]; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/hooks.ts b/tgui/packages/tgui/interfaces/CentcomPodLauncher/hooks.ts new file mode 100644 index 00000000000..32f4f9bd3f7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/hooks.ts @@ -0,0 +1,5 @@ +import { useLocalState } from '../../backend'; + +export const useCompact = (context) => useLocalState(context, 'compact', false); + +export const useTab = (context) => useLocalState(context, 'tab', 1); diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/index.tsx b/tgui/packages/tgui/interfaces/CentcomPodLauncher/index.tsx new file mode 100644 index 00000000000..cf1134889ac --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/index.tsx @@ -0,0 +1,74 @@ +import { Section, Stack } from '../../components'; +import { Window } from '../../layouts'; +import { useCompact } from './hooks'; +import { PodBays } from './PodBays'; +import { PodLaunch } from './PodLaunch'; +import { PodSounds } from './PodSounds'; +import { PodStatusPage } from './PodStatusPage'; +import { PresetsPage } from './PresetsPage'; +import { ReverseMenu } from './ReverseMenu'; +import { StylePage } from './StylePage'; +import { Timing } from './Timing'; +import { ViewTabHolder } from './ViewTabHolder'; + +export const CentcomPodLauncher = (props, context) => { + const [compact] = useCompact(context); + + return ( + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+ {!compact && ( + + + + )} + + + + + + + + + {!compact && ( + + + + )} + + + + + +
+
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CentcomPodLauncher/types.ts b/tgui/packages/tgui/interfaces/CentcomPodLauncher/types.ts new file mode 100644 index 00000000000..315cc7dc286 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CentcomPodLauncher/types.ts @@ -0,0 +1,70 @@ +import { Placement } from '@popperjs/core'; +import { BooleanLike } from 'common/react'; + +export type PodLauncherData = { + bayNumber: string; + custom_rev_delay: number; + customDropoff: BooleanLike; + damageChoice: number; + defaultSoundVolume: number; + delays: Record; + effectAnnounce: BooleanLike; + effectBluespace: BooleanLike; + effectBurst: BooleanLike; + effectCircle: BooleanLike; + effectLimb: BooleanLike; + effectMissile: BooleanLike; + effectName: number; + effectOrgans: BooleanLike; + effectQuiet: BooleanLike; + effectReverse: BooleanLike; + effectShrapnel: BooleanLike; + effectStealth: BooleanLike; + effectStun: BooleanLike; + effectTarget: string | null; + explosionChoice: number; + fallingSound: BooleanLike; + giveLauncher: BooleanLike; + landingSound: string | null; + launchChoice: number; + launchClone: BooleanLike; + launchRandomItem: BooleanLike; + leavingSound: string | null; + mapRef: string; + numObjects: number; + oldArea: string | null; + openingSound: string | null; + payload: BooleanLike; + picking_dropoff_turf: BooleanLike; + podDesc: string; + podName: string; + renderLighting: BooleanLike; + rev_delays: Record; + reverse_option_list: Record; + shrapnelMagnitude: number; + shrapnelType: string; + soundVolume: number; + styleChoice: string; + podStyles: Array>; +}; + +export type PodDelay = { + title: string; + tooltip: string; +}; + +export type PodEffect = + | { + act: string; + choiceNumber?: number; + content?: string; + details?: boolean; + divider?: never; + icon: string; + payload?: Record; + selected?: string; + soloSelected?: string; + title: string; + tooltipPosition?: Placement; + } + | { divider: boolean }; diff --git a/tgui/packages/tgui/interfaces/CheckboxListInputModal.tsx b/tgui/packages/tgui/interfaces/CheckboxListInputModal.tsx new file mode 100644 index 00000000000..3bb0ee31ce5 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CheckboxListInputModal.tsx @@ -0,0 +1,87 @@ +import { Loader } from './common/Loader'; +import { InputButtons } from './common/InputButtons'; +import { Button, Section, Stack } from '../components'; +import { useBackend, useLocalState } from '../backend'; +import { Window } from '../layouts'; +import { createLogger } from '../logging'; +import { BooleanLike } from 'common/react'; + +type ListInputData = { + init_value: string; + items: CheckboxData[]; + message: string; + timeout: number; + title: string; +}; + +interface CheckboxData { + key: string; + checked: BooleanLike; +} +export const CheckboxListInputModal = (props, context) => { + const { act, data } = useBackend(context); + const { items = [], message = '', init_value, timeout, title } = data; + const [edittedItems, setEdittedItems] = useLocalState( + context, + 'edittedItems', + items + ); + + const windowHeight = 330 + Math.ceil(message.length / 3); + + const onClick = (new_item: CheckboxData | null = null) => { + let updatedItems = [...edittedItems]; + updatedItems = updatedItems.map((item) => + item.key === new_item.key ? { ...item, checked: !new_item.checked } : item + ); + setEdittedItems(updatedItems); + }; + + return ( + + {timeout && } + +
+ + + + + + + + +
+
+
+ ); +}; + +/** + * Displays the list of selectable items. + * If a search query is provided, filters the items. + */ +const ListDisplay = (props, context) => { + const { filteredItems, onClick } = props; + + return ( +
+ {filteredItems.map((item, index) => { + return ( + onClick(item)} + checked={item.checked} + style={{ + 'animation': 'none', + 'transition': 'none', + }} + > + {item.key.replace(/^\w/, (c) => c.toUpperCase())} + + ); + })} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CoinMint.tsx b/tgui/packages/tgui/interfaces/CoinMint.tsx new file mode 100644 index 00000000000..fde01d796e7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/CoinMint.tsx @@ -0,0 +1,128 @@ +import { classes } from '../../common/react'; +import { BooleanLike } from 'common/react'; +import { useBackend } from '../backend'; +import { Button, Section, ProgressBar, Stack, NoticeBox } from '../components'; +import { Window } from '../layouts'; + +type MintData = { + active: BooleanLike; + moneyBag: BooleanLike; + moneyBagContent: number; + moneyBagMaxContent: number; + totalCoins: number; + totalMaterials: number; + maxMaterials: number; + chosenMaterial: string; + materials: MintMaterial[]; +}; + +type MintMaterial = { + name: string; + amount: number; + id: string; +}; + +export const CoinMint = (props, context) => { + const { act, data } = useBackend(context); + const { materials, moneyBag, moneyBagContent, moneyBagMaxContent } = data; + const dynamicHeight = + (moneyBag ? 210 : 138) + Math.ceil(materials.length / 4) * 64; + return ( + + + + + + Произведено монет: {data.totalCoins} + + + +
act('activate')} + /> + } + > + + + + + + + +
+
+ {!!moneyBag && ( + +
act('ejectBag')} + /> + } + > + + {moneyBagContent} / {moneyBagMaxContent} + +
+
+ )} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ColorPickerModal.tsx b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx new file mode 100644 index 00000000000..3522c4442e1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ColorPickerModal.tsx @@ -0,0 +1,668 @@ +/* eslint-disable react/state-in-constructor */ +/** + * @file + * @copyright 2023 itsmeow + * @license MIT + */ + +import { Loader } from './common/Loader'; +import { useBackend, useLocalState } from '../backend'; +import { + Autofocus, + Box, + Flex, + Section, + Stack, + Pointer, + NumberInput, + Tooltip, +} from '../components'; +import { Window } from '../layouts'; +import { clamp } from 'common/math'; +import { + hexToHsva, + HsvaColor, + hsvaToHex, + hsvaToHslString, + hsvaToRgba, + rgbaToHsva, + validHex, +} from 'common/color'; +import { Interaction, Interactive } from 'tgui/components/Interactive'; +import { classes } from 'common/react'; +import { Component, FocusEvent, FormEvent, InfernoNode } from 'inferno'; +import { logger } from 'tgui/logging'; +import { InputButtons } from './common/InputButtons'; + +type ColorPickerData = { + autofocus: boolean; + buttons: string[]; + message: string; + large_buttons: boolean; + swapped_buttons: boolean; + timeout: number; + title: string; + default_color: string; +}; + +export const ColorPickerModal = (_, context) => { + const { data } = useBackend(context); + const { + timeout, + message, + title, + autofocus, + default_color = '#000000', + } = data; + let [selectedColor, setSelectedColor] = useLocalState( + context, + 'color_picker_choice', + hexToHsva(default_color) + ); + + return ( + + {!!timeout && } + + + {message && ( + +
+ + {message} + +
+
+ )} + +
+ {!!autofocus && } + +
+
+ + + +
+
+
+ ); +}; + +export const ColorSelector = ( + { + color, + setColor, + defaultColor, + }: { color: HsvaColor; setColor; defaultColor: string }, + context +) => { + const handleChange = (params: Partial) => { + setColor((current: HsvaColor) => { + return Object.assign({}, current, params); + }); + }; + const rgb = hsvaToRgba(color); + const hexColor = hsvaToHex(color); + return ( + + + + +
+ + +
+
+ + + Current + + + Previous + +
+ + + + + + +
+
+
+ + + + + + Hex: + + + { + logger.info(value); + setColor(hexToHsva(value)); + }} + prefixed + /> + + + + + + + + H: + + + + + + handleChange({ h: v })} + max={360} + unit="°" + /> + + + + + + + S: + + + + + + handleChange({ s: v })} + unit="%" + /> + + + + + + + V: + + + + + + handleChange({ v: v })} + unit="%" + /> + + + + + + + + R: + + + + + + { + rgb.r = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + G: + + + + + + { + rgb.g = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + + + B: + + + + + + { + rgb.b = v; + handleChange(rgbaToHsva(rgb)); + }} + max={255} + /> + + + + + +
+ ); +}; + +const TextSetter = ({ + value, + callback, + min = 0, + max = 100, + unit, +}: { + value: number; + callback: any; + min?: number; + max?: number; + unit?: string; +}) => { + return ( + + ); +}; + +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +interface HexColorInputProps + extends Omit { + /** Enables `#` prefix displaying */ + prefixed?: boolean; + /** Allows `#rgba` and `#rrggbbaa` color formats */ + alpha?: boolean; +} + +/** Adds "#" symbol to the beginning of the string */ +const prefix = (value: string) => '#' + value; + +export const HexColorInput = (props: HexColorInputProps): InfernoNode => { + const { prefixed, alpha, color, fluid, onChange, ...rest } = props; + + /** Escapes all non-hexadecimal characters including "#" */ + const escape = (value: string) => + value.replace(/([^0-9A-F]+)/gi, '').substring(0, alpha ? 8 : 6); + + /** Validates hexadecimal strings */ + const validate = (value: string) => validHex(value, alpha); + + return ( + + ); +}; + +interface ColorInputBaseProps { + fluid?: boolean; + color: string; + onChange: (newColor: string) => void; + /** Blocks typing invalid characters and limits string length */ + escape: (value: string) => string; + /** Checks that value is valid color string */ + validate: (value: string) => boolean; + /** Processes value before displaying it in the input */ + format?: (value: string) => string; +} + +export class ColorInput extends Component { + props: ColorInputBaseProps; + state: { localValue: string }; + + constructor(props: ColorInputBaseProps) { + super(); + this.props = props; + this.state = { localValue: this.props.escape(this.props.color) }; + } + + // Trigger `onChange` handler only if the input value is a valid color + handleInput = (e: FormEvent) => { + const inputValue = this.props.escape(e.currentTarget.value); + this.setState({ localValue: inputValue }); + }; + + // Take the color from props if the last typed color (in local state) is not valid + handleBlur = (e: FocusEvent) => { + if (e.currentTarget) { + if (!this.props.validate(e.currentTarget.value)) { + this.setState({ localValue: this.props.escape(this.props.color) }); // return to default; + } else { + this.props.onChange( + this.props.escape + ? this.props.escape(e.currentTarget.value) + : e.currentTarget.value + ); + } + } + }; + + componentDidUpdate(prevProps, prevState): void { + if (prevProps.color !== this.props.color) { + // Update the local state when `color` property value is changed + this.setState({ localValue: this.props.escape(this.props.color) }); + } + } + + render() { + return ( + +
.
+ +
+ ); + } +} + +const SaturationValue = ({ hsva, onChange }) => { + const handleMove = (interaction: Interaction) => { + onChange({ + s: interaction.left * 100, + v: 100 - interaction.top * 100, + }); + }; + + const handleKey = (offset: Interaction) => { + // Saturation and brightness always fit into [0, 100] range + onChange({ + s: clamp(hsva.s + offset.left * 100, 0, 100), + v: clamp(hsva.v - offset.top * 100, 0, 100), + }); + }; + + const containerStyle = { + 'background-color': `${hsvaToHslString({ h: hsva.h, s: 100, v: 100, a: 1 })} !important`, + }; + + return ( +
+ + + +
+ ); +}; + +const Hue = ({ + className, + hue, + onChange, +}: { + className?: string; + hue: number; + onChange: (newHue: { h: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ h: 360 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 360 + onChange({ + h: clamp(hue + offset.left * 360, 0, 360), + }); + }; + + const nodeClassName = classes(['react-colorful__hue', className]); + + return ( +
+ + + +
+ ); +}; + +const Saturation = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newSaturation: { s: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ s: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + // Hue measured in degrees of the color circle ranging from 0 to 100 + onChange({ + s: clamp(color.s + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__saturation', className]); + + return ( +
+ + + +
+ ); +}; + +const Value = ({ + className, + color, + onChange, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: { v: number }) => void; +}) => { + const handleMove = (interaction: Interaction) => { + onChange({ v: 100 * interaction.left }); + }; + + const handleKey = (offset: Interaction) => { + onChange({ + v: clamp(color.v + offset.left * 100, 0, 100), + }); + }; + + const nodeClassName = classes(['react-colorful__value', className]); + + return ( +
+ + + +
+ ); +}; + +const RGBSlider = ({ + className, + color, + onChange, + target, +}: { + className?: string; + color: HsvaColor; + onChange: (newValue: HsvaColor) => void; + target: string; +}) => { + const rgb = hsvaToRgba(color); + + const setNewTarget = (value: number) => { + rgb[target] = value; + onChange(rgbaToHsva(rgb)); + }; + + const handleMove = (interaction: Interaction) => { + setNewTarget(255 * interaction.left); + }; + + const handleKey = (offset: Interaction) => { + setNewTarget(clamp(rgb[target] + offset.left * 255, 0, 255)); + }; + + const nodeClassName = classes([`react-colorful__${target}`, className]); + + let selected = + target === 'r' + ? `rgb(${Math.round(rgb.r)},0,0)` + : target === 'g' + ? `rgb(0,${Math.round(rgb.g)},0)` + : `rgb(0,0,${Math.round(rgb.b)})`; + + return ( +
+ + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/FloorPainter.js b/tgui/packages/tgui/interfaces/FloorPainter.js index 5b878683a7f..7744ef56ce4 100644 --- a/tgui/packages/tgui/interfaces/FloorPainter.js +++ b/tgui/packages/tgui/interfaces/FloorPainter.js @@ -1,6 +1,7 @@ import { useBackend, useLocalState } from '../backend'; import { Button, + DmIcon, LabeledList, Section, Table, @@ -13,30 +14,33 @@ import { Window } from '../layouts'; const SelectableTile = (props, context) => { const { act, data } = useBackend(context); - const { image, isSelected, onSelect } = props; + const { icon_state, direction, isSelected, onSelect } = props; return ( - ); }; +const Dir = { + NORTH: 1, + SOUTH: 2, + EAST: 4, + WEST: 8, +}; + export const FloorPainter = (props, context) => { const { act, data } = useBackend(context); - const { - availableStyles, - selectedStyle, - selectedDir, - directionsPreview, - allStylesPreview, - } = data; + const { availableStyles, selectedStyle, selectedDir } = data; return ( @@ -71,13 +75,13 @@ export const FloorPainter = (props, context) => { {availableStyles.map((style) => ( - + act('select_style', { style: style })} /> @@ -89,9 +93,9 @@ export const FloorPainter = (props, context) => { - {['north', '', 'south'].map((latitude) => ( + {[Dir.NORTH, null, Dir.SOUTH].map((latitude) => ( - {[latitude + 'west', latitude, latitude + 'east'].map( + {[latitude + Dir.WEST, latitude, latitude + Dir.EAST].map( (dir) => ( { 'text-align': 'center', }} > - {dir === '' ? ( + {dir === null ? ( ) : ( act('select_direction', { direction: dir }) diff --git a/tgui/packages/tgui/interfaces/ImplantPad.js b/tgui/packages/tgui/interfaces/ImplantPad.js index 10a42084fa8..80592c8577a 100644 --- a/tgui/packages/tgui/interfaces/ImplantPad.js +++ b/tgui/packages/tgui/interfaces/ImplantPad.js @@ -1,5 +1,13 @@ import { useBackend, useLocalState } from '../backend'; -import { Button, Section, Box, LabeledList, Input, Icon } from '../components'; +import { + Button, + Section, + Box, + LabeledList, + Input, + Icon, + DmIcon, +} from '../components'; import { Window } from '../layouts'; export const ImplantPad = (props, context) => { @@ -27,8 +35,9 @@ export const ImplantPad = (props, context) => { {implant && contains_case ? ( <> - >; +}; + +type Gear = { + name: string; + index_name: string; + desc: string; + icon: string; + icon_state: string; + cost: number; + gear_tier: number; + allowed_roles: string[]; + tweaks: Record; +}; + +type Tweak = { + name: string; + icon: string; + tooltip: string; +}; + +const sortTypes = { + 'Default': (a, b) => a.gear.gear_tier - b.gear.gear_tier, + 'Alphabetical': (a, b) => + a.gear.name.toLowerCase().localeCompare(b.gear.name.toLowerCase()), + 'Cost': (a, b) => a.gear.cost - b.gear.cost, +}; + +export const Loadout = (props, context) => { + const { act, data } = useBackend(context); + const [search, setSearch] = useLocalState(context, 'search', false); + const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); + const [category, setCategory] = useLocalState( + context, + 'category', + Object.keys(data.gears)[0] + ); + const [tweakedGear, setTweakedGear] = useLocalState( + context, + 'tweakedGear', + '' + ); + + return ( + + {tweakedGear && ( + + )} + + + + + + + + + + + + + + + + + + + ); +}; + +const LoadoutCategories = (props, context) => { + const { act, data } = useBackend(context); + const { category, setCategory } = props; + return ( + + {Object.keys(data.gears).map((cat) => ( + setCategory(cat)} + > + {cat} + + ))} + + ); +}; + +const LoadoutGears = (props, context) => { + const { act, data } = useBackend(context); + const { user_tier, gear_slots, max_gear_slots } = data; + const { category, search, setSearch, searchText, setSearchText } = props; + + const [sortType, setSortType] = useLocalState(context, 'sortType', 'Default'); + const [sortReverse, setsortReverse] = useLocalState( + context, + 'sortReverse', + false + ); + const testSearch = createSearch(searchText, (gear) => gear.name); + + let contents; + if (searchText.length > 2) { + contents = Object.entries(data.gears) + .reduce((a, [key, gears]) => { + return a.concat( + Object.entries(gears).map(([key, gear]) => ({ key, gear })) + ); + }, []) + .filter(({ gear }) => { + return testSearch(gear); + }); + } else { + contents = Object.entries(data.gears[category]).map(([key, gear]) => ({ + key, + gear, + })); + } + + contents.sort(sortTypes[sortType]); + if (sortReverse) { + contents = contents.reverse(); + } + + return ( +
+ + setSortType(value)} + /> + + +
+ } + tooltipPosition="left" + /> + )} + {Object.entries(gear.tweaks).map( + ([key, tweaks]: [string, Tweak[]]) => + tweaks.map((tweak) => ( +
+ {filteredItems.map((item, index) => ( + handleDragStart(index)} + onDragOver={handleDragOver} + onDrop={() => handleDrop(index)} + style={{ + padding: '8px', + }} + > + + + ))} +
+ + ); +}; diff --git a/tgui/packages/tgui/interfaces/ReagentsEditor.tsx b/tgui/packages/tgui/interfaces/ReagentsEditor.tsx new file mode 100644 index 00000000000..a17bca82ab8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ReagentsEditor.tsx @@ -0,0 +1,213 @@ +import { Component } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, Icon, Input, Section, Stack, Table } from '../components'; +import { Window } from '../layouts'; +import { createSearch } from 'common/string'; + +type StaticReagentInformation = { + name: string; +}; + +type VolatileReagentInformation = { + volume: number | null; + uid: string; +}; + +type FilteredReagentInformation = StaticReagentInformation & { + id: string; +} & Partial; + +type StaticData = { + reagentsInformation: Record; +}; + +type VolatileData = { + reagents: Record; +}; + +type ReagentsEditorData = StaticData & VolatileData; + +type ReagentsEditorState = { + searchText: string; +}; + +// The linter is raising false-positives for unused state +/* eslint-disable react/no-unused-state */ +export class ReagentsEditor extends Component<{}, ReagentsEditorState> { + constructor(props: {}) { + super(props); + this.state = { + searchText: '', + }; + } + + handleSearchChange = (e: Event) => { + const target = e.target as HTMLInputElement; + this.setState({ searchText: target.value }); + }; + + override render(props: {}, state: ReagentsEditorState, context: unknown) { + const { + act, + data: { reagentsInformation, reagents: reagentsInContainer }, + } = useBackend(this.context); + + let reagents = Object.entries(reagentsInContainer) + .map( + ([reagentID, reagent]): FilteredReagentInformation => ({ + ...reagent, + ...reagentsInformation[reagentID], + id: reagentID, + }) + ) + .sort((a, b) => a.name.localeCompare(b.name)); + if (state.searchText !== '') { + reagents = reagents.concat( + Object.entries(reagentsInformation) + .filter( + ([reagentID, _]) => reagentsInContainer[reagentID] === undefined + ) + .map( + ([reagentID, reagent]): FilteredReagentInformation => ({ + ...reagent, + id: reagentID, + }) + ) + .sort((a, b) => a.name.localeCompare(b.name)) + ); + } + + const reagentsRows = reagents + .filter(({ id: reagentID, name }) => + createSearch(state.searchText, () => `${reagentID}|${name}`)({}) + ) + .map((reagent) => { + const { volume, uid } = reagent; + return volume === undefined ? ( + + ) : ( + + ); + }); + + return ( + + +
+ + + + + + + +
+
+
+ ); + } +} + +// Row for a reagent with non-zero volume +const PresentReagentRow = ( + { + reagent: { id: reagentID, name, uid, volume }, + }: { reagent: FilteredReagentInformation }, + context: unknown +) => { + const { act } = useBackend(context); + return ( + + + {name} (ID: {reagentID}) + + +
+
+ + {volume === null ? `NULL` : `${volume}u`} + +
+
+ ); +}; + +// Row for a reagent with zero volume +const AbsentReagentRow = ( + { reagent: { id: reagentID, name } }: { reagent: FilteredReagentInformation }, + context: unknown +) => { + const { act } = useBackend(context); + return ( + + + {reagentID} ({name}) + + +
diff --git a/tgui/packages/tgui/interfaces/Workshop.js b/tgui/packages/tgui/interfaces/Workshop.js index 28408318c59..d9f273df59d 100644 --- a/tgui/packages/tgui/interfaces/Workshop.js +++ b/tgui/packages/tgui/interfaces/Workshop.js @@ -4,6 +4,7 @@ import { Box, Button, Collapsible, + DmIcon, Dropdown, Input, LabeledList, @@ -205,8 +206,9 @@ const WorkshopItemsCategory = (properties, context) => { {items.map((item) => ( - 0.3, rgba(0, 0, 0, 1), rgba(255, 255, 255, 1)); + + background-color: rgba($color, $opacity); + color: $text-color; + border: solid $border-color; + border-width: $border-width; + transition: + background-color $transition-duration, + border-color $transition-duration; + + @if $hoverable { + &:hover { + background-color: rgba(lighten($color, 50%), $opacity); + } + } +} + +@each $color-name, $color-value in $bg-map { + .color__#{$color-name} { + @include button-style($color-value, $border-width: 1px); + } + + .contentColor__#{$color-name} { + @include button-style( + $color-value, + $border-color: lighten($color-value, 25%), + $opacity: 1, + $hoverable: false + ); + } + + .buttonsContainerColor__#{$color-name} { + @include button-style( + $color-value, + $border-width: 1px 1px 1px 0, + $opacity: 0.33, + $hoverable: false, + $transition-duration: 0 + ); + } +} + +.color__default { + @include button-style(lighten($color-default, 85%), $border-width: 1px); +} + +.disabled { + background-color: rgba($color-disabled, 0.25) !important; + border-color: rgba($color-disabled, 0.25) !important; +} + +.selected { + @include button-style( + $color-selected, + $border-color: rgba($color-selected, 0.25), + $border-width: 1px + ); +} + +.contentColor__default { + @include button-style( + lighten($color-default, 80%), + $border-color: lighten($color-default, 100%), + $opacity: 1, + $hoverable: false + ); +} + +.contentDisabled { + background-color: $color-disabled !important; + border-top: 1px solid lighten($color-disabled, 25%) !important; +} + +.contentSelected { + @include button-style( + $color-selected, + $border-color: lighten($color-selected, 25%), + $opacity: 1, + $hoverable: false + ); +} + +.buttonsContainerColor__default { + @include button-style( + lighten($color-default, 85%), + $border-width: 1px 1px 1px 0, + $hoverable: false, + $transition-duration: 0 + ); +} + +.ImageButton { + display: inline-table; + position: relative; + text-align: center; + margin: 0.25em; + user-select: none; + -ms-user-select: none; + + .noAction { + pointer-events: none; + } + + .container { + display: flex; + flex-direction: column; + border-radius: 0.33em; + } + + .image { + position: relative; + align-self: center; + pointer-events: none; + overflow: hidden; + line-height: 0; + padding: 0.25em; + border-radius: 0.33em; + } + + .buttonsContainer { + display: flex; + position: absolute; + overflow: hidden; + left: 1px; + bottom: 1.8em; + max-width: 100%; + z-index: 1; + + &.buttonsAltContainer { + overflow: visible; + flex-direction: column; + pointer-events: none; + top: 1px; + bottom: inherit !important; + } + + &.buttonsEmpty { + bottom: 1px; + } + + & > * { + /* I know !important is bad, but here's no other way */ + margin: 0 !important; + padding: 0 0.2em !important; + border-radius: 0 !important; + } + } + + .content { + -ms-user-select: none; + user-select: none; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0.25em 0.5em; + margin: -1px; + border-radius: 0 0 0.33em 0.33em; + z-index: 2; + } +} + +.fluid { + display: flex; + flex-direction: row; + position: relative; + text-align: center; + margin: 0 0 0.5em 0; + user-select: none; + -ms-user-select: none; + + &:last-of-type { + margin-bottom: 0; + } + + .info { + display: flex; + flex-direction: column; + justify-content: center; + flex: 1; + } + + .title { + font-weight: bold; + padding: 0.5em; + + &.divider { + margin: 0 0.5em; + border-bottom: Divider.$thickness solid Divider.$color; + } + } + + .contentFluid { + padding: 0.5em; + color: white; + } + + .container { + flex-direction: row; + flex: 1; + + &.hasButtons { + border-radius: 0.33em 0 0 0.33em; + border-width: 1px 0 1px 1px; + } + } + + .image { + padding: 0; + } + + .buttonsContainer { + position: relative; + left: inherit; + bottom: inherit; + border-radius: 0 0.33em 0.33em 0; + + &.buttonsEmpty { + bottom: inherit; + } + + &.buttonsAltContainer { + overflow: hidden; + pointer-events: auto; + top: inherit; + + & > * { + border-top: 1px solid rgba(255, 255, 255, 0.075); + + &:first-child { + border-top: 0; + } + } + } + + & > * { + display: inline-flex; + flex-direction: column; + justify-content: center; + text-align: center; + white-space: pre-wrap; + line-height: base.em(14px); + height: 100%; + border-left: 1px solid rgba(255, 255, 255, 0.075); + } + } +} diff --git a/tgui/packages/tgui/styles/components/Tooltip.scss b/tgui/packages/tgui/styles/components/Tooltip.scss index 497813a206d..f811ef889e6 100644 --- a/tgui/packages/tgui/styles/components/Tooltip.scss +++ b/tgui/packages/tgui/styles/components/Tooltip.scss @@ -11,7 +11,7 @@ $background-color: #000000 !default; $border-radius: base.$border-radius !default; .Tooltip { - z-index: 2; + z-index: 999; /* Should be above everything */ padding: 0.5em 0.75em; pointer-events: none; text-align: left; diff --git a/tgui/packages/tgui/styles/interfaces/ColorPicker.scss b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss new file mode 100644 index 00000000000..99f628c35e2 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/ColorPicker.scss @@ -0,0 +1,153 @@ +/** + * MIT License + * https://github.com/omgovich/react-colorful/ + * + * Copyright (c) 2020 Vlad Shilov + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +@use '../colors.scss'; +@use '../base.scss'; + +.react-colorful { + position: relative; + display: flex; + flex-direction: column; + width: 200px; + height: 200px; + user-select: none; + cursor: default; +} + +.react-colorful__saturation_value { + position: relative; + flex-grow: 1; + border-color: transparent; /* Fixes https://github.com/omgovich/react-colorful/issues/139 */ + border-bottom: 12px solid #000; + border-radius: 8px 8px 0 0; + background-image: linear-gradient( + to top, + rgba(0, 0, 0, 255), + rgba(0, 0, 0, 0) + ), + linear-gradient(to right, rgba(255, 255, 255, 255), rgba(255, 255, 255, 0)); +} + +.react-colorful__pointer-fill, +.react-colorful__alpha-gradient { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + pointer-events: none; + border-radius: inherit; +} + +/* Improve elements rendering on light backgrounds */ +.react-colorful__alpha-gradient, +.react-colorful__saturation_value { + box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.05); +} + +.react-colorful__hue, +.react-colorful__r, +.react-colorful__g, +.react-colorful__b, +.react-colorful__alpha, +.react-colorful__saturation, +.react-colorful__value { + position: relative; + height: 24px; +} + +.react-colorful__hue { + background: linear-gradient( + to right, + #f00 0%, + #ff0 17%, + #0f0 33%, + #0ff 50%, + #00f 67%, + #f0f 83%, + #f00 100% + ); +} + +.react-colorful__r { + background: linear-gradient(to right, #000, #f00); +} + +.react-colorful__g { + background: linear-gradient(to right, #000, #0f0); +} + +.react-colorful__b { + background: linear-gradient(to right, #000, #00f); +} + +/* Round bottom corners of the last element: `Hue` for `ColorPicker` or `Alpha` for `AlphaColorPicker` */ +.react-colorful__last-control { + border-radius: 0 0 8px 8px; +} + +.react-colorful__interactive { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + border-radius: inherit; + outline: none; + /* Don't trigger the default scrolling behavior when the event is originating from this element */ + touch-action: none; +} + +.react-colorful__pointer { + position: absolute; + z-index: 1; + box-sizing: border-box; + width: 28px; + height: 28px; + transform: translate(-50%, -50%); + background-color: #cfcfcf; + border: 2px solid #cfcfcf; + border-radius: 50%; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.4); +} + +.react-colorful__interactive:focus .react-colorful__pointer { + transform: translate(-50%, -50%) scale(1.1); + background-color: #fff; + border-color: #fff; +} + +/* Chessboard-like pattern for alpha related elements */ +.react-colorful__alpha, +.react-colorful__alpha-pointer { + background-color: #fff; + background-image: url('data:image/svg+xml,'); +} + +.react-colorful__saturation-pointer, +.react-colorful__value-pointer, +.react-colorful__hue-pointer, +.react-colorful__r-pointer, +.react-colorful__g-pointer, +.react-colorful__b-pointer { + z-index: 1; + width: 20px; + height: 20px; +} + +/* Display the saturation value pointer over the hue one */ +.react-colorful__saturation_value-pointer { + z-index: 3; +} diff --git a/tgui/packages/tgui/styles/interfaces/Loadout.scss b/tgui/packages/tgui/styles/interfaces/Loadout.scss new file mode 100644 index 00000000000..584f50762b3 --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/Loadout.scss @@ -0,0 +1,13 @@ +@use '../base.scss' as *; + +.Loadout-Modal__background { + padding: 0.5em; + background-color: $color-bg; +} + +.Loadout-InfoBox { + display: flex; + line-height: 1.2rem; + text-shadow: 0 1px 0 2px rgba(0, 0, 0, 0.66); + text-align: left; +} diff --git a/tgui/packages/tgui/styles/interfaces/ReagentsEditor.scss b/tgui/packages/tgui/styles/interfaces/ReagentsEditor.scss new file mode 100644 index 00000000000..bc25f59ca1c --- /dev/null +++ b/tgui/packages/tgui/styles/interfaces/ReagentsEditor.scss @@ -0,0 +1,40 @@ +@use '../colors.scss'; + +.reagents-table { + border-collapse: separate; + border-spacing: 0 0.3em; +} + +.volume-cell { + min-width: 3em; + text-align: right; + vertical-align: middle; +} + +.volume-cell:not(:hover) .volume-actions-wrapper { + display: none; +} + +.volume-cell:hover .volume-label { + display: none; +} + +.reagent-absent-name-cell { + color: colors.$grey; +} + +.reagent-row > *:last-child { + padding-right: 0.5em; +} + +.absent-row:not(:hover) .add-reagent-button { + visibility: hidden; +} + +.condensed-button { + margin: 0; + padding: 0; + min-height: 0; + line-height: 0; + background: none; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 4222996bd47..ff2d7b25d83 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -27,6 +27,7 @@ @include meta.load-css('./components/Flex.scss'); @include meta.load-css('./components/Icon.scss'); @include meta.load-css('./components/ImageButton.scss'); +@include meta.load-css('./components/ImageButtonTS.scss'); @include meta.load-css('./components/Input.scss'); @include meta.load-css('./components/Knob.scss'); @include meta.load-css('./components/LabeledList.scss'); @@ -50,15 +51,18 @@ @include meta.load-css('./interfaces/BrigCells.scss'); @include meta.load-css('./interfaces/CameraConsole.scss'); @include meta.load-css('./interfaces/Changelog.scss'); +@include meta.load-css('./interfaces/ColorPicker.scss'); @include meta.load-css('./interfaces/Contractor.scss'); @include meta.load-css('./interfaces/ExosuitFabricator.scss'); @include meta.load-css('./interfaces/ListInput.scss'); +@include meta.load-css('./interfaces/Loadout.scss'); @include meta.load-css('./interfaces/Minesweeper.scss'); @include meta.load-css('./interfaces/Newscaster.scss'); @include meta.load-css('./interfaces/NuclearBomb.scss'); @include meta.load-css('./interfaces/OreRedemption.scss'); @include meta.load-css('./interfaces/PDA.scss'); @include meta.load-css('./interfaces/PoolController.scss'); +@include meta.load-css('./interfaces/ReagentsEditor.scss'); @include meta.load-css('./interfaces/QuestConsole.scss'); @include meta.load-css('./interfaces/RequestConsole.scss'); @include meta.load-css('./interfaces/RndConsole.scss'); diff --git a/tgui/packages/tgui/styles/themes/ntOS95.scss b/tgui/packages/tgui/styles/themes/ntOS95.scss index df541bf18a4..08c4194c31f 100644 --- a/tgui/packages/tgui/styles/themes/ntOS95.scss +++ b/tgui/packages/tgui/styles/themes/ntOS95.scss @@ -71,12 +71,12 @@ $scrollbar-color-multiplier: 1; } .Section { - &__titleText { - color: black; - } color: black; background-color: #c0c0c0; outline: base.em(2px) outset #c3c3c3; + &__titleText { + color: black; + } } .Input { diff --git a/tgui/public/tgui-panel.bundle.css b/tgui/public/tgui-panel.bundle.css index 899c8829ea6..2b40a799211 100644 --- a/tgui/public/tgui-panel.bundle.css +++ b/tgui/public/tgui-panel.bundle.css @@ -1 +1 @@ -html,body{box-sizing:border-box;height:100%;margin:0;font-size:12px}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif}*,*:before,*:after{box-sizing:inherit}h1,h2,h3,h4,h5,h6{display:block;margin:0;padding:6px 0;padding:.5rem 0}h1{font-size:18px;font-size:1.5rem}h2{font-size:16px;font-size:1.333rem}h3{font-size:14px;font-size:1.167rem}h4{font-size:12px;font-size:1rem}td,th{vertical-align:baseline;text-align:left}.candystripe:nth-child(odd){background-color:rgba(0,0,0,.25)}.color-black{color:#1a1a1a!important}.color-white{color:#fff!important}.color-red{color:#df3e3e!important}.color-orange{color:#f37f33!important}.color-yellow{color:#fbda21!important}.color-olive{color:#cbe41c!important}.color-green{color:#25ca4c!important}.color-teal{color:#00d6cc!important}.color-blue{color:#2e93de!important}.color-violet{color:#7349cf!important}.color-purple{color:#ad45d0!important}.color-pink{color:#e34da1!important}.color-brown{color:#b97447!important}.color-grey{color:#848484!important}.color-good{color:#68c22d!important}.color-average{color:#f29a29!important}.color-bad{color:#df3e3e!important}.color-label{color:#8b9bb0!important}.color-gold{color:#f3b22f!important}.color-bg-black{background-color:#000!important}.color-bg-white{background-color:#d9d9d9!important}.color-bg-red{background-color:#bd2020!important}.color-bg-orange{background-color:#d95e0c!important}.color-bg-yellow{background-color:#d9b804!important}.color-bg-olive{background-color:#9aad14!important}.color-bg-green{background-color:#1b9638!important}.color-bg-teal{background-color:#009a93!important}.color-bg-blue{background-color:#1c71b1!important}.color-bg-violet{background-color:#552dab!important}.color-bg-purple{background-color:#8b2baa!important}.color-bg-pink{background-color:#cf2082!important}.color-bg-brown{background-color:#8c5836!important}.color-bg-grey{background-color:#646464!important}.color-bg-good{background-color:#4d9121!important}.color-bg-average{background-color:#cd7a0d!important}.color-bg-bad{background-color:#bd2020!important}.color-bg-label{background-color:#657a94!important}.color-bg-gold{background-color:#d6920c!important}.debug-layout,.debug-layout *:not(g):not(path){color:rgba(255,255,255,.9)!important;background:rgba(0,0,0,0)!important;outline:1px solid rgba(255,255,255,.5)!important;box-shadow:none!important;filter:none!important}.debug-layout:hover,.debug-layout *:not(g):not(path):hover{outline-color:rgba(255,255,255,.8)!important}.outline-dotted{outline-style:dotted!important}.outline-dashed{outline-style:dashed!important}.outline-solid{outline-style:solid!important}.outline-double{outline-style:double!important}.outline-groove{outline-style:groove!important}.outline-ridge{outline-style:ridge!important}.outline-inset{outline-style:inset!important}.outline-outset{outline-style:outset!important}.outline-color-black{outline:.167rem solid #1a1a1a!important}.outline-color-white{outline:.167rem solid #fff!important}.outline-color-red{outline:.167rem solid #df3e3e!important}.outline-color-orange{outline:.167rem solid #f37f33!important}.outline-color-yellow{outline:.167rem solid #fbda21!important}.outline-color-olive{outline:.167rem solid #cbe41c!important}.outline-color-green{outline:.167rem solid #25ca4c!important}.outline-color-teal{outline:.167rem solid #00d6cc!important}.outline-color-blue{outline:.167rem solid #2e93de!important}.outline-color-violet{outline:.167rem solid #7349cf!important}.outline-color-purple{outline:.167rem solid #ad45d0!important}.outline-color-pink{outline:.167rem solid #e34da1!important}.outline-color-brown{outline:.167rem solid #b97447!important}.outline-color-grey{outline:.167rem solid #848484!important}.outline-color-good{outline:.167rem solid #68c22d!important}.outline-color-average{outline:.167rem solid #f29a29!important}.outline-color-bad{outline:.167rem solid #df3e3e!important}.outline-color-label{outline:.167rem solid #8b9bb0!important}.outline-color-gold{outline:.167rem solid #f3b22f!important}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-baseline{text-align:baseline}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-pre{white-space:pre}.text-bold{font-weight:700}.text-italic{font-style:italic}.text-underline{text-decoration:underline}.BlockQuote{color:#8b9bb0;border-left:.1666666667em solid #8b9bb0;padding-left:.5em;margin-bottom:.5em}.BlockQuote:last-child{margin-bottom:0}.Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.Button:last-child{margin-right:0;margin-bottom:0}.Button .fa,.Button .fas,.Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.Button--hasContent .fa,.Button--hasContent .fas,.Button--hasContent .far{margin-right:.25em}.Button--hasContent.Button--iconRight .fa,.Button--hasContent.Button--iconRight .fas,.Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.Button--fluid{display:block;margin-left:0;margin-right:0}.Button--circular{border-radius:50%}.Button--compact{padding:0 .25em;line-height:1.333em}.Button--multiLine{white-space:normal;word-wrap:break-word}.Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.Button--color--black:focus{transition:color .25s,background-color .25s}.Button--color--black:hover{background-color:#101010;color:#fff}.Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.Button--color--white:focus{transition:color .25s,background-color .25s}.Button--color--white:hover{background-color:#f8f8f8;color:#000}.Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--red:focus{transition:color .25s,background-color .25s}.Button--color--red:hover{background-color:#d93f3f;color:#fff}.Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.Button--color--orange:focus{transition:color .25s,background-color .25s}.Button--color--orange:hover{background-color:#ef7e33;color:#fff}.Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--yellow:focus{transition:color .25s,background-color .25s}.Button--color--yellow:hover{background-color:#f5d523;color:#000}.Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.Button--color--olive:focus{transition:color .25s,background-color .25s}.Button--color--olive:hover{background-color:#bdd327;color:#fff}.Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--color--green:focus{transition:color .25s,background-color .25s}.Button--color--green:hover{background-color:#2fb94f;color:#fff}.Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.Button--color--teal:focus{transition:color .25s,background-color .25s}.Button--color--teal:hover{background-color:#10bdb6;color:#fff}.Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.Button--color--blue:focus{transition:color .25s,background-color .25s}.Button--color--blue:hover{background-color:#308fd6;color:#fff}.Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.Button--color--violet:focus{transition:color .25s,background-color .25s}.Button--color--violet:hover{background-color:#7249ca;color:#fff}.Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.Button--color--purple:focus{transition:color .25s,background-color .25s}.Button--color--purple:hover{background-color:#aa46ca;color:#fff}.Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.Button--color--pink:focus{transition:color .25s,background-color .25s}.Button--color--pink:hover{background-color:#e04ca0;color:#fff}.Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.Button--color--brown:focus{transition:color .25s,background-color .25s}.Button--color--brown:hover{background-color:#ae724c;color:#fff}.Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.Button--color--grey:focus{transition:color .25s,background-color .25s}.Button--color--grey:hover{background-color:#818181;color:#fff}.Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.Button--color--good:focus{transition:color .25s,background-color .25s}.Button--color--good:hover{background-color:#67b335;color:#fff}.Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.Button--color--average:focus{transition:color .25s,background-color .25s}.Button--color--average:hover{background-color:#eb972b;color:#fff}.Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--bad:focus{transition:color .25s,background-color .25s}.Button--color--bad:hover{background-color:#d93f3f;color:#fff}.Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.Button--color--label:focus{transition:color .25s,background-color .25s}.Button--color--label:hover{background-color:#8a9aae;color:#fff}.Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.Button--color--gold:focus{transition:color .25s,background-color .25s}.Button--color--gold:hover{background-color:#eeaf30;color:#fff}.Button--color--default{transition:color .1s,background-color .1s;background-color:#3e6189;color:#fff}.Button--color--default:focus{transition:color .25s,background-color .25s}.Button--color--default:hover{background-color:#567daa;color:#fff}.Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--caution:focus{transition:color .25s,background-color .25s}.Button--color--caution:hover{background-color:#f5d523;color:#000}.Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--danger:focus{transition:color .25s,background-color .25s}.Button--color--danger:hover{background-color:#d93f3f;color:#fff}.Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,0);color:rgba(255,255,255,.5)}.Button--color--transparent:focus{transition:color .25s,background-color .25s}.Button--color--transparent:hover{background-color:rgba(50,50,50,.81);color:#fff}.Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,.6);color:rgba(255,255,255,.5)}.Button--color--translucent:focus{transition:color .25s,background-color .25s}.Button--color--translucent:hover{background-color:rgba(54,54,54,.925);color:#fff}.Button--disabled{background-color:#999!important}.Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--selected:focus{transition:color .25s,background-color .25s}.Button--selected:hover{background-color:#2fb94f;color:#fff}.Button--modal{float:right;z-index:1;margin-top:-.5rem}.ColorBox{display:inline-block;width:1em;height:1em;line-height:1em;text-align:center}.Dimmer{display:flex;justify-content:center;align-items:center;position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.75);z-index:1}.Dropdown{position:relative;align-items:center}.Dropdown__control{display:inline-block;align-items:center;font-family:Verdana,sans-serif;font-size:1em;width:8.3333333333em;line-height:1.3333333333em;-ms-user-select:none;user-select:none}.Dropdown__arrow-button{float:right;padding-left:.35em;width:1.2em;height:1.8333333333em;border-left:.0833333333em solid #000;border-left:.0833333333em solid rgba(0,0,0,.25)}.Dropdown__menu{overflow-y:auto;align-items:center;z-index:5;max-height:16.6666666667em;border-radius:0 0 .1666666667em .1666666667em;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75)}.Dropdown__menu-scroll{overflow-y:scroll}.Dropdown__menuentry{padding:.1666666667em .3333333333em;font-family:Verdana,sans-serif;font-size:1em;line-height:1.4166666667em;transition:background-color .1s ease-out}.Dropdown__menuentry.selected{background-color:rgba(255,255,255,.5)!important;transition:background-color 0ms}.Dropdown__menuentry:hover{background-color:rgba(255,255,255,.2);transition:background-color 0ms}.Dropdown__over{top:auto;bottom:100%}.Dropdown__selected-text{display:inline-block;text-overflow:ellipsis;white-space:nowrap;height:1.4166666667em;width:calc(100% - 1.2em);text-align:left;padding-top:2.5px}.Flex{display:-ms-flexbox;display:flex}.Flex--inline{display:inline-flex}.Flex--iefix{display:block}.Flex--iefix.Flex--inline,.Flex__item--iefix{display:inline-block}.Flex--iefix--column>.Flex__item--iefix{display:block}.Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.Knob__popupValue,.Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Knob__popupValue--right{top:.25rem;right:-50%}.Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.Knob__ringTrackPivot{transform:rotate(135deg)}.Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.Knob__ringFillPivot{transform:rotate(135deg)}.Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.Knob--color--white .Knob__ringFill{stroke:#fff}.Knob--color--red .Knob__ringFill{stroke:#df3e3e}.Knob--color--orange .Knob__ringFill{stroke:#f37f33}.Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.Knob--color--green .Knob__ringFill{stroke:#25ca4c}.Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.Knob--color--blue .Knob__ringFill{stroke:#2e93de}.Knob--color--violet .Knob__ringFill{stroke:#7349cf}.Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.Knob--color--pink .Knob__ringFill{stroke:#e34da1}.Knob--color--brown .Knob__ringFill{stroke:#b97447}.Knob--color--grey .Knob__ringFill{stroke:#848484}.Knob--color--good .Knob__ringFill{stroke:#68c22d}.Knob--color--average .Knob__ringFill{stroke:#f29a29}.Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.LabeledList{display:table;width:100%;width:calc(100% + 1em);border-collapse:collapse;border-spacing:0;margin:-.25em -.5em 0;padding:0}.LabeledList__row{display:table-row}.LabeledList__row:last-child .LabeledList__cell{padding-bottom:0}.LabeledList__cell{display:table-cell;margin:0;padding:.25em .5em;border:0;text-align:left;vertical-align:baseline}.LabeledList__label{width:1%;white-space:nowrap;min-width:5em}.LabeledList__buttons{width:.1%;white-space:nowrap;text-align:right;padding-top:.0833333333em;padding-bottom:0}.LabeledList__breakContents{word-break:break-all;word-wrap:break-word}.Modal{background-color:#202020;max-width:calc(100% - 1rem);padding:1rem;scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#000;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.NoticeBox--color--black{color:#fff;background-color:#000}.NoticeBox--color--white{color:#000;background-color:#b3b3b3}.NoticeBox--color--red{color:#fff;background-color:#701f1f}.NoticeBox--color--orange{color:#fff;background-color:#854114}.NoticeBox--color--yellow{color:#000;background-color:#83710d}.NoticeBox--color--olive{color:#000;background-color:#576015}.NoticeBox--color--green{color:#fff;background-color:#174e24}.NoticeBox--color--teal{color:#fff;background-color:#064845}.NoticeBox--color--blue{color:#fff;background-color:#1b4565}.NoticeBox--color--violet{color:#fff;background-color:#3b2864}.NoticeBox--color--purple{color:#fff;background-color:#542663}.NoticeBox--color--pink{color:#fff;background-color:#802257}.NoticeBox--color--brown{color:#fff;background-color:#4c3729}.NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.NoticeBox--color--average{color:#fff;background-color:#7b4e13}.NoticeBox--color--bad{color:#fff;background-color:#701f1f}.NoticeBox--color--label{color:#fff;background-color:#53565a}.NoticeBox--color--gold{color:#fff;background-color:#825d13}.NoticeBox--type--info{color:#fff;background-color:#235982}.NoticeBox--type--success{color:#fff;background-color:#1e662f}.NoticeBox--type--warning{color:#fff;background-color:#a95219}.NoticeBox--type--danger{color:#fff;background-color:#8f2828}.NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.NumberInput--fluid{display:block}.NumberInput__content{margin-left:.5em}.NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.ProgressBar__fill--animated{transition:background-color .5s,width .5s}.ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.ProgressBar--color--default{border:.0833333333em solid #3e6189}.ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.ProgressBar--color--disabled{border:1px solid #999}.ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.ProgressBar--color--black{border:.0833333333em solid #000!important}.ProgressBar--color--black .ProgressBar__fill{background-color:#000}.ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.ProgressBar--color--grey{border:.0833333333em solid #646464!important}.ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--label{border:.0833333333em solid #657a94!important}.ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.Section{position:relative;margin-bottom:.5em;background-color:#131313;box-sizing:border-box}.Section:last-child{margin-bottom:0}.Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.Section__rest{position:relative}.Section__content{padding:.66em .5em}.Section--fitted>.Section__rest>.Section__content{padding:0}.Section--fill{display:flex;flex-direction:column;height:100%}.Section--fill>.Section__rest{flex-grow:1}.Section--fill>.Section__rest>.Section__content{height:100%}.Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.Section--scrollable{overflow-x:hidden;overflow-y:hidden}.Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.Section .Section:first-child{margin-top:-.5em}.Section .Section .Section__titleText{font-size:1.0833333333em}.Section .Section .Section .Section__titleText{font-size:1em}.Slider:not(.Slider__disabled){cursor:e-resize}.Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Divider--horizontal{margin:.5em 0}.Divider--horizontal:not(.Divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Divider--vertical{height:100%;margin:0 .5em}.Divider--vertical:not(.Divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--fill{height:100%}.Stack--horizontal>.Stack__item{margin-left:.5em}.Stack--horizontal>.Stack__item:first-child{margin-left:0}.Stack--vertical>.Stack__item{margin-top:.5em}.Stack--vertical>.Stack__item:first-child{margin-top:0}.Stack--zebra>.Stack__item:nth-child(2n){background-color:#131313}.Stack--horizontal>.Stack__divider:not(.Stack__divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--vertical>.Stack__divider:not(.Stack__divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Table{display:table;width:100%;border-collapse:collapse;border-spacing:0;margin:0}.Table--collapsing{width:auto}.Table__row{display:table-row}.Table__cell{display:table-cell;padding:0 .25em}.Table__cell:first-child{padding-left:0}.Table__cell:last-child{padding-right:0}.Table__row--header .Table__cell,.Table__cell--header{font-weight:700;padding-bottom:.5em}.Table__cell--collapsing{width:1%;white-space:nowrap}.Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#131313}.Tabs--fill{height:100%}.Section .Tabs{background-color:rgba(0,0,0,0)}.Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.Tabs--horizontal:last-child{margin-bottom:0}.Tabs__Tab{flex-grow:0}.Tabs--fluid .Tabs__Tab{flex-grow:1}.Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(255,255,255,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.Tab:not(.Tab--selected):hover{background-color:rgba(255,255,255,.075);transition:background-color 0}.Tab--selected{background-color:rgba(255,255,255,.125);color:#dfe7f0}.Tab__text{flex-grow:1;margin:0 .5em}.Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #d4dfec}.Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.Tabs--vertical .Tab--selected{border-left:.1666666667em solid #d4dfec}.Tab--selected.Tab--color--black{color:#535353}.Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#1a1a1a}.Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#1a1a1a}.Tab--selected.Tab--color--white{color:#fff}.Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#fff}.Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#fff}.Tab--selected.Tab--color--red{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#df3e3e}.Tab--selected.Tab--color--orange{color:#f69f66}.Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#f37f33}.Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#f37f33}.Tab--selected.Tab--color--yellow{color:#fce358}.Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#fbda21}.Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#fbda21}.Tab--selected.Tab--color--olive{color:#d8eb55}.Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#cbe41c}.Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#cbe41c}.Tab--selected.Tab--color--green{color:#53e074}.Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#25ca4c}.Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#25ca4c}.Tab--selected.Tab--color--teal{color:#21fff5}.Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00d6cc}.Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00d6cc}.Tab--selected.Tab--color--blue{color:#62aee6}.Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#2e93de}.Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#2e93de}.Tab--selected.Tab--color--violet{color:#9676db}.Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#7349cf}.Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#7349cf}.Tab--selected.Tab--color--purple{color:#c274db}.Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#ad45d0}.Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#ad45d0}.Tab--selected.Tab--color--pink{color:#ea79b9}.Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#e34da1}.Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#e34da1}.Tab--selected.Tab--color--brown{color:#ca9775}.Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#b97447}.Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#b97447}.Tab--selected.Tab--color--grey{color:#a3a3a3}.Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#848484}.Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#848484}.Tab--selected.Tab--color--good{color:#8cd95a}.Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#68c22d}.Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#68c22d}.Tab--selected.Tab--color--average{color:#f5b35e}.Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#f29a29}.Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#f29a29}.Tab--selected.Tab--color--bad{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#df3e3e}.Tab--selected.Tab--color--label{color:#a8b4c4}.Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#8b9bb0}.Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#8b9bb0}.Tab--selected.Tab--color--gold{color:#f6c563}.Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#f3b22f}.Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#f3b22f}.Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.Input--fluid{display:block;width:auto}.Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input--monospace .Input__input{font-family:Consolas,monospace}.TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.TextArea--fluid{display:block;width:auto;height:auto}.TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.Tooltip{z-index:2;padding:.5em .75em;pointer-events:none;text-align:left;transition:opacity .15s ease-out;background-color:#000;color:#fff;box-shadow:.1em .1em 1.25em -.1em rgba(0,0,0,.5);border-radius:.16em;max-width:20.8333333333em}.Chat{color:#abc6ec}.Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.Chat__badge:before{content:"x"}.Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.Chat__scrollButton{position:fixed;right:2em;bottom:1em}.Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#131313}.Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.Chat__highlight{color:#000}.Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.ChatMessage{word-wrap:break-word}.ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.Ping{position:relative;padding:.125em .25em;border:.0833333333em solid rgba(140,140,140,.5);border-radius:.25em;width:3.75em;text-align:right}.Ping__indicator{content:"";position:absolute;top:.5em;left:.5em;width:.5em;height:.5em;background-color:#888;border-radius:.25em}.Notifications{position:absolute;top:1em;left:.75em;right:2em}.Notification{color:#fff;background-color:#dc143c;padding:.5em;margin:1em 0}.Notification:first-child{margin-top:0}.Notification:last-child{margin-bottom:0}html,body{scrollbar-color:#363636 #181818}.Layout,.Layout *{scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.Layout__content--flexRow{display:flex;flex-flow:row}.Layout__content--flexColumn{display:flex;flex-flow:column}.Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.Layout__content--noMargin{margin:0}.Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#202020;background-image:linear-gradient(to bottom,#202020,#202020)}.Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.Window__contentPadding:after{height:0}.Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(56,56,56,.25);pointer-events:none}.Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.emoji16x16{vertical-align:middle}a{color:#397ea5}a.popt{text-decoration:none}.popup{position:fixed;top:50%;left:50%;background:#ddd}.popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.popup .close:hover{background:#999}.popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.popup input[type=text]:hover,.popup input[type=text]:active,.popup input[type=text]:focus{border-color:green}.popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.popup input[type=submit]:hover,.popup input[type=submit]:focus,.popup input[type=submit]:active{background:#aaa;cursor:pointer}.changeFont{padding:10px}.changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.changeFont a:hover{background:#ccc}.highlightPopup{padding:10px;text-align:center}.highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.highlightPopup input.highlightColor{background-color:#ff0}.highlightPopup input.highlightTermSubmit{margin-top:5px}.contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.contextMenu a:hover{background-color:#ccc}.filterMessages{padding:5px}.filterMessages div{padding:2px 0}.icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.motd{color:#a4bad6;font-family:Verdana,sans-serif;white-space:normal}.motd h1,.motd h2,.motd h3,.motd h4,.motd h5,.motd h6{color:#a4bad6;text-decoration:underline}.motd a,.motd a:link,.motd a:active,.motd a:hover{color:#a4bad6}.italic,.italics,.emote{font-style:italic}.highlight{background:#ff0}h1,h2,h3,h4,h5,h6{color:#a4bad6;font-family:Georgia,Verdana,sans-serif}em{font-style:normal;font-weight:700}.darkmblue{color:#6685f5}.prefix,.ooc{font-weight:700}.looc{color:#69c;font-weight:700}.adminobserverooc{color:#09c;font-weight:700}.adminobserver{color:#960;font-weight:700}.admin{color:#386aff;font-weight:700}.adminsay{color:#9611d4;font-weight:700}.mentorhelp{color:#07b;font-weight:700}.adminhelp{color:#a00;font-weight:700}.playerreply{color:#80b;font-weight:700}.pmsend{color:#6685f5}.debug{color:#6d2f83}.name,.yell{font-weight:700}.siliconsay{font-family:Courier New,Courier,monospace}.radio{color:#20b142}.deptradio{color:#939}.comradio{color:#5f5cff}.syndradio{color:#8f4a4b}.dsquadradio{color:#998599}.resteamradio{color:#18bc46}.airadio{color:#ff5ed7}.centradio{color:#2681a5}.secradio{color:#dd3535}.engradio{color:#feac20}.medradio{color:#00b5ad}.sciradio{color:#c68cfa}.supradio{color:#b88646}.srvradio{color:#bbd164}.proradio{color:#b84f92}.all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.mentor_channel{color:#775bff;font-weight:700}.mentor_channel_admin{color:#a35cff;font-weight:700}.djradio{color:#960}.binaryradio{color:#1b00fb;font-family:Courier New,Courier,monospace}.mommiradio{color:#6685f5}.alert{color:#d82020}h1.alert,h2.alert{color:#a4bad6}.ghostalert{color:#cc00c6;font-style:italic;font-weight:700}.emote{font-style:italic}.selecteddna{color:#a4bad6;background-color:#001b1b}.attack{color:red}.moderate{color:#c00}.disarm{color:#900}.passive{color:#600}.warning{color:#c51e1e;font-style:italic}.boldwarning{color:#c51e1e;font-style:italic;font-weight:700}.danger{color:#c51e1e;font-weight:700}.userdanger{color:#c51e1e;font-weight:700;font-size:120%}.biggerdanger{color:red;font-weight:700;font-size:150%}.info{color:#9ab0ff}.notice{color:#6685f5}.boldnotice{color:#6685f5;font-weight:700}.suicide{color:#ff5050;font-style:italic}.green{color:#03bb39}.pr_announce,.boldannounceic,.boldannounceooc{color:#c51e1e;font-weight:700}.greenannounce{color:#059223;font-weight:700}.terrorspider{color:#cf52fa}.chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.chaosgood{color:#19e0c0;font-weight:700}.chaosneutral{color:#479ac0;font-weight:700}.chaosbad{color:#9047c0;font-weight:700}.chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.sinister{color:purple;font-weight:700;font-style:italic}.medal{font-weight:700}.confirm{color:#00af3b}.rose{color:#ff5050}.sans{font-family:Comic Sans MS,cursive,sans-serif}.wingdings{font-family:Wingdings,Webdings}.robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.ancient{color:#008b8b;font-style:italic}.newscaster{color:#c00}.mod{color:#735638;font-weight:700}.modooc{color:#184880;font-weight:700}.adminmod{color:#f0aa14;font-weight:700}.tajaran{color:#803b56}.skrell{color:#00ced1}.solcom{color:#8282fb}.com_srus{color:#7c4848}.zombie{color:red}.soghun{color:#228b22}.changeling{color:#00b4de}.vox{color:#a0a}.diona{color:#804000;font-weight:700}.trinary{color:#727272}.kidan{color:#c64c05}.slime{color:#07a}.drask{color:#a3d4eb;font-family:Arial Black}.moth{color:#869b29;font-family:Copperplate}.clown{color:red}.vulpkanin{color:#b97a57}.abductor{color:purple;font-style:italic}.mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.rough{font-family:Trebuchet MS,cursive,sans-serif}.say_quote{font-family:Georgia,Verdana,sans-serif}.cult{color:purple;font-weight:700;font-style:italic}.cultspeech{color:#af0000;font-style:italic}.cultitalic{color:#a60000;font-style:italic}.cultlarge{color:#a60000;font-weight:700;font-size:120%}.narsie{color:#a60000;font-weight:700;font-size:300%}.narsiesmall{color:#a60000;font-weight:700;font-size:200%}.interface{color:#9031c4}.big{font-size:150%}.reallybig{font-size:175%}.greentext{color:#0f0;font-size:150%}.redtext{color:red;font-size:150%}.bold{font-weight:700}.his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.center{text-align:center}.red{color:red}.purple{color:#9031c4}.skeleton{color:#c8c8c8;font-weight:700;font-style:italic}.gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.orange{color:orange}.orangei{color:orange;font-style:italic}.orangeb{color:orange;font-weight:700}.resonate{color:#298f85}.healthscan_oxy{color:#5cc9ff}.revennotice{color:#6685f5}.revenboldnotice{color:#6685f5;font-weight:700}.revenbignotice{color:#6685f5;font-weight:700;font-size:120%}.revenminor{color:#823abb}.revenwarning{color:#760fbb;font-style:italic}.revendanger{color:#760fbb;font-weight:700;font-size:120%}.specialnotice{color:#4a6f82;font-weight:700;font-size:120%}.good{color:green}.average{color:#ff8000}.bad{color:red}.italics,.talkinto{font-style:italic}.whisper{font-style:italic;color:#ccc}.recruit{color:#5c00e6;font-weight:700;font-style:italic}.memo{color:#638500;text-align:center}.memoedit{text-align:center;font-size:75%}.connectionClosed,.fatalError{background:red;color:#fff;padding:5px}.connectionClosed.restored{background:green}.internal.boldnshit{color:#6685f5;font-weight:700}.rebooting{background:#2979af;color:#fff;padding:5px}.rebooting a{color:#fff!important;text-decoration-color:#fff!important}.text-normal{font-weight:400;font-style:normal}.hidden{display:none;visibility:hidden}.colossus{color:#7f282a;font-size:175%}.hierophant{color:#609;font-weight:700;font-style:italic}.hierophant_warning{color:#609;font-style:italic}.emoji{max-height:16px;max-width:16px}.adminticket{color:#3daf21;font-weight:700}.adminticketalt{color:#ccb847;font-weight:700}span.body .codephrases{color:#55f}span.body .coderesponses{color:#f33}.announcement h1,.announcement h2{color:#a4bad6;margin:8pt 0;line-height:1.2}.announcement p{color:#d82020;line-height:1.3}.announcement.minor h1{font-size:180%}.announcement.minor h2{font-size:170%}.announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.bolditalics{font-style:italic;font-weight:700}.boxed_message{background:#1b1c1e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.boxed_message.left_align_text{text-align:left}.boxed_message.red_border{background:#1e1b1b;border-color:#a00}.boxed_message.green_border{background:#1b1e1c;border-color:#0f0}.boxed_message.purple_border{background:#1d1c1f;border-color:#8000ff}.boxed_message.notice_border{background:#1b1c1e;border-color:#6685f5}.boxed_message.thick_border{border-width:thick}.oxygen{color:#449dff}.nitrogen{color:#f94541}.carbon_dioxide{color:#ccc}.plasma{color:#eb6b00}.sleeping_agent{color:#f28b89}.agent_b{color:teal}.spyradio{color:#776f96}.sovradio{color:#f7941d}.taipan{color:#ffec8b}.spider_clan{color:#3cfd1e}.event_alpha{color:#88910f}.event_beta{color:#1d83f7}.event_gamma{color:#d46549}.blob{color:#006221;font-weight:700;font-style:italic}.blobteslium_paste{color:#512e89;font-weight:700;font-style:italic}.blobradioactive_gel{color:#2476f0;font-weight:700;font-style:italic}.blobb_sorium{color:olive;font-weight:700;font-style:italic}.blobcryogenic_liquid{color:#8ba6e9;font-weight:700;font-style:italic}.blobkinetic{color:orange;font-weight:700;font-style:italic}.bloblexorin_jelly{color:#00ffc5;font-weight:700;font-style:italic}.blobenvenomed_filaments{color:#9acd32;font-weight:700;font-style:italic}.blobboiling_oil{color:#b68d00;font-weight:700;font-style:italic}.blobripping_tendrils{color:#7f0000;font-weight:700;font-style:italic}.shadowling{color:#a37bb5}.clock{color:#bd8700;font-weight:700;font-style:italic}.clockspeech{color:#996e00;font-style:italic}.clockitalic{color:#bd8700;font-style:italic}.clocklarge{color:#bd8700;font-weight:700;font-size:120%}.ratvar{color:#bd8700;font-weight:700;font-size:300%}.examine{border:1px solid #1c1c1c;padding:10px;margin:2px 10px;background:#252525;color:#fff}.examine a{color:#fff}.examine .info{color:#4450ff}.examine .notice,.examine .boldnotice{color:#6685f5}.examine .deptradio{color:#ad43ab}.adminooc{color:#a0320e;font-weight:700}.deadsay{color:#b800b1}.admin_channel{color:#fcba03}.alien{color:#923492}.noticealien{color:#00a000}.alertalien{color:#00a000;font-weight:700}.dantalion{color:#1a7d5b}.engradio{color:#a66300}.proradio{color:#e3027a}.theme-light .color-black{color:#000!important}.theme-light .color-white{color:#e6e6e6!important}.theme-light .color-red{color:#c82121!important}.theme-light .color-orange{color:#e6630d!important}.theme-light .color-yellow{color:#e5c304!important}.theme-light .color-olive{color:#a3b816!important}.theme-light .color-green{color:#1d9f3b!important}.theme-light .color-teal{color:#00a39c!important}.theme-light .color-blue{color:#1e78bb!important}.theme-light .color-violet{color:#5a30b5!important}.theme-light .color-purple{color:#932eb4!important}.theme-light .color-pink{color:#db228a!important}.theme-light .color-brown{color:#955d39!important}.theme-light .color-grey{color:#e6e6e6!important}.theme-light .color-good{color:#529923!important}.theme-light .color-average{color:#da810e!important}.theme-light .color-bad{color:#c82121!important}.theme-light .color-label{color:#353535!important}.theme-light .color-gold{color:#e39b0d!important}.theme-light .color-bg-black{background-color:#000!important}.theme-light .color-bg-white{background-color:#bfbfbf!important}.theme-light .color-bg-red{background-color:#a61c1c!important}.theme-light .color-bg-orange{background-color:#c0530b!important}.theme-light .color-bg-yellow{background-color:#bfa303!important}.theme-light .color-bg-olive{background-color:#889912!important}.theme-light .color-bg-green{background-color:#188532!important}.theme-light .color-bg-teal{background-color:#008882!important}.theme-light .color-bg-blue{background-color:#19649c!important}.theme-light .color-bg-violet{background-color:#4b2897!important}.theme-light .color-bg-purple{background-color:#7a2696!important}.theme-light .color-bg-pink{background-color:#b61d73!important}.theme-light .color-bg-brown{background-color:#7c4d2f!important}.theme-light .color-bg-grey{background-color:#bfbfbf!important}.theme-light .color-bg-good{background-color:#44801d!important}.theme-light .color-bg-average{background-color:#b56b0b!important}.theme-light .color-bg-bad{background-color:#a61c1c!important}.theme-light .color-bg-label{background-color:#2c2c2c!important}.theme-light .color-bg-gold{background-color:#bd810b!important}.theme-light .Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#fff}.theme-light .Tabs--fill{height:100%}.theme-light .Section .Tabs{background-color:rgba(0,0,0,0)}.theme-light .Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.theme-light .Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.theme-light .Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.theme-light .Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.theme-light .Tabs--horizontal:last-child{margin-bottom:0}.theme-light .Tabs__Tab{flex-grow:0}.theme-light .Tabs--fluid .Tabs__Tab{flex-grow:1}.theme-light .Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(0,0,0,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.theme-light .Tab:not(.Tab--selected):hover{background-color:rgba(0,0,0,.075);transition:background-color 0}.theme-light .Tab--selected{background-color:rgba(0,0,0,.125);color:#404040}.theme-light .Tab__text{flex-grow:1;margin:0 .5em}.theme-light .Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.theme-light .Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.theme-light .Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.theme-light .Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #000}.theme-light .Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.theme-light .Tabs--vertical .Tab--selected{border-left:.1666666667em solid #000}.theme-light .Tab--selected.Tab--color--black{color:#404040}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#000}.theme-light .Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#000}.theme-light .Tab--selected.Tab--color--white{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--red{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--orange{color:#f48942}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#e6630d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#e6630d}.theme-light .Tab--selected.Tab--color--yellow{color:#fcdd33}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#e5c304}.theme-light .Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#e5c304}.theme-light .Tab--selected.Tab--color--olive{color:#d0e732}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#a3b816}.theme-light .Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#a3b816}.theme-light .Tab--selected.Tab--color--green{color:#33da5a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#1d9f3b}.theme-light .Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#1d9f3b}.theme-light .Tab--selected.Tab--color--teal{color:#00faef}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00a39c}.theme-light .Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00a39c}.theme-light .Tab--selected.Tab--color--blue{color:#419ce1}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#1e78bb}.theme-light .Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#1e78bb}.theme-light .Tab--selected.Tab--color--violet{color:#7f58d3}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#5a30b5}.theme-light .Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#5a30b5}.theme-light .Tab--selected.Tab--color--purple{color:#b455d4}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#932eb4}.theme-light .Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#932eb4}.theme-light .Tab--selected.Tab--color--pink{color:#e558a7}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#db228a}.theme-light .Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#db228a}.theme-light .Tab--selected.Tab--color--brown{color:#c0825a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#955d39}.theme-light .Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#955d39}.theme-light .Tab--selected.Tab--color--grey{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--good{color:#77d23b}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#529923}.theme-light .Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#529923}.theme-light .Tab--selected.Tab--color--average{color:#f3a23a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#da810e}.theme-light .Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#da810e}.theme-light .Tab--selected.Tab--color--bad{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--label{color:#686868}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#353535}.theme-light .Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#353535}.theme-light .Tab--selected.Tab--color--gold{color:#f4b73f}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#e39b0d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#e39b0d}.theme-light .Section{position:relative;margin-bottom:.5em;background-color:#fff;box-sizing:border-box}.theme-light .Section:last-child{margin-bottom:0}.theme-light .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #fff}.theme-light .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#000}.theme-light .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-light .Section__rest{position:relative}.theme-light .Section__content{padding:.66em .5em}.theme-light .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-light .Section--fill{display:flex;flex-direction:column;height:100%}.theme-light .Section--fill>.Section__rest{flex-grow:1}.theme-light .Section--fill>.Section__rest>.Section__content{height:100%}.theme-light .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-light .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-light .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-light .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-light .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-light .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-light .Section .Section:first-child{margin-top:-.5em}.theme-light .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-light .Section .Section .Section .Section__titleText{font-size:1em}.theme-light .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-light .Button:last-child{margin-right:0;margin-bottom:0}.theme-light .Button .fa,.theme-light .Button .fas,.theme-light .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-light .Button--hasContent .fa,.theme-light .Button--hasContent .fas,.theme-light .Button--hasContent .far{margin-right:.25em}.theme-light .Button--hasContent.Button--iconRight .fa,.theme-light .Button--hasContent.Button--iconRight .fas,.theme-light .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-light .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-light .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-light .Button--circular{border-radius:50%}.theme-light .Button--compact{padding:0 .25em;line-height:1.333em}.theme-light .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-light .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-light .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--black:hover{background-color:#101010;color:#fff}.theme-light .Button--color--white{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--white:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--red{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--red:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--orange{transition:color .1s,background-color .1s;background-color:#c0530b;color:#fff}.theme-light .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--orange:hover{background-color:#e76d1d;color:#fff}.theme-light .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#bfa303;color:#fff}.theme-light .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--yellow:hover{background-color:#e7c714;color:#fff}.theme-light .Button--color--olive{transition:color .1s,background-color .1s;background-color:#889912;color:#fff}.theme-light .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--olive:hover{background-color:#a9bc25;color:#fff}.theme-light .Button--color--green{transition:color .1s,background-color .1s;background-color:#188532;color:#fff}.theme-light .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--green:hover{background-color:#2ba648;color:#fff}.theme-light .Button--color--teal{transition:color .1s,background-color .1s;background-color:#008882;color:#fff}.theme-light .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--teal:hover{background-color:#10a9a2;color:#fff}.theme-light .Button--color--blue{transition:color .1s,background-color .1s;background-color:#19649c;color:#fff}.theme-light .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--blue:hover{background-color:#2c81c0;color:#fff}.theme-light .Button--color--violet{transition:color .1s,background-color .1s;background-color:#4b2897;color:#fff}.theme-light .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--violet:hover{background-color:#653db9;color:#fff}.theme-light .Button--color--purple{transition:color .1s,background-color .1s;background-color:#7a2696;color:#fff}.theme-light .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--purple:hover{background-color:#9a3bb9;color:#fff}.theme-light .Button--color--pink{transition:color .1s,background-color .1s;background-color:#b61d73;color:#fff}.theme-light .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--pink:hover{background-color:#d93591;color:#fff}.theme-light .Button--color--brown{transition:color .1s,background-color .1s;background-color:#7c4d2f;color:#fff}.theme-light .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--brown:hover{background-color:#9c6745;color:#fff}.theme-light .Button--color--grey{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--grey:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--good{transition:color .1s,background-color .1s;background-color:#44801d;color:#fff}.theme-light .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--good:hover{background-color:#5d9f31;color:#fff}.theme-light .Button--color--average{transition:color .1s,background-color .1s;background-color:#b56b0b;color:#fff}.theme-light .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--average:hover{background-color:#dc891d;color:#fff}.theme-light .Button--color--bad{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--bad:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--label{transition:color .1s,background-color .1s;background-color:#2c2c2c;color:#fff}.theme-light .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--label:hover{background-color:#424242;color:#fff}.theme-light .Button--color--gold{transition:color .1s,background-color .1s;background-color:#bd810b;color:#fff}.theme-light .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--gold:hover{background-color:#e5a11c;color:#fff}.theme-light .Button--color--default{transition:color .1s,background-color .1s;background-color:#bbb;color:#000}.theme-light .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--default:hover{background-color:#e3e3e3;color:#000}.theme-light .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-light .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-light .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-light .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-light .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,0);color:rgba(0,0,0,.5)}.theme-light .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--transparent:hover{background-color:rgba(255,255,255,.81);color:#000}.theme-light .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,.6);color:rgba(0,0,0,.5)}.theme-light .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--translucent:hover{background-color:rgba(253,253,253,.925);color:#000}.theme-light .Button--disabled{background-color:#363636!important}.theme-light .Button--selected{transition:color .1s,background-color .1s;background-color:#0668b8;color:#fff}.theme-light .Button--selected:focus{transition:color .25s,background-color .25s}.theme-light .Button--selected:hover{background-color:#1785df;color:#fff}.theme-light .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-light .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#353535;background-color:#e6e6e6;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-light .NumberInput--fluid{display:block}.theme-light .NumberInput__content{margin-left:.5em}.theme-light .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-light .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #353535;background-color:#353535}.theme-light .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#e6e6e6;color:#000;text-align:right}.theme-light .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#000;background-color:#e6e6e6;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-light .Input--disabled{color:#777;border-color:#000;border-color:rgba(0,0,0,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-light .Input--fluid{display:block;width:auto}.theme-light .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-light .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#000;color:inherit}.theme-light .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-light .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-light .TextArea{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;background-color:#e6e6e6;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-light .TextArea--fluid{display:block;width:auto;height:auto}.theme-light .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-light .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-light .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-light .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-light .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-light .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-light .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-light .Knob__popupValue,.theme-light .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .Knob__popupValue--right{top:.25rem;right:-50%}.theme-light .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-light .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-light .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-light .Knob__ringFillPivot{transform:rotate(135deg)}.theme-light .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-light .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-light .Knob--color--black .Knob__ringFill{stroke:#000}.theme-light .Knob--color--white .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--red .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--orange .Knob__ringFill{stroke:#e6630d}.theme-light .Knob--color--yellow .Knob__ringFill{stroke:#e5c304}.theme-light .Knob--color--olive .Knob__ringFill{stroke:#a3b816}.theme-light .Knob--color--green .Knob__ringFill{stroke:#1d9f3b}.theme-light .Knob--color--teal .Knob__ringFill{stroke:#00a39c}.theme-light .Knob--color--blue .Knob__ringFill{stroke:#1e78bb}.theme-light .Knob--color--violet .Knob__ringFill{stroke:#5a30b5}.theme-light .Knob--color--purple .Knob__ringFill{stroke:#932eb4}.theme-light .Knob--color--pink .Knob__ringFill{stroke:#db228a}.theme-light .Knob--color--brown .Knob__ringFill{stroke:#955d39}.theme-light .Knob--color--grey .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--good .Knob__ringFill{stroke:#529923}.theme-light .Knob--color--average .Knob__ringFill{stroke:#da810e}.theme-light .Knob--color--bad .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--label .Knob__ringFill{stroke:#353535}.theme-light .Knob--color--gold .Knob__ringFill{stroke:#e39b0d}.theme-light .Slider:not(.Slider__disabled){cursor:e-resize}.theme-light .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-light .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #000}.theme-light .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #000}.theme-light .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-light .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-light .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-light .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-light .ProgressBar--color--default{border:.0833333333em solid #bfbfbf}.theme-light .ProgressBar--color--default .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--disabled{border:1px solid #999}.theme-light .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-light .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-light .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-light .ProgressBar--color--white{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--white .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--red{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--red .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--orange{border:.0833333333em solid #c0530b!important}.theme-light .ProgressBar--color--orange .ProgressBar__fill{background-color:#c0530b}.theme-light .ProgressBar--color--yellow{border:.0833333333em solid #bfa303!important}.theme-light .ProgressBar--color--yellow .ProgressBar__fill{background-color:#bfa303}.theme-light .ProgressBar--color--olive{border:.0833333333em solid #889912!important}.theme-light .ProgressBar--color--olive .ProgressBar__fill{background-color:#889912}.theme-light .ProgressBar--color--green{border:.0833333333em solid #188532!important}.theme-light .ProgressBar--color--green .ProgressBar__fill{background-color:#188532}.theme-light .ProgressBar--color--teal{border:.0833333333em solid #008882!important}.theme-light .ProgressBar--color--teal .ProgressBar__fill{background-color:#008882}.theme-light .ProgressBar--color--blue{border:.0833333333em solid #19649c!important}.theme-light .ProgressBar--color--blue .ProgressBar__fill{background-color:#19649c}.theme-light .ProgressBar--color--violet{border:.0833333333em solid #4b2897!important}.theme-light .ProgressBar--color--violet .ProgressBar__fill{background-color:#4b2897}.theme-light .ProgressBar--color--purple{border:.0833333333em solid #7a2696!important}.theme-light .ProgressBar--color--purple .ProgressBar__fill{background-color:#7a2696}.theme-light .ProgressBar--color--pink{border:.0833333333em solid #b61d73!important}.theme-light .ProgressBar--color--pink .ProgressBar__fill{background-color:#b61d73}.theme-light .ProgressBar--color--brown{border:.0833333333em solid #7c4d2f!important}.theme-light .ProgressBar--color--brown .ProgressBar__fill{background-color:#7c4d2f}.theme-light .ProgressBar--color--grey{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--grey .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--good{border:.0833333333em solid #44801d!important}.theme-light .ProgressBar--color--good .ProgressBar__fill{background-color:#44801d}.theme-light .ProgressBar--color--average{border:.0833333333em solid #b56b0b!important}.theme-light .ProgressBar--color--average .ProgressBar__fill{background-color:#b56b0b}.theme-light .ProgressBar--color--bad{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--bad .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--label{border:.0833333333em solid #2c2c2c!important}.theme-light .ProgressBar--color--label .ProgressBar__fill{background-color:#2c2c2c}.theme-light .ProgressBar--color--gold{border:.0833333333em solid #bd810b!important}.theme-light .ProgressBar--color--gold .ProgressBar__fill{background-color:#bd810b}.theme-light .Chat{color:#000}.theme-light .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-light .Chat__badge:before{content:"x"}.theme-light .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-light .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-light .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-light .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#fff}.theme-light .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-light .Chat__highlight{color:#000}.theme-light .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-light .ChatMessage{word-wrap:break-word}.theme-light .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-light .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-light html,.theme-light body{scrollbar-color:#a7a7a7 #f2f2f2}.theme-light .Layout,.theme-light .Layout *{scrollbar-base-color:#f2f2f2;scrollbar-face-color:#d6d6d6;scrollbar-3dlight-color:#eee;scrollbar-highlight-color:#eee;scrollbar-track-color:#f2f2f2;scrollbar-arrow-color:#777;scrollbar-shadow-color:#d6d6d6}.theme-light .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-light .Layout__content--flexRow{display:flex;flex-flow:row}.theme-light .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-light .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-light .Layout__content--noMargin{margin:0}.theme-light .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#000;background-color:#eee;background-image:linear-gradient(to bottom,#eee,#eee)}.theme-light .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-light .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-light .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-light .Window__contentPadding:after{height:0}.theme-light .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-light .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(252,252,252,.25);pointer-events:none}.theme-light .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-light .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-light .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-light .TitleBar{background-color:#eee;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-light .TitleBar__clickable{color:rgba(0,0,0,.5);background-color:#eee;transition:color .25s,background-color .25s}.theme-light .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-light .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(0,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-light .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-light .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-light .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-light .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-light html,.theme-light body{padding:0;margin:0;height:100%;color:#000}.theme-light body{background:#fff;font-family:Verdana,sans-serif;font-size:13px;line-height:1.2;overflow-x:hidden;overflow-y:scroll;word-wrap:break-word}.theme-light img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.theme-light img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.theme-light a{color:#00f}.theme-light a.popt{text-decoration:none}.theme-light .popup{position:fixed;top:50%;left:50%;background:#ddd}.theme-light .popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.theme-light .popup .close:hover{background:#999}.theme-light .popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.theme-light .popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.theme-light .popup input[type=text]:hover,.theme-light .popup input[type=text]:active,.theme-light .popup input[type=text]:focus{border-color:green}.theme-light .popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.theme-light .popup input[type=submit]:hover,.theme-light .popup input[type=submit]:focus,.theme-light .popup input[type=submit]:active{background:#aaa;cursor:pointer}.theme-light .changeFont{padding:10px}.theme-light .changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.theme-light .changeFont a:hover{background:#ccc}.theme-light .highlightPopup{padding:10px;text-align:center}.theme-light .highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.theme-light .highlightPopup input.highlightColor{background-color:#ff0}.theme-light .highlightPopup input.highlightTermSubmit{margin-top:5px}.theme-light .contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.theme-light .contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.theme-light .contextMenu a:hover{background-color:#ccc}.theme-light .filterMessages{padding:5px}.theme-light .filterMessages div{padding:2px 0}.theme-light .icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.theme-light .motd{color:#638500;font-family:Verdana,sans-serif;white-space:normal}.theme-light .motd h1,.theme-light .motd h2,.theme-light .motd h3,.theme-light .motd h4,.theme-light .motd h5,.theme-light .motd h6{color:#638500;text-decoration:underline}.theme-light .motd a,.theme-light .motd a:link,.theme-light .motd a:active,.theme-light .motd a:hover{color:#638500}.theme-light .italic,.theme-light .italics,.theme-light .emote{font-style:italic}.theme-light .highlight{background:#ff0}.theme-light h1,.theme-light h2,.theme-light h3,.theme-light h4,.theme-light h5,.theme-light h6{color:#00f;font-family:Georgia,Verdana,sans-serif}.theme-light em{font-style:normal;font-weight:700}.theme-light .darkmblue{color:#00f}.theme-light .prefix,.theme-light .ooc{font-weight:700}.theme-light .looc{color:#69c;font-weight:700}.theme-light .adminobserverooc{color:#09c;font-weight:700}.theme-light .adminooc{color:#b82e00;font-weight:700}.theme-light .adminobserver{color:#960;font-weight:700}.theme-light .admin{color:#386aff;font-weight:700}.theme-light .adminsay{color:#9611d4;font-weight:700}.theme-light .mentorhelp{color:#07b;font-weight:700}.theme-light .adminhelp{color:#a00;font-weight:700}.theme-light .playerreply{color:#80b;font-weight:700}.theme-light .pmsend{color:#00f}.theme-light .debug{color:#6d2f83}.theme-light .name,.theme-light .yell{font-weight:700}.theme-light .siliconsay{font-family:Courier New,Courier,monospace}.theme-light .deadsay{color:#5c00e6}.theme-light .radio{color:#408010}.theme-light .deptradio{color:#939}.theme-light .comradio{color:#204090}.theme-light .syndradio{color:#6d3f40}.theme-light .dsquadradio{color:#686868}.theme-light .resteamradio{color:#18bc46}.theme-light .airadio{color:#f0f}.theme-light .centradio{color:#5c5c7c}.theme-light .secradio{color:#a30000}.theme-light .engradio{color:#a66300}.theme-light .medradio{color:#009190}.theme-light .sciradio{color:#939}.theme-light .supradio{color:#7f6539}.theme-light .srvradio{color:#80a000}.theme-light .proradio{color:#e3027a}.theme-light .admin_channel{color:#9a04d1;font-weight:700}.theme-light .all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.theme-light .mentor_channel{color:#775bff;font-weight:700}.theme-light .mentor_channel_admin{color:#a35cff;font-weight:700}.theme-light .djradio{color:#630}.theme-light .binaryradio{color:#0b0050;font-family:Courier New,Courier,monospace}.theme-light .mommiradio{color:navy}.theme-light .alert{color:red}.theme-light h1.alert,.theme-light h2.alert{color:#000}.theme-light .ghostalert{color:#5c00e6;font-style:italic;font-weight:700}.theme-light .emote{font-style:italic}.theme-light .selecteddna{color:#fff;background-color:#001b1b}.theme-light .attack{color:red}.theme-light .moderate{color:#c00}.theme-light .disarm{color:#900}.theme-light .passive{color:#600}.theme-light .warning{color:red;font-style:italic}.theme-light .boldwarning{color:red;font-style:italic;font-weight:700}.theme-light .danger{color:red;font-weight:700}.theme-light .userdanger{color:red;font-weight:700;font-size:120%}.theme-light .biggerdanger{color:red;font-weight:700;font-size:150%}.theme-light .info{color:#00c}.theme-light .notice{color:#009}.theme-light .boldnotice{color:#009;font-weight:700}.theme-light .suicide{color:#ff5050;font-style:italic}.theme-light .green{color:#03bb39}.theme-light .pr_announce{color:#228b22;font-weight:700}.theme-light .boldannounceic,.theme-light .boldannounceooc{color:red;font-weight:700}.theme-light .greenannounce{color:#0f0;font-weight:700}.theme-light .alien{color:#543354}.theme-light .noticealien{color:#00c000}.theme-light .alertalien{color:#00c000;font-weight:700}.theme-light .terrorspider{color:#320e32}.theme-light .chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.theme-light .chaosgood{color:#19e0c0;font-weight:700}.theme-light .chaosneutral{color:#479ac0;font-weight:700}.theme-light .chaosbad{color:#9047c0;font-weight:700}.theme-light .chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.theme-light .sinister{color:purple;font-weight:700;font-style:italic}.theme-light .confirm{color:#00af3b}.theme-light .rose{color:#ff5050}.theme-light .sans{font-family:Comic Sans MS,cursive,sans-serif}.theme-light .wingdings{font-family:Wingdings,Webdings}.theme-light .robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.theme-light .ancient{color:#008b8b;font-style:italic}.theme-light .newscaster{color:maroon}.theme-light .mod{color:#735638;font-weight:700}.theme-light .modooc{color:#184880;font-weight:700}.theme-light .adminmod{color:#402a14;font-weight:700}.theme-light .tajaran{color:#803b56}.theme-light .skrell{color:#00ced1}.theme-light .solcom{color:#22228b}.theme-light .com_srus{color:#7c4848}.theme-light .zombie{color:red}.theme-light .soghun{color:#228b22}.theme-light .changeling{color:purple}.theme-light .vox{color:#a0a}.theme-light .diona{color:#804000;font-weight:700}.theme-light .trinary{color:#727272}.theme-light .kidan{color:#664205}.theme-light .slime{color:#07a}.theme-light .drask{color:#a3d4eb;font-family:Arial Black}.theme-light .moth{color:#869b29;font-family:Copperplate}.theme-light .clown{color:red}.theme-light .vulpkanin{color:#b97a57}.theme-light .abductor{color:purple;font-style:italic}.theme-light .mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.theme-light .rough{font-family:Trebuchet MS,cursive,sans-serif}.theme-light .say_quote{font-family:Georgia,Verdana,sans-serif}.theme-light .cult{color:purple;font-weight:700;font-style:italic}.theme-light .cultspeech{color:#7f0000;font-style:italic}.theme-light .cultitalic{color:#960000;font-style:italic}.theme-light .cultlarge{color:#960000;font-weight:700;font-size:120%}.theme-light .narsie{color:#960000;font-weight:700;font-size:300%}.theme-light .narsiesmall{color:#960000;font-weight:700;font-size:200%}.theme-light .interface{color:#303}.theme-light .big{font-size:150%}.theme-light .reallybig{font-size:175%}.theme-light .greentext{color:#0f0;font-size:150%}.theme-light .redtext{color:red;font-size:150%}.theme-light .bold{font-weight:700}.theme-light .his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.theme-light .center{text-align:center}.theme-light .red{color:red}.theme-light .purple{color:#5e2d79}.theme-light .skeleton{color:#585858;font-weight:700;font-style:italic}.theme-light .gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.theme-light .orange{color:orange}.theme-light .orangei{color:orange;font-style:italic}.theme-light .orangeb{color:orange;font-weight:700}.theme-light .resonate{color:#298f85}.theme-light .healthscan_oxy{color:#0074bd}.theme-light .revennotice{color:#1d2953}.theme-light .revenboldnotice{color:#1d2953;font-weight:700}.theme-light .revenbignotice{color:#1d2953;font-weight:700;font-size:120%}.theme-light .revenminor{color:#823abb}.theme-light .revenwarning{color:#760fbb;font-style:italic}.theme-light .revendanger{color:#760fbb;font-weight:700;font-size:120%}.theme-light .specialnoticebold{color:#36525e;font-weight:700;font-size:120%}.theme-light .specialnotice{color:#36525e;font-size:120%}.theme-light .medal{font-weight:700}.theme-light .good{color:green}.theme-light .average{color:#ff8000}.theme-light .bad{color:red}.theme-light .italics,.theme-light .talkinto{font-style:italic}.theme-light .whisper{font-style:italic;color:#333}.theme-light .recruit{color:#5c00e6;font-weight:700;font-style:italic}.theme-light .memo{color:#638500;text-align:center}.theme-light .memoedit{text-align:center;font-size:75%}.theme-light .connectionClosed,.theme-light .fatalError{background:red;color:#fff;padding:5px}.theme-light .connectionClosed.restored{background:green}.theme-light .internal.boldnshit{color:#00f;font-weight:700}.theme-light .rebooting{background:#2979af;color:#fff;padding:5px}.theme-light .rebooting a{color:#fff!important;text-decoration-color:#fff!important}.theme-light .text-normal{font-weight:400;font-style:normal}.theme-light .hidden{display:none;visibility:hidden}.theme-light .colossus{color:#7f282a;font-size:175%}.theme-light .hierophant{color:#609;font-weight:700;font-style:italic}.theme-light .hierophant_warning{color:#609;font-style:italic}.theme-light .emoji{max-height:16px;max-width:16px}.theme-light .adminticket{color:#3e7336;font-weight:700}.theme-light .adminticketalt{color:#014c8a;font-weight:700}.theme-light span.body .codephrases{color:#00f}.theme-light span.body .coderesponses{color:red}.theme-light .announcement h1,.theme-light .announcement h2{color:#000;margin:8pt 0;line-height:1.2}.theme-light .announcement p{color:#d82020;line-height:1.3}.theme-light .announcement.minor h1{font-size:180%}.theme-light .announcement.minor h2{font-size:170%}.theme-light .announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.theme-light .bolditalics{font-style:italic;font-weight:700}.theme-light .boxed_message{background:#f7fcff;border:1px solid #111a26;margin:.5em;padding:.5em .75em;text-align:center}.theme-light .boxed_message.left_align_text{text-align:left}.theme-light .boxed_message.red_border{background:#fff7f7;border-color:#a00}.theme-light .boxed_message.green_border{background:#f7fff7;border-color:#0f0}.theme-light .boxed_message.purple_border{background:#fdf7ff;border-color:#a0f}.theme-light .boxed_message.notice_border{background:#f7fdff;border-color:#0000bf}.theme-light .boxed_message.thick_border{border-width:thick}.theme-light .oxygen{color:#006adb}.theme-light .nitrogen{color:#d00a06}.theme-light .carbon_dioxide{color:#1f1f1f}.theme-light .plasma{color:#853c00}.theme-light .sleeping_agent{color:#e82f2c}.theme-light .agent_b{color:#004d4d}.theme-light .spyradio{color:#776f96}.theme-light .sovradio{color:#f7941d}.theme-light .taipan{color:#998e54}.theme-light .syndiecom{color:#8f4242}.theme-light .spider_clan{color:#044a1b}.theme-light .event_alpha{color:#88910f}.theme-light .event_beta{color:#1d83f7}.theme-light .event_gamma{color:#d46549}.theme-light .blob{color:#006221;font-weight:700;font-style:italic}.theme-light .blobteslium_paste{color:#412968;font-weight:700;font-style:italic}.theme-light .blobradioactive_gel{color:#2476f0;font-weight:700;font-style:italic}.theme-light .blobb_sorium{color:olive;font-weight:700;font-style:italic}.theme-light .blobcryogenic_liquid{color:#8ba6e9;font-weight:700;font-style:italic}.theme-light .blobkinetic{color:orange;font-weight:700;font-style:italic}.theme-light .bloblexorin_jelly{color:#00ffc5;font-weight:700;font-style:italic}.theme-light .blobenvenomed_filaments{color:#9acd32;font-weight:700;font-style:italic}.theme-light .blobboiling_oil{color:#b68d00;font-weight:700;font-style:italic}.theme-light .blobripping_tendrils{color:#7f0000;font-weight:700;font-style:italic}.theme-light .shadowling{color:#3b2769}.theme-light .clock{color:#bd8700;font-weight:700;font-style:italic}.theme-light .clockspeech{color:#996e00;font-style:italic}.theme-light .clockitalic{color:#bd8700;font-style:italic}.theme-light .clocklarge{color:#bd8700;font-weight:700;font-size:120%}.theme-light .ratvar{color:#bd8700;font-weight:700;font-size:300%}.theme-light .examine{border:1px solid #000;padding:5px;margin:2px 10px;background:#d3d3d3}.theme-light .dantalion{color:#1a7d5b}.theme-ntos .color-black{color:#1a1a1a!important}.theme-ntos .color-white{color:#fff!important}.theme-ntos .color-red{color:#df3e3e!important}.theme-ntos .color-orange{color:#f37f33!important}.theme-ntos .color-yellow{color:#fbda21!important}.theme-ntos .color-olive{color:#cbe41c!important}.theme-ntos .color-green{color:#25ca4c!important}.theme-ntos .color-teal{color:#00d6cc!important}.theme-ntos .color-blue{color:#2e93de!important}.theme-ntos .color-violet{color:#7349cf!important}.theme-ntos .color-purple{color:#ad45d0!important}.theme-ntos .color-pink{color:#e34da1!important}.theme-ntos .color-brown{color:#b97447!important}.theme-ntos .color-grey{color:#848484!important}.theme-ntos .color-good{color:#68c22d!important}.theme-ntos .color-average{color:#f29a29!important}.theme-ntos .color-bad{color:#df3e3e!important}.theme-ntos .color-label{color:#8b9bb0!important}.theme-ntos .color-gold{color:#f3b22f!important}.theme-ntos .color-bg-black{background-color:#000!important}.theme-ntos .color-bg-white{background-color:#d9d9d9!important}.theme-ntos .color-bg-red{background-color:#bd2020!important}.theme-ntos .color-bg-orange{background-color:#d95e0c!important}.theme-ntos .color-bg-yellow{background-color:#d9b804!important}.theme-ntos .color-bg-olive{background-color:#9aad14!important}.theme-ntos .color-bg-green{background-color:#1b9638!important}.theme-ntos .color-bg-teal{background-color:#009a93!important}.theme-ntos .color-bg-blue{background-color:#1c71b1!important}.theme-ntos .color-bg-violet{background-color:#552dab!important}.theme-ntos .color-bg-purple{background-color:#8b2baa!important}.theme-ntos .color-bg-pink{background-color:#cf2082!important}.theme-ntos .color-bg-brown{background-color:#8c5836!important}.theme-ntos .color-bg-grey{background-color:#646464!important}.theme-ntos .color-bg-good{background-color:#4d9121!important}.theme-ntos .color-bg-average{background-color:#cd7a0d!important}.theme-ntos .color-bg-bad{background-color:#bd2020!important}.theme-ntos .color-bg-label{background-color:#657a94!important}.theme-ntos .color-bg-gold{background-color:#d6920c!important}.theme-ntos .Section{position:relative;margin-bottom:.5em;background-color:#121922;box-sizing:border-box}.theme-ntos .Section:last-child{margin-bottom:0}.theme-ntos .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.theme-ntos .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-ntos .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-ntos .Section__rest{position:relative}.theme-ntos .Section__content{padding:.66em .5em}.theme-ntos .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-ntos .Section--fill{display:flex;flex-direction:column;height:100%}.theme-ntos .Section--fill>.Section__rest{flex-grow:1}.theme-ntos .Section--fill>.Section__rest>.Section__content{height:100%}.theme-ntos .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-ntos .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-ntos .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-ntos .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-ntos .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-ntos .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-ntos .Section .Section:first-child{margin-top:-.5em}.theme-ntos .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-ntos .Section .Section .Section .Section__titleText{font-size:1em}.theme-ntos .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-ntos .Button:last-child{margin-right:0;margin-bottom:0}.theme-ntos .Button .fa,.theme-ntos .Button .fas,.theme-ntos .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-ntos .Button--hasContent .fa,.theme-ntos .Button--hasContent .fas,.theme-ntos .Button--hasContent .far{margin-right:.25em}.theme-ntos .Button--hasContent.Button--iconRight .fa,.theme-ntos .Button--hasContent.Button--iconRight .fas,.theme-ntos .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-ntos .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-ntos .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-ntos .Button--circular{border-radius:50%}.theme-ntos .Button--compact{padding:0 .25em;line-height:1.333em}.theme-ntos .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-ntos .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-ntos .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--black:hover{background-color:#101010;color:#fff}.theme-ntos .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-ntos .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-ntos .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-ntos .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-ntos .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-ntos .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-ntos .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-ntos .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-ntos .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-ntos .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-ntos .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-ntos .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-ntos .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-ntos .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-ntos .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-ntos .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-ntos .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-ntos .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-ntos .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-ntos .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-ntos .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-ntos .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-ntos .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-ntos .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-ntos .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.theme-ntos .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-ntos .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-ntos .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-ntos .Button--color--default{transition:color .1s,background-color .1s;background-color:#384e68;color:#fff}.theme-ntos .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--default:hover{background-color:#4f6885;color:#fff}.theme-ntos .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--danger:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,0);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--transparent:hover{background-color:rgba(44,57,73,.81);color:#fff}.theme-ntos .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,.6);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--translucent:hover{background-color:rgba(48,61,76,.925);color:#fff}.theme-ntos .Button--disabled{background-color:#999!important}.theme-ntos .Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--selected:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--selected:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-ntos .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-ntos .NumberInput--fluid{display:block}.theme-ntos .NumberInput__content{margin-left:.5em}.theme-ntos .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-ntos .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.theme-ntos .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-ntos .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-ntos .Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-ntos .Input--fluid{display:block;width:auto}.theme-ntos .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-ntos .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-ntos .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-ntos .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-ntos .TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-ntos .TextArea--fluid{display:block;width:auto;height:auto}.theme-ntos .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-ntos .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-ntos .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-ntos .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-ntos .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-ntos .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-ntos .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-ntos .Knob__popupValue,.theme-ntos .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .Knob__popupValue--right{top:.25rem;right:-50%}.theme-ntos .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-ntos .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-ntos .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-ntos .Knob__ringFillPivot{transform:rotate(135deg)}.theme-ntos .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-ntos .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-ntos .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-ntos .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-ntos .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-ntos .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-ntos .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-ntos .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-ntos .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-ntos .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-ntos .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-ntos .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-ntos .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-ntos .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-ntos .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-ntos .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-ntos .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-ntos .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-ntos .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-ntos .Slider:not(.Slider__disabled){cursor:e-resize}.theme-ntos .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-ntos .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-ntos .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-ntos .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-ntos .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-ntos .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-ntos .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-ntos .ProgressBar--color--default{border:.0833333333em solid #3e6189}.theme-ntos .ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.theme-ntos .ProgressBar--color--disabled{border:1px solid #999}.theme-ntos .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-ntos .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-ntos .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-ntos .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-ntos .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-ntos .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-ntos .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-ntos .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-ntos .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-ntos .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-ntos .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-ntos .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-ntos .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-ntos .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-ntos .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-ntos .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-ntos .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-ntos .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-ntos .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-ntos .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-ntos .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-ntos .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-ntos .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-ntos .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-ntos .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-ntos .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-ntos .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-ntos .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-ntos .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-ntos .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-ntos .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-ntos .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-ntos .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-ntos .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-ntos .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-ntos .Chat{color:#abc6ec}.theme-ntos .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-ntos .Chat__badge:before{content:"x"}.theme-ntos .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-ntos .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-ntos .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-ntos .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#121922}.theme-ntos .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-ntos .Chat__highlight{color:#000}.theme-ntos .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-ntos .ChatMessage{word-wrap:break-word}.theme-ntos .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-ntos .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-ntos html,.theme-ntos body{scrollbar-color:#2a3b4f #141d26}.theme-ntos .Layout,.theme-ntos .Layout *{scrollbar-base-color:#141d26;scrollbar-face-color:#2a3b4f;scrollbar-3dlight-color:#1b2633;scrollbar-highlight-color:#1b2633;scrollbar-track-color:#141d26;scrollbar-arrow-color:#7290b4;scrollbar-shadow-color:#2a3b4f}.theme-ntos .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-ntos .Layout__content--flexRow{display:flex;flex-flow:row}.theme-ntos .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-ntos .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-ntos .Layout__content--noMargin{margin:0}.theme-ntos .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#1b2633;background-image:linear-gradient(to bottom,#1b2633,#1b2633)}.theme-ntos .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-ntos .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-ntos .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-ntos .Window__contentPadding:after{height:0}.theme-ntos .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-ntos .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(50,63,78,.25);pointer-events:none}.theme-ntos .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-ntos .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-ntos .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-ntos .TitleBar{background-color:#1b2633;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-ntos .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#1b2633;transition:color .25s,background-color .25s}.theme-ntos .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-ntos .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-ntos .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-ntos .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-ntos .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-ntos .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-ntos .boxed_message{background:#1c242e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-ntos .boxed_message.left_align_text{text-align:left}.theme-ntos .boxed_message.red_border{background:#2e1c1c;border-color:#a00}.theme-ntos .boxed_message.green_border{background:#1c2e22;border-color:#0f0}.theme-ntos .boxed_message.purple_border{background:#221c2e;border-color:#8000ff}.theme-ntos .boxed_message.notice_border{background:#1f2633;border-color:#6685f5}.theme-ntos .boxed_message.thick_border{border-width:thick}.theme-syndicate .color-black{color:#1a1a1a!important}.theme-syndicate .color-white{color:#fff!important}.theme-syndicate .color-red{color:#df3e3e!important}.theme-syndicate .color-orange{color:#f37f33!important}.theme-syndicate .color-yellow{color:#fbda21!important}.theme-syndicate .color-olive{color:#cbe41c!important}.theme-syndicate .color-green{color:#25ca4c!important}.theme-syndicate .color-teal{color:#00d6cc!important}.theme-syndicate .color-blue{color:#2e93de!important}.theme-syndicate .color-violet{color:#7349cf!important}.theme-syndicate .color-purple{color:#ad45d0!important}.theme-syndicate .color-pink{color:#e34da1!important}.theme-syndicate .color-brown{color:#b97447!important}.theme-syndicate .color-grey{color:#848484!important}.theme-syndicate .color-good{color:#68c22d!important}.theme-syndicate .color-average{color:#f29a29!important}.theme-syndicate .color-bad{color:#df3e3e!important}.theme-syndicate .color-label{color:#b89797!important}.theme-syndicate .color-gold{color:#f3b22f!important}.theme-syndicate .color-bg-black{background-color:#000!important}.theme-syndicate .color-bg-white{background-color:#d9d9d9!important}.theme-syndicate .color-bg-red{background-color:#bd2020!important}.theme-syndicate .color-bg-orange{background-color:#d95e0c!important}.theme-syndicate .color-bg-yellow{background-color:#d9b804!important}.theme-syndicate .color-bg-olive{background-color:#9aad14!important}.theme-syndicate .color-bg-green{background-color:#1b9638!important}.theme-syndicate .color-bg-teal{background-color:#009a93!important}.theme-syndicate .color-bg-blue{background-color:#1c71b1!important}.theme-syndicate .color-bg-violet{background-color:#552dab!important}.theme-syndicate .color-bg-purple{background-color:#8b2baa!important}.theme-syndicate .color-bg-pink{background-color:#cf2082!important}.theme-syndicate .color-bg-brown{background-color:#8c5836!important}.theme-syndicate .color-bg-grey{background-color:#646464!important}.theme-syndicate .color-bg-good{background-color:#4d9121!important}.theme-syndicate .color-bg-average{background-color:#cd7a0d!important}.theme-syndicate .color-bg-bad{background-color:#bd2020!important}.theme-syndicate .color-bg-label{background-color:#9d6f6f!important}.theme-syndicate .color-bg-gold{background-color:#d6920c!important}.theme-syndicate .Section{position:relative;margin-bottom:.5em;background-color:#2b0101;box-sizing:border-box}.theme-syndicate .Section:last-child{margin-bottom:0}.theme-syndicate .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #397439}.theme-syndicate .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-syndicate .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-syndicate .Section__rest{position:relative}.theme-syndicate .Section__content{padding:.66em .5em}.theme-syndicate .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-syndicate .Section--fill{display:flex;flex-direction:column;height:100%}.theme-syndicate .Section--fill>.Section__rest{flex-grow:1}.theme-syndicate .Section--fill>.Section__rest>.Section__content{height:100%}.theme-syndicate .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-syndicate .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-syndicate .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-syndicate .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-syndicate .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-syndicate .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-syndicate .Section .Section:first-child{margin-top:-.5em}.theme-syndicate .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-syndicate .Section .Section .Section .Section__titleText{font-size:1em}.theme-syndicate .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-syndicate .Button:last-child{margin-right:0;margin-bottom:0}.theme-syndicate .Button .fa,.theme-syndicate .Button .fas,.theme-syndicate .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-syndicate .Button--hasContent .fa,.theme-syndicate .Button--hasContent .fas,.theme-syndicate .Button--hasContent .far{margin-right:.25em}.theme-syndicate .Button--hasContent.Button--iconRight .fa,.theme-syndicate .Button--hasContent.Button--iconRight .fas,.theme-syndicate .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-syndicate .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-syndicate .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-syndicate .Button--circular{border-radius:50%}.theme-syndicate .Button--compact{padding:0 .25em;line-height:1.333em}.theme-syndicate .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-syndicate .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-syndicate .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--black:hover{background-color:#101010;color:#fff}.theme-syndicate .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-syndicate .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-syndicate .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-syndicate .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-syndicate .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-syndicate .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-syndicate .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-syndicate .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-syndicate .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-syndicate .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-syndicate .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-syndicate .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-syndicate .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-syndicate .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-syndicate .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-syndicate .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-syndicate .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-syndicate .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-syndicate .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-syndicate .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-syndicate .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-syndicate .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-syndicate .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-syndicate .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-syndicate .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-syndicate .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-syndicate .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-syndicate .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-syndicate .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--label{transition:color .1s,background-color .1s;background-color:#9d6f6f;color:#fff}.theme-syndicate .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--label:hover{background-color:#b89696;color:#fff}.theme-syndicate .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-syndicate .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-syndicate .Button--color--default{transition:color .1s,background-color .1s;background-color:#397439;color:#fff}.theme-syndicate .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--default:hover{background-color:#509350;color:#fff}.theme-syndicate .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-syndicate .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-syndicate .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-syndicate .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-syndicate .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,0);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--transparent:hover{background-color:rgba(103,14,14,.81);color:#fff}.theme-syndicate .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,.6);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--translucent:hover{background-color:rgba(105,20,20,.925);color:#fff}.theme-syndicate .Button--disabled{background-color:#363636!important}.theme-syndicate .Button--selected{transition:color .1s,background-color .1s;background-color:#9d0808;color:#fff}.theme-syndicate .Button--selected:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--selected:hover{background-color:#c11919;color:#fff}.theme-syndicate .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-syndicate .NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#fff;background-color:#910101;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.theme-syndicate .NoticeBox--color--black{color:#fff;background-color:#000}.theme-syndicate .NoticeBox--color--white{color:#000;background-color:#b3b3b3}.theme-syndicate .NoticeBox--color--red{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--orange{color:#fff;background-color:#854114}.theme-syndicate .NoticeBox--color--yellow{color:#000;background-color:#83710d}.theme-syndicate .NoticeBox--color--olive{color:#000;background-color:#576015}.theme-syndicate .NoticeBox--color--green{color:#fff;background-color:#174e24}.theme-syndicate .NoticeBox--color--teal{color:#fff;background-color:#064845}.theme-syndicate .NoticeBox--color--blue{color:#fff;background-color:#1b4565}.theme-syndicate .NoticeBox--color--violet{color:#fff;background-color:#3b2864}.theme-syndicate .NoticeBox--color--purple{color:#fff;background-color:#542663}.theme-syndicate .NoticeBox--color--pink{color:#fff;background-color:#802257}.theme-syndicate .NoticeBox--color--brown{color:#fff;background-color:#4c3729}.theme-syndicate .NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.theme-syndicate .NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.theme-syndicate .NoticeBox--color--average{color:#fff;background-color:#7b4e13}.theme-syndicate .NoticeBox--color--bad{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--label{color:#fff;background-color:#635c5c}.theme-syndicate .NoticeBox--color--gold{color:#fff;background-color:#825d13}.theme-syndicate .NoticeBox--type--info{color:#fff;background-color:#235982}.theme-syndicate .NoticeBox--type--success{color:#fff;background-color:#1e662f}.theme-syndicate .NoticeBox--type--warning{color:#fff;background-color:#a95219}.theme-syndicate .NoticeBox--type--danger{color:#fff;background-color:#8f2828}.theme-syndicate .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;color:#87ce87;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-syndicate .NumberInput--fluid{display:block}.theme-syndicate .NumberInput__content{margin-left:.5em}.theme-syndicate .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-syndicate .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #87ce87;background-color:#87ce87}.theme-syndicate .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-syndicate .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-syndicate .Input--disabled{color:#777;border-color:#6b6b6b;border-color:rgba(107,107,107,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-syndicate .Input--fluid{display:block;width:auto}.theme-syndicate .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-syndicate .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-syndicate .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-syndicate .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-syndicate .TextArea{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-syndicate .TextArea--fluid{display:block;width:auto;height:auto}.theme-syndicate .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-syndicate .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-syndicate .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-syndicate .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-syndicate .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-syndicate .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-syndicate .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-syndicate .Knob__popupValue,.theme-syndicate .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .Knob__popupValue--right{top:.25rem;right:-50%}.theme-syndicate .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-syndicate .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-syndicate .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-syndicate .Knob__ringFillPivot{transform:rotate(135deg)}.theme-syndicate .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-syndicate .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-syndicate .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-syndicate .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-syndicate .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-syndicate .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-syndicate .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-syndicate .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-syndicate .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-syndicate .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-syndicate .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-syndicate .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-syndicate .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-syndicate .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-syndicate .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-syndicate .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-syndicate .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-syndicate .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--label .Knob__ringFill{stroke:#b89797}.theme-syndicate .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-syndicate .Slider:not(.Slider__disabled){cursor:e-resize}.theme-syndicate .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-syndicate .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-syndicate .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-syndicate .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,.5);transition:border-color .5s}.theme-syndicate .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-syndicate .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-syndicate .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-syndicate .ProgressBar--color--default{border:.0833333333em solid #306330}.theme-syndicate .ProgressBar--color--default .ProgressBar__fill{background-color:#306330}.theme-syndicate .ProgressBar--color--disabled{border:1px solid #999}.theme-syndicate .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-syndicate .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-syndicate .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-syndicate .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-syndicate .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-syndicate .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-syndicate .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-syndicate .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-syndicate .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-syndicate .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-syndicate .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-syndicate .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-syndicate .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-syndicate .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-syndicate .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-syndicate .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-syndicate .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-syndicate .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-syndicate .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-syndicate .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-syndicate .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-syndicate .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-syndicate .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-syndicate .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-syndicate .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-syndicate .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-syndicate .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-syndicate .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-syndicate .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-syndicate .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-syndicate .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-syndicate .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--label{border:.0833333333em solid #9d6f6f!important}.theme-syndicate .ProgressBar--color--label .ProgressBar__fill{background-color:#9d6f6f}.theme-syndicate .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-syndicate .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-syndicate .Chat{color:#abc6ec}.theme-syndicate .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-syndicate .Chat__badge:before{content:"x"}.theme-syndicate .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-syndicate .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-syndicate .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-syndicate .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#2b0101}.theme-syndicate .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-syndicate .Chat__highlight{color:#000}.theme-syndicate .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-syndicate .ChatMessage{word-wrap:break-word}.theme-syndicate .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-syndicate .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-syndicate html,.theme-syndicate body{scrollbar-color:#770303 #3a0202}.theme-syndicate .Layout,.theme-syndicate .Layout *{scrollbar-base-color:#3a0202;scrollbar-face-color:#770303;scrollbar-3dlight-color:#4d0202;scrollbar-highlight-color:#4d0202;scrollbar-track-color:#3a0202;scrollbar-arrow-color:#fa2d2d;scrollbar-shadow-color:#770303}.theme-syndicate .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-syndicate .Layout__content--flexRow{display:flex;flex-flow:row}.theme-syndicate .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-syndicate .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-syndicate .Layout__content--noMargin{margin:0}.theme-syndicate .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#4d0202;background-image:linear-gradient(to bottom,#4d0202,#4d0202)}.theme-syndicate .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-syndicate .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-syndicate .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-syndicate .Window__contentPadding:after{height:0}.theme-syndicate .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-syndicate .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(108,22,22,.25);pointer-events:none}.theme-syndicate .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-syndicate .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-syndicate .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-syndicate .TitleBar{background-color:#910101;border-bottom:1px solid #161616;box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-syndicate .TitleBar__clickable{color:rgba(255,255,255,.5);background-color:#910101;transition:color .25s,background-color .25s}.theme-syndicate .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-syndicate .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,255,255,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-syndicate .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-syndicate .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-syndicate .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-syndicate .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-syndicate .adminooc{color:#29ccbe}.theme-syndicate .debug{color:#8f39e6}.theme-syndicate .boxed_message{background:rgba(20,20,35,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-syndicate .boxed_message.left_align_text{text-align:left}.theme-syndicate .boxed_message.red_border{background:rgba(0,0,0,.2);border-color:red}.theme-syndicate .boxed_message.green_border{background:rgba(0,75,0,.25);border-color:#0f0}.theme-syndicate .boxed_message.purple_border{background:rgba(25,0,50,.25);border-color:#8000ff}.theme-syndicate .boxed_message.notice_border{background:rgba(0,0,75,.25);border-color:#6685f5}.theme-syndicate .boxed_message.thick_border{border-width:thick}.theme-paradise .color-black{color:#1a1a1a!important}.theme-paradise .color-white{color:#fff!important}.theme-paradise .color-red{color:#df3e3e!important}.theme-paradise .color-orange{color:#f37f33!important}.theme-paradise .color-yellow{color:#fbda21!important}.theme-paradise .color-olive{color:#cbe41c!important}.theme-paradise .color-green{color:#25ca4c!important}.theme-paradise .color-teal{color:#00d6cc!important}.theme-paradise .color-blue{color:#2e93de!important}.theme-paradise .color-violet{color:#7349cf!important}.theme-paradise .color-purple{color:#ad45d0!important}.theme-paradise .color-pink{color:#e34da1!important}.theme-paradise .color-brown{color:#b97447!important}.theme-paradise .color-grey{color:#848484!important}.theme-paradise .color-good{color:#68c22d!important}.theme-paradise .color-average{color:#f29a29!important}.theme-paradise .color-bad{color:#df3e3e!important}.theme-paradise .color-label{color:#b8a497!important}.theme-paradise .color-gold{color:#f3b22f!important}.theme-paradise .color-bg-black{background-color:#000!important}.theme-paradise .color-bg-white{background-color:#d9d9d9!important}.theme-paradise .color-bg-red{background-color:#bd2020!important}.theme-paradise .color-bg-orange{background-color:#d95e0c!important}.theme-paradise .color-bg-yellow{background-color:#d9b804!important}.theme-paradise .color-bg-olive{background-color:#9aad14!important}.theme-paradise .color-bg-green{background-color:#1b9638!important}.theme-paradise .color-bg-teal{background-color:#009a93!important}.theme-paradise .color-bg-blue{background-color:#1c71b1!important}.theme-paradise .color-bg-violet{background-color:#552dab!important}.theme-paradise .color-bg-purple{background-color:#8b2baa!important}.theme-paradise .color-bg-pink{background-color:#cf2082!important}.theme-paradise .color-bg-brown{background-color:#8c5836!important}.theme-paradise .color-bg-grey{background-color:#646464!important}.theme-paradise .color-bg-good{background-color:#4d9121!important}.theme-paradise .color-bg-average{background-color:#cd7a0d!important}.theme-paradise .color-bg-bad{background-color:#bd2020!important}.theme-paradise .color-bg-label{background-color:#9d826f!important}.theme-paradise .color-bg-gold{background-color:#d6920c!important}.theme-paradise .Section{position:relative;margin-bottom:.5em;background-color:#40071a;background-color:rgba(0,0,0,.5);box-sizing:border-box}.theme-paradise .Section:last-child{margin-bottom:0}.theme-paradise .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #208080}.theme-paradise .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-paradise .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-paradise .Section__rest{position:relative}.theme-paradise .Section__content{padding:.66em .5em}.theme-paradise .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-paradise .Section--fill{display:flex;flex-direction:column;height:100%}.theme-paradise .Section--fill>.Section__rest{flex-grow:1}.theme-paradise .Section--fill>.Section__rest>.Section__content{height:100%}.theme-paradise .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-paradise .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-paradise .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-paradise .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-paradise .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-paradise .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-paradise .Section .Section:first-child{margin-top:-.5em}.theme-paradise .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-paradise .Section .Section .Section .Section__titleText{font-size:1em}.theme-paradise .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-paradise .Button:last-child{margin-right:0;margin-bottom:0}.theme-paradise .Button .fa,.theme-paradise .Button .fas,.theme-paradise .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-paradise .Button--hasContent .fa,.theme-paradise .Button--hasContent .fas,.theme-paradise .Button--hasContent .far{margin-right:.25em}.theme-paradise .Button--hasContent.Button--iconRight .fa,.theme-paradise .Button--hasContent.Button--iconRight .fas,.theme-paradise .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-paradise .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-paradise .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-paradise .Button--circular{border-radius:50%}.theme-paradise .Button--compact{padding:0 .25em;line-height:1.333em}.theme-paradise .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-paradise .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-paradise .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--black:hover{background-color:#101010;color:#fff}.theme-paradise .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-paradise .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-paradise .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-paradise .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-paradise .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-paradise .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-paradise .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-paradise .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-paradise .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-paradise .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-paradise .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-paradise .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-paradise .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-paradise .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-paradise .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-paradise .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-paradise .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-paradise .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-paradise .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-paradise .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-paradise .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-paradise .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-paradise .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-paradise .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-paradise .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-paradise .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-paradise .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--label{transition:color .1s,background-color .1s;background-color:#9d826f;color:#fff}.theme-paradise .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--label:hover{background-color:#b8a396;color:#fff}.theme-paradise .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-paradise .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-paradise .Button--color--default{transition:color .1s,background-color .1s;background-color:#208080;color:#fff}.theme-paradise .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--default:hover{background-color:#34a0a0;color:#fff}.theme-paradise .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--danger{transition:color .1s,background-color .1s;background-color:#8c1eff;color:#fff}.theme-paradise .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--danger:hover{background-color:#ae61ff;color:#fff}.theme-paradise .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,0);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--transparent:hover{background-color:rgba(164,27,73,.81);color:#fff}.theme-paradise .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,.6);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--translucent:hover{background-color:rgba(164,32,76,.925);color:#fff}.theme-paradise .Button--disabled{background-color:#999!important}.theme-paradise .Button--selected{transition:color .1s,background-color .1s;background-color:#bf6030;color:#fff}.theme-paradise .Button--selected:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--selected:hover{background-color:#d4835a;color:#fff}.theme-paradise .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-paradise .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;color:#e65c2e;background-color:rgba(0,0,0,.25);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-paradise .NumberInput--fluid{display:block}.theme-paradise .NumberInput__content{margin-left:.5em}.theme-paradise .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-paradise .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #e65c2e;background-color:#e65c2e}.theme-paradise .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,.25);color:#fff;text-align:right}.theme-paradise .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-paradise .Input--disabled{color:#777;border-color:#4a4a4a;border-color:rgba(74,74,74,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-paradise .Input--fluid{display:block;width:auto}.theme-paradise .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-paradise .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-paradise .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-paradise .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-paradise .TextArea{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-paradise .TextArea--fluid{display:block;width:auto;height:auto}.theme-paradise .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-paradise .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-paradise .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-paradise .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-paradise .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-paradise .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-paradise .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-paradise .Knob__popupValue,.theme-paradise .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .Knob__popupValue--right{top:.25rem;right:-50%}.theme-paradise .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-paradise .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-paradise .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-paradise .Knob__ringFillPivot{transform:rotate(135deg)}.theme-paradise .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-paradise .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-paradise .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-paradise .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-paradise .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-paradise .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-paradise .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-paradise .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-paradise .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-paradise .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-paradise .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-paradise .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-paradise .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-paradise .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-paradise .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-paradise .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-paradise .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-paradise .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--label .Knob__ringFill{stroke:#b8a497}.theme-paradise .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-paradise .Slider:not(.Slider__disabled){cursor:e-resize}.theme-paradise .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-paradise .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-paradise .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-paradise .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-paradise .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-paradise .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-paradise .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-paradise .ProgressBar--color--default{border:.0833333333em solid #1b6d6d}.theme-paradise .ProgressBar--color--default .ProgressBar__fill{background-color:#1b6d6d}.theme-paradise .ProgressBar--color--disabled{border:1px solid #999}.theme-paradise .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-paradise .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-paradise .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-paradise .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-paradise .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-paradise .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-paradise .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-paradise .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-paradise .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-paradise .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-paradise .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-paradise .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-paradise .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-paradise .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-paradise .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-paradise .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-paradise .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-paradise .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-paradise .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-paradise .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-paradise .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-paradise .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-paradise .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-paradise .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-paradise .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-paradise .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-paradise .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-paradise .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-paradise .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-paradise .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-paradise .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-paradise .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--label{border:.0833333333em solid #9d826f!important}.theme-paradise .ProgressBar--color--label .ProgressBar__fill{background-color:#9d826f}.theme-paradise .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-paradise .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-paradise .Chat{color:#abc6ec}.theme-paradise .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-paradise .Chat__badge:before{content:"x"}.theme-paradise .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-paradise .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-paradise .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-paradise .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#fff;background-color:#db2828}.theme-paradise .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-paradise .Chat__highlight{color:#000}.theme-paradise .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-paradise .ChatMessage{word-wrap:break-word}.theme-paradise .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-paradise .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-paradise html,.theme-paradise body{scrollbar-color:#cb1551 #680b29}.theme-paradise .Layout,.theme-paradise .Layout *{scrollbar-base-color:#680b29;scrollbar-face-color:#99103d;scrollbar-3dlight-color:#800d33;scrollbar-highlight-color:#800d33;scrollbar-track-color:#680b29;scrollbar-arrow-color:#ea2e6c;scrollbar-shadow-color:#99103d}.theme-paradise .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-paradise .Layout__content--flexRow{display:flex;flex-flow:row}.theme-paradise .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-paradise .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-paradise .Layout__content--noMargin{margin:0}.theme-paradise .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#800d33;background-image:linear-gradient(to bottom,#80014b,#80460d)}.theme-paradise .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-paradise .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-paradise .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-paradise .Window__contentPadding:after{height:0}.theme-paradise .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-paradise .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(166,34,78,.25);pointer-events:none}.theme-paradise .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-paradise .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-paradise .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-paradise .TitleBar{background-color:#800d33;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-paradise .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#800d33;transition:color .25s,background-color .25s}.theme-paradise .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-paradise .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-paradise .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-paradise .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-paradise .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-paradise .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-paradise .adminooc{color:#29ccbe}.theme-paradise .debug{color:#8f39e6}.theme-paradise .boxed_message{background:rgba(0,0,0,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-paradise .boxed_message.left_align_text{text-align:left}.theme-paradise .boxed_message.red_border{background:rgba(0,0,0,.25);border-color:#a00}.theme-paradise .boxed_message.green_border{background:rgba(0,0,0,.25);border-color:#0f0}.theme-paradise .boxed_message.purple_border{background:rgba(0,0,0,.25);border-color:#8000ff}.theme-paradise .boxed_message.notice_border{background:rgba(0,0,0,.25);border-color:#6685f5}.theme-paradise .boxed_message.thick_border{border-width:thick} +html,body{box-sizing:border-box;height:100%;margin:0;font-size:12px}html{overflow:hidden;cursor:default}body{overflow:auto;font-family:Verdana,Geneva,sans-serif}*,*:before,*:after{box-sizing:inherit}h1,h2,h3,h4,h5,h6{display:block;margin:0;padding:6px 0;padding:.5rem 0}h1{font-size:18px;font-size:1.5rem}h2{font-size:16px;font-size:1.333rem}h3{font-size:14px;font-size:1.167rem}h4{font-size:12px;font-size:1rem}td,th{vertical-align:baseline;text-align:left}.candystripe:nth-child(odd){background-color:rgba(0,0,0,.25)}.color-black{color:#1a1a1a!important}.color-white{color:#fff!important}.color-red{color:#df3e3e!important}.color-orange{color:#f37f33!important}.color-yellow{color:#fbda21!important}.color-olive{color:#cbe41c!important}.color-green{color:#25ca4c!important}.color-teal{color:#00d6cc!important}.color-blue{color:#2e93de!important}.color-violet{color:#7349cf!important}.color-purple{color:#ad45d0!important}.color-pink{color:#e34da1!important}.color-brown{color:#b97447!important}.color-grey{color:#848484!important}.color-good{color:#68c22d!important}.color-average{color:#f29a29!important}.color-bad{color:#df3e3e!important}.color-label{color:#8b9bb0!important}.color-gold{color:#f3b22f!important}.color-bg-black{background-color:#000!important}.color-bg-white{background-color:#d9d9d9!important}.color-bg-red{background-color:#bd2020!important}.color-bg-orange{background-color:#d95e0c!important}.color-bg-yellow{background-color:#d9b804!important}.color-bg-olive{background-color:#9aad14!important}.color-bg-green{background-color:#1b9638!important}.color-bg-teal{background-color:#009a93!important}.color-bg-blue{background-color:#1c71b1!important}.color-bg-violet{background-color:#552dab!important}.color-bg-purple{background-color:#8b2baa!important}.color-bg-pink{background-color:#cf2082!important}.color-bg-brown{background-color:#8c5836!important}.color-bg-grey{background-color:#646464!important}.color-bg-good{background-color:#4d9121!important}.color-bg-average{background-color:#cd7a0d!important}.color-bg-bad{background-color:#bd2020!important}.color-bg-label{background-color:#657a94!important}.color-bg-gold{background-color:#d6920c!important}.debug-layout,.debug-layout *:not(g):not(path){color:rgba(255,255,255,.9)!important;background:rgba(0,0,0,0)!important;outline:1px solid rgba(255,255,255,.5)!important;box-shadow:none!important;filter:none!important}.debug-layout:hover,.debug-layout *:not(g):not(path):hover{outline-color:rgba(255,255,255,.8)!important}.outline-dotted{outline-style:dotted!important}.outline-dashed{outline-style:dashed!important}.outline-solid{outline-style:solid!important}.outline-double{outline-style:double!important}.outline-groove{outline-style:groove!important}.outline-ridge{outline-style:ridge!important}.outline-inset{outline-style:inset!important}.outline-outset{outline-style:outset!important}.outline-color-black{outline:.167rem solid #1a1a1a!important}.outline-color-white{outline:.167rem solid #fff!important}.outline-color-red{outline:.167rem solid #df3e3e!important}.outline-color-orange{outline:.167rem solid #f37f33!important}.outline-color-yellow{outline:.167rem solid #fbda21!important}.outline-color-olive{outline:.167rem solid #cbe41c!important}.outline-color-green{outline:.167rem solid #25ca4c!important}.outline-color-teal{outline:.167rem solid #00d6cc!important}.outline-color-blue{outline:.167rem solid #2e93de!important}.outline-color-violet{outline:.167rem solid #7349cf!important}.outline-color-purple{outline:.167rem solid #ad45d0!important}.outline-color-pink{outline:.167rem solid #e34da1!important}.outline-color-brown{outline:.167rem solid #b97447!important}.outline-color-grey{outline:.167rem solid #848484!important}.outline-color-good{outline:.167rem solid #68c22d!important}.outline-color-average{outline:.167rem solid #f29a29!important}.outline-color-bad{outline:.167rem solid #df3e3e!important}.outline-color-label{outline:.167rem solid #8b9bb0!important}.outline-color-gold{outline:.167rem solid #f3b22f!important}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-baseline{text-align:baseline}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-pre{white-space:pre}.text-bold{font-weight:700}.text-italic{font-style:italic}.text-underline{text-decoration:underline}.BlockQuote{color:#8b9bb0;border-left:.1666666667em solid #8b9bb0;padding-left:.5em;margin-bottom:.5em}.BlockQuote:last-child{margin-bottom:0}.Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.Button:last-child{margin-right:0;margin-bottom:0}.Button .fa,.Button .fas,.Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.Button--hasContent .fa,.Button--hasContent .fas,.Button--hasContent .far{margin-right:.25em}.Button--hasContent.Button--iconRight .fa,.Button--hasContent.Button--iconRight .fas,.Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.Button--fluid{display:block;margin-left:0;margin-right:0}.Button--circular{border-radius:50%}.Button--compact{padding:0 .25em;line-height:1.333em}.Button--multiLine{white-space:normal;word-wrap:break-word}.Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.Button--color--black:focus{transition:color .25s,background-color .25s}.Button--color--black:hover{background-color:#101010;color:#fff}.Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.Button--color--white:focus{transition:color .25s,background-color .25s}.Button--color--white:hover{background-color:#f8f8f8;color:#000}.Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--red:focus{transition:color .25s,background-color .25s}.Button--color--red:hover{background-color:#d93f3f;color:#fff}.Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.Button--color--orange:focus{transition:color .25s,background-color .25s}.Button--color--orange:hover{background-color:#ef7e33;color:#fff}.Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--yellow:focus{transition:color .25s,background-color .25s}.Button--color--yellow:hover{background-color:#f5d523;color:#000}.Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.Button--color--olive:focus{transition:color .25s,background-color .25s}.Button--color--olive:hover{background-color:#bdd327;color:#fff}.Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--color--green:focus{transition:color .25s,background-color .25s}.Button--color--green:hover{background-color:#2fb94f;color:#fff}.Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.Button--color--teal:focus{transition:color .25s,background-color .25s}.Button--color--teal:hover{background-color:#10bdb6;color:#fff}.Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.Button--color--blue:focus{transition:color .25s,background-color .25s}.Button--color--blue:hover{background-color:#308fd6;color:#fff}.Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.Button--color--violet:focus{transition:color .25s,background-color .25s}.Button--color--violet:hover{background-color:#7249ca;color:#fff}.Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.Button--color--purple:focus{transition:color .25s,background-color .25s}.Button--color--purple:hover{background-color:#aa46ca;color:#fff}.Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.Button--color--pink:focus{transition:color .25s,background-color .25s}.Button--color--pink:hover{background-color:#e04ca0;color:#fff}.Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.Button--color--brown:focus{transition:color .25s,background-color .25s}.Button--color--brown:hover{background-color:#ae724c;color:#fff}.Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.Button--color--grey:focus{transition:color .25s,background-color .25s}.Button--color--grey:hover{background-color:#818181;color:#fff}.Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.Button--color--good:focus{transition:color .25s,background-color .25s}.Button--color--good:hover{background-color:#67b335;color:#fff}.Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.Button--color--average:focus{transition:color .25s,background-color .25s}.Button--color--average:hover{background-color:#eb972b;color:#fff}.Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--bad:focus{transition:color .25s,background-color .25s}.Button--color--bad:hover{background-color:#d93f3f;color:#fff}.Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.Button--color--label:focus{transition:color .25s,background-color .25s}.Button--color--label:hover{background-color:#8a9aae;color:#fff}.Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.Button--color--gold:focus{transition:color .25s,background-color .25s}.Button--color--gold:hover{background-color:#eeaf30;color:#fff}.Button--color--default{transition:color .1s,background-color .1s;background-color:#3e6189;color:#fff}.Button--color--default:focus{transition:color .25s,background-color .25s}.Button--color--default:hover{background-color:#567daa;color:#fff}.Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.Button--color--caution:focus{transition:color .25s,background-color .25s}.Button--color--caution:hover{background-color:#f5d523;color:#000}.Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.Button--color--danger:focus{transition:color .25s,background-color .25s}.Button--color--danger:hover{background-color:#d93f3f;color:#fff}.Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,0);color:rgba(255,255,255,.5)}.Button--color--transparent:focus{transition:color .25s,background-color .25s}.Button--color--transparent:hover{background-color:rgba(50,50,50,.81);color:#fff}.Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(32,32,32,.6);color:rgba(255,255,255,.5)}.Button--color--translucent:focus{transition:color .25s,background-color .25s}.Button--color--translucent:hover{background-color:rgba(54,54,54,.925);color:#fff}.Button--disabled{background-color:#999!important}.Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.Button--selected:focus{transition:color .25s,background-color .25s}.Button--selected:hover{background-color:#2fb94f;color:#fff}.Button--modal{float:right;z-index:1;margin-top:-.5rem}.ColorBox{display:inline-block;width:1em;height:1em;line-height:1em;text-align:center}.Dimmer{display:flex;justify-content:center;align-items:center;position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(0,0,0,.75);z-index:5}.Dropdown{position:relative;align-items:center}.Dropdown__control{display:inline-block;align-items:center;font-family:Verdana,sans-serif;font-size:1em;width:8.3333333333em;line-height:1.3333333333em;-ms-user-select:none;user-select:none}.Dropdown__arrow-button{float:right;padding-left:.35em;width:1.2em;height:1.8333333333em;border-left:.0833333333em solid #000;border-left:.0833333333em solid rgba(0,0,0,.25)}.Dropdown__menu{overflow-y:auto;align-items:center;z-index:5;max-height:16.6666666667em;border-radius:0 0 .1666666667em .1666666667em;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75)}.Dropdown__menu-scroll{overflow-y:scroll}.Dropdown__menuentry{padding:.1666666667em .3333333333em;font-family:Verdana,sans-serif;font-size:1em;line-height:1.4166666667em;transition:background-color .1s ease-out}.Dropdown__menuentry.selected{background-color:rgba(255,255,255,.5)!important;transition:background-color 0ms}.Dropdown__menuentry:hover{background-color:rgba(255,255,255,.2);transition:background-color 0ms}.Dropdown__over{top:auto;bottom:100%}.Dropdown__selected-text{display:inline-block;text-overflow:ellipsis;white-space:nowrap;height:1.4166666667em;width:calc(100% - 1.2em);text-align:left;padding-top:2.5px}.Flex{display:-ms-flexbox;display:flex}.Flex--inline{display:inline-flex}.Flex--iefix{display:block}.Flex--iefix.Flex--inline,.Flex__item--iefix{display:inline-block}.Flex--iefix--column>.Flex__item--iefix{display:block}.Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.Knob__popupValue,.Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Knob__popupValue--right{top:.25rem;right:-50%}.Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.Knob__ringTrackPivot{transform:rotate(135deg)}.Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.Knob__ringFillPivot{transform:rotate(135deg)}.Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.Knob--color--white .Knob__ringFill{stroke:#fff}.Knob--color--red .Knob__ringFill{stroke:#df3e3e}.Knob--color--orange .Knob__ringFill{stroke:#f37f33}.Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.Knob--color--green .Knob__ringFill{stroke:#25ca4c}.Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.Knob--color--blue .Knob__ringFill{stroke:#2e93de}.Knob--color--violet .Knob__ringFill{stroke:#7349cf}.Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.Knob--color--pink .Knob__ringFill{stroke:#e34da1}.Knob--color--brown .Knob__ringFill{stroke:#b97447}.Knob--color--grey .Knob__ringFill{stroke:#848484}.Knob--color--good .Knob__ringFill{stroke:#68c22d}.Knob--color--average .Knob__ringFill{stroke:#f29a29}.Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.LabeledList{display:table;width:100%;width:calc(100% + 1em);border-collapse:collapse;border-spacing:0;margin:-.25em -.5em 0;padding:0}.LabeledList__row{display:table-row}.LabeledList__row:last-child .LabeledList__cell{padding-bottom:0}.LabeledList__cell{display:table-cell;margin:0;padding:.25em .5em;border:0;text-align:left;vertical-align:baseline}.LabeledList__label{width:1%;white-space:nowrap;min-width:5em}.LabeledList__buttons{width:.1%;white-space:nowrap;text-align:right;padding-top:.0833333333em;padding-bottom:0}.LabeledList__breakContents{word-break:break-all;word-wrap:break-word}.Modal{background-color:#202020;max-width:calc(100% - 1rem);padding:1rem;scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#000;background-color:#bb9b68;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.NoticeBox--color--black{color:#fff;background-color:#000}.NoticeBox--color--white{color:#000;background-color:#b3b3b3}.NoticeBox--color--red{color:#fff;background-color:#701f1f}.NoticeBox--color--orange{color:#fff;background-color:#854114}.NoticeBox--color--yellow{color:#000;background-color:#83710d}.NoticeBox--color--olive{color:#000;background-color:#576015}.NoticeBox--color--green{color:#fff;background-color:#174e24}.NoticeBox--color--teal{color:#fff;background-color:#064845}.NoticeBox--color--blue{color:#fff;background-color:#1b4565}.NoticeBox--color--violet{color:#fff;background-color:#3b2864}.NoticeBox--color--purple{color:#fff;background-color:#542663}.NoticeBox--color--pink{color:#fff;background-color:#802257}.NoticeBox--color--brown{color:#fff;background-color:#4c3729}.NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.NoticeBox--color--average{color:#fff;background-color:#7b4e13}.NoticeBox--color--bad{color:#fff;background-color:#701f1f}.NoticeBox--color--label{color:#fff;background-color:#53565a}.NoticeBox--color--gold{color:#fff;background-color:#825d13}.NoticeBox--type--info{color:#fff;background-color:#235982}.NoticeBox--type--success{color:#fff;background-color:#1e662f}.NoticeBox--type--warning{color:#fff;background-color:#a95219}.NoticeBox--type--danger{color:#fff;background-color:#8f2828}.NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.NumberInput--fluid{display:block}.NumberInput__content{margin-left:.5em}.NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.ProgressBar__fill--animated{transition:background-color .5s,width .5s}.ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.ProgressBar--color--default{border:.0833333333em solid #3e6189}.ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.ProgressBar--color--disabled{border:1px solid #999}.ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.ProgressBar--color--black{border:.0833333333em solid #000!important}.ProgressBar--color--black .ProgressBar__fill{background-color:#000}.ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.ProgressBar--color--grey{border:.0833333333em solid #646464!important}.ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.ProgressBar--color--label{border:.0833333333em solid #657a94!important}.ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.Section{position:relative;margin-bottom:.5em;background-color:#131313;box-sizing:border-box}.Section:last-child{margin-bottom:0}.Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.Section__rest{position:relative}.Section__content{padding:.66em .5em}.Section--fitted>.Section__rest>.Section__content{padding:0}.Section--fill{display:flex;flex-direction:column;height:100%}.Section--fill>.Section__rest{flex-grow:1}.Section--fill>.Section__rest>.Section__content{height:100%}.Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.Section--scrollable{overflow-x:hidden;overflow-y:hidden}.Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.Section .Section:first-child{margin-top:-.5em}.Section .Section .Section__titleText{font-size:1.0833333333em}.Section .Section .Section .Section__titleText{font-size:1em}.Slider:not(.Slider__disabled){cursor:e-resize}.Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.Divider--horizontal{margin:.5em 0}.Divider--horizontal:not(.Divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Divider--vertical{height:100%;margin:0 .5em}.Divider--vertical:not(.Divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--fill{height:100%}.Stack--horizontal>.Stack__item{margin-left:.5em}.Stack--horizontal>.Stack__item:first-child{margin-left:0}.Stack--vertical>.Stack__item{margin-top:.5em}.Stack--vertical>.Stack__item:first-child{margin-top:0}.Stack--zebra>.Stack__item:nth-child(2n){background-color:#131313}.Stack--horizontal>.Stack__divider:not(.Stack__divider--hidden){border-left:.1666666667em solid rgba(255,255,255,.1)}.Stack--vertical>.Stack__divider:not(.Stack__divider--hidden){border-top:.1666666667em solid rgba(255,255,255,.1)}.Table{display:table;width:100%;border-collapse:collapse;border-spacing:0;margin:0}.Table--collapsing{width:auto}.Table__row{display:table-row}.Table__cell{display:table-cell;padding:0 .25em}.Table__cell:first-child{padding-left:0}.Table__cell:last-child{padding-right:0}.Table__row--header .Table__cell,.Table__cell--header{font-weight:700;padding-bottom:.5em}.Table__cell--collapsing{width:1%;white-space:nowrap}.Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#131313}.Tabs--fill{height:100%}.Section .Tabs{background-color:rgba(0,0,0,0)}.Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.Tabs--horizontal:last-child{margin-bottom:0}.Tabs__Tab{flex-grow:0}.Tabs--fluid .Tabs__Tab{flex-grow:1}.Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(255,255,255,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.Tab:not(.Tab--selected):hover{background-color:rgba(255,255,255,.075);transition:background-color 0}.Tab--selected{background-color:rgba(255,255,255,.125);color:#dfe7f0}.Tab__text{flex-grow:1;margin:0 .5em}.Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #d4dfec}.Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.Tabs--vertical .Tab--selected{border-left:.1666666667em solid #d4dfec}.Tab--selected.Tab--color--black{color:#535353}.Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#1a1a1a}.Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#1a1a1a}.Tab--selected.Tab--color--white{color:#fff}.Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#fff}.Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#fff}.Tab--selected.Tab--color--red{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#df3e3e}.Tab--selected.Tab--color--orange{color:#f69f66}.Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#f37f33}.Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#f37f33}.Tab--selected.Tab--color--yellow{color:#fce358}.Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#fbda21}.Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#fbda21}.Tab--selected.Tab--color--olive{color:#d8eb55}.Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#cbe41c}.Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#cbe41c}.Tab--selected.Tab--color--green{color:#53e074}.Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#25ca4c}.Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#25ca4c}.Tab--selected.Tab--color--teal{color:#21fff5}.Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00d6cc}.Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00d6cc}.Tab--selected.Tab--color--blue{color:#62aee6}.Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#2e93de}.Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#2e93de}.Tab--selected.Tab--color--violet{color:#9676db}.Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#7349cf}.Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#7349cf}.Tab--selected.Tab--color--purple{color:#c274db}.Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#ad45d0}.Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#ad45d0}.Tab--selected.Tab--color--pink{color:#ea79b9}.Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#e34da1}.Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#e34da1}.Tab--selected.Tab--color--brown{color:#ca9775}.Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#b97447}.Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#b97447}.Tab--selected.Tab--color--grey{color:#a3a3a3}.Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#848484}.Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#848484}.Tab--selected.Tab--color--good{color:#8cd95a}.Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#68c22d}.Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#68c22d}.Tab--selected.Tab--color--average{color:#f5b35e}.Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#f29a29}.Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#f29a29}.Tab--selected.Tab--color--bad{color:#e76e6e}.Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#df3e3e}.Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#df3e3e}.Tab--selected.Tab--color--label{color:#a8b4c4}.Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#8b9bb0}.Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#8b9bb0}.Tab--selected.Tab--color--gold{color:#f6c563}.Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#f3b22f}.Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#f3b22f}.Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.Input--fluid{display:block;width:auto}.Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.Input--monospace .Input__input{font-family:Consolas,monospace}.TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.TextArea--fluid{display:block;width:auto;height:auto}.TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.Tooltip{z-index:999;padding:.5em .75em;pointer-events:none;text-align:left;transition:opacity .15s ease-out;background-color:#000;color:#fff;box-shadow:.1em .1em 1.25em -.1em rgba(0,0,0,.5);border-radius:.16em;max-width:20.8333333333em}.Chat{color:#abc6ec}.Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.Chat__badge:before{content:"x"}.Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.Chat__scrollButton{position:fixed;right:2em;bottom:1em}.Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#131313}.Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.Chat__highlight{color:#000}.Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.ChatMessage{word-wrap:break-word}.ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.Ping{position:relative;padding:.125em .25em;border:.0833333333em solid rgba(140,140,140,.5);border-radius:.25em;width:3.75em;text-align:right}.Ping__indicator{content:"";position:absolute;top:.5em;left:.5em;width:.5em;height:.5em;background-color:#888;border-radius:.25em}.Notifications{position:absolute;top:1em;left:.75em;right:2em}.Notification{color:#fff;background-color:#dc143c;padding:.5em;margin:1em 0}.Notification:first-child{margin-top:0}.Notification:last-child{margin-bottom:0}html,body{scrollbar-color:#363636 #181818}.Layout,.Layout *{scrollbar-base-color:#181818;scrollbar-face-color:#363636;scrollbar-3dlight-color:#202020;scrollbar-highlight-color:#202020;scrollbar-track-color:#181818;scrollbar-arrow-color:#909090;scrollbar-shadow-color:#363636}.Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.Layout__content--flexRow{display:flex;flex-flow:row}.Layout__content--flexColumn{display:flex;flex-flow:column}.Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.Layout__content--noMargin{margin:0}.Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#202020;background-image:linear-gradient(to bottom,#202020,#202020)}.Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.Window__contentPadding:after{height:0}.Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(56,56,56,.25);pointer-events:none}.Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.emoji16x16{vertical-align:middle}a{color:#397ea5}a.popt{text-decoration:none}.popup{position:fixed;top:50%;left:50%;background:#ddd}.popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.popup .close:hover{background:#999}.popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.popup input[type=text]:hover,.popup input[type=text]:active,.popup input[type=text]:focus{border-color:green}.popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.popup input[type=submit]:hover,.popup input[type=submit]:focus,.popup input[type=submit]:active{background:#aaa;cursor:pointer}.changeFont{padding:10px}.changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.changeFont a:hover{background:#ccc}.highlightPopup{padding:10px;text-align:center}.highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.highlightPopup input.highlightColor{background-color:#ff0}.highlightPopup input.highlightTermSubmit{margin-top:5px}.contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.contextMenu a:hover{background-color:#ccc}.filterMessages{padding:5px}.filterMessages div{padding:2px 0}.icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.motd{color:#a4bad6;font-family:Verdana,sans-serif;white-space:normal}.motd h1,.motd h2,.motd h3,.motd h4,.motd h5,.motd h6{color:#a4bad6;text-decoration:underline}.motd a,.motd a:link,.motd a:active,.motd a:hover{color:#a4bad6}.italic,.italics,.emote{font-style:italic}.highlight{background:#ff0}h1,h2,h3,h4,h5,h6{color:#a4bad6;font-family:Georgia,Verdana,sans-serif}em{font-style:normal;font-weight:700}.darkmblue{color:#6685f5}.prefix,.ooc{font-weight:700}.looc{color:#69c;font-weight:700}.adminobserverooc{color:#09c;font-weight:700}.adminobserver{color:#960;font-weight:700}.admin{color:#386aff;font-weight:700}.adminsay{color:#9611d4;font-weight:700}.mentorhelp{color:#07b;font-weight:700}.adminhelp{color:#a00;font-weight:700}.playerreply{color:#80b;font-weight:700}.pmsend{color:#6685f5}.debug{color:#6d2f83}.name,.yell{font-weight:700}.siliconsay{font-family:Courier New,Courier,monospace}.radio{color:#20b142}.deptradio{color:#939}.comradio{color:#5f5cff}.syndradio{color:#8f4a4b}.dsquadradio{color:#998599}.resteamradio{color:#18bc46}.airadio{color:#ff5ed7}.centradio{color:#2681a5}.secradio{color:#dd3535}.engradio{color:#feac20}.medradio{color:#00b5ad}.sciradio{color:#c68cfa}.supradio{color:#b88646}.srvradio{color:#bbd164}.proradio{color:#b84f92}.all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.mentor_channel{color:#775bff;font-weight:700}.mentor_channel_admin{color:#a35cff;font-weight:700}.djradio{color:#960}.binaryradio{color:#1b00fb;font-family:Courier New,Courier,monospace}.mommiradio{color:#6685f5}.alert{color:#d82020}h1.alert,h2.alert{color:#a4bad6}.ghostalert{color:#cc00c6;font-style:italic;font-weight:700}.emote{font-style:italic}.selecteddna{color:#a4bad6;background-color:#001b1b}.attack{color:red}.moderate{color:#c00}.disarm{color:#900}.passive{color:#600}.warning{color:#c51e1e;font-style:italic}.boldwarning{color:#c51e1e;font-style:italic;font-weight:700}.danger{color:#c51e1e;font-weight:700}.userdanger{color:#c51e1e;font-weight:700;font-size:120%}.biggerdanger{color:red;font-weight:700;font-size:150%}.info{color:#9ab0ff}.notice{color:#6685f5}.boldnotice{color:#6685f5;font-weight:700}.suicide{color:#ff5050;font-style:italic}.green{color:#03bb39}.pr_announce,.boldannounceic,.boldannounceooc{color:#c51e1e;font-weight:700}.greenannounce{color:#059223;font-weight:700}.terrorspider{color:#cf52fa}.chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.chaosgood{color:#19e0c0;font-weight:700}.chaosneutral{color:#479ac0;font-weight:700}.chaosbad{color:#9047c0;font-weight:700}.chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.sinister{color:purple;font-weight:700;font-style:italic}.medal{font-weight:700}.confirm{color:#00af3b}.rose{color:#ff5050}.sans{font-family:Comic Sans MS,cursive,sans-serif}.wingdings{font-family:Wingdings,Webdings}.robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.ancient{color:#008b8b;font-style:italic}.newscaster{color:#c00}.mod{color:#735638;font-weight:700}.modooc{color:#184880;font-weight:700}.adminmod{color:#f0aa14;font-weight:700}.tajaran{color:#803b56}.skrell{color:#00ced1}.solcom{color:#8282fb}.com_srus{color:#7c4848}.zombie{color:red}.soghun{color:#228b22}.changeling{color:#00b4de}.vox{color:#a0a}.diona{color:#804000;font-weight:700}.trinary{color:#727272}.kidan{color:#c64c05}.slime{color:#07a}.drask{color:#a3d4eb;font-family:Arial Black}.moth{color:#869b29;font-family:Copperplate}.clown{color:red}.vulpkanin{color:#b97a57}.abductor{color:purple;font-style:italic}.mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.rough{font-family:Trebuchet MS,cursive,sans-serif}.say_quote{font-family:Georgia,Verdana,sans-serif}.cult{color:purple;font-weight:700;font-style:italic}.cultspeech{color:#af0000;font-style:italic}.cultitalic{color:#a60000;font-style:italic}.cultlarge{color:#a60000;font-weight:700;font-size:120%}.narsie{color:#a60000;font-weight:700;font-size:300%}.narsiesmall{color:#a60000;font-weight:700;font-size:200%}.interface{color:#9031c4}.big{font-size:150%}.reallybig{font-size:175%}.greentext{color:#0f0;font-size:150%}.redtext{color:red;font-size:150%}.bold{font-weight:700}.his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.center{text-align:center}.red{color:red}.purple{color:#9031c4}.skeleton{color:#c8c8c8;font-weight:700;font-style:italic}.gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.orange{color:orange}.orangei{color:orange;font-style:italic}.orangeb{color:orange;font-weight:700}.resonate{color:#298f85}.healthscan_oxy{color:#5cc9ff}.revennotice{color:#6685f5}.revenboldnotice{color:#6685f5;font-weight:700}.revenbignotice{color:#6685f5;font-weight:700;font-size:120%}.revenminor{color:#823abb}.revenwarning{color:#760fbb;font-style:italic}.revendanger{color:#760fbb;font-weight:700;font-size:120%}.specialnotice{color:#4a6f82;font-weight:700;font-size:120%}.good{color:green}.average{color:#ff8000}.bad{color:red}.italics,.talkinto{font-style:italic}.whisper{font-style:italic;color:#ccc}.recruit{color:#5c00e6;font-weight:700;font-style:italic}.memo{color:#638500;text-align:center}.memoedit{text-align:center;font-size:75%}.connectionClosed,.fatalError{background:red;color:#fff;padding:5px}.connectionClosed.restored{background:green}.internal.boldnshit{color:#6685f5;font-weight:700}.rebooting{background:#2979af;color:#fff;padding:5px}.rebooting a{color:#fff!important;text-decoration-color:#fff!important}.text-normal{font-weight:400;font-style:normal}.hidden{display:none;visibility:hidden}.colossus{color:#7f282a;font-size:175%}.hierophant{color:#609;font-weight:700;font-style:italic}.hierophant_warning{color:#609;font-style:italic}.emoji{max-height:16px;max-width:16px}.adminticket{color:#3daf21;font-weight:700}.adminticketalt{color:#ccb847;font-weight:700}span.body .codephrases{color:#55f}span.body .coderesponses{color:#f33}.announcement h1,.announcement h2{color:#a4bad6;margin:8pt 0;line-height:1.2}.announcement p{color:#d82020;line-height:1.3}.announcement.minor h1{font-size:180%}.announcement.minor h2{font-size:170%}.announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.bolditalics{font-style:italic;font-weight:700}.boxed_message{background:#1b1c1e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.boxed_message.left_align_text{text-align:left}.boxed_message.red_border{background:#1e1b1b;border-color:#a00}.boxed_message.green_border{background:#1b1e1c;border-color:#0f0}.boxed_message.purple_border{background:#1d1c1f;border-color:#8000ff}.boxed_message.notice_border{background:#1b1c1e;border-color:#6685f5}.boxed_message.thick_border{border-width:thick}.oxygen{color:#449dff}.nitrogen{color:#f94541}.carbon_dioxide{color:#ccc}.plasma{color:#eb6b00}.sleeping_agent{color:#f28b89}.agent_b{color:teal}.spyradio{color:#776f96}.sovradio{color:#f7941d}.taipan{color:#ffec8b}.spider_clan{color:#3cfd1e}.event_alpha{color:#88910f}.event_beta{color:#1d83f7}.event_gamma{color:#d46549}.blob{color:#006221;font-weight:700;font-style:italic}.blobteslium_paste{color:#512e89;font-weight:700;font-style:italic}.blobradioactive_gel{color:#2476f0;font-weight:700;font-style:italic}.blobb_sorium{color:olive;font-weight:700;font-style:italic}.blobcryogenic_liquid{color:#8ba6e9;font-weight:700;font-style:italic}.blobkinetic{color:orange;font-weight:700;font-style:italic}.bloblexorin_jelly{color:#00ffc5;font-weight:700;font-style:italic}.blobenvenomed_filaments{color:#9acd32;font-weight:700;font-style:italic}.blobboiling_oil{color:#b68d00;font-weight:700;font-style:italic}.blobripping_tendrils{color:#7f0000;font-weight:700;font-style:italic}.shadowling{color:#a37bb5}.clock{color:#bd8700;font-weight:700;font-style:italic}.clockspeech{color:#996e00;font-style:italic}.clockitalic{color:#bd8700;font-style:italic}.clocklarge{color:#bd8700;font-weight:700;font-size:120%}.ratvar{color:#bd8700;font-weight:700;font-size:300%}.examine{border:1px solid #1c1c1c;padding:10px;margin:2px 10px;background:#252525;color:#fff}.examine a{color:#fff}.examine .info{color:#4450ff}.examine .notice,.examine .boldnotice{color:#6685f5}.examine .deptradio{color:#ad43ab}.adminooc{color:#a0320e;font-weight:700}.deadsay{color:#b800b1}.admin_channel{color:#fcba03}.alien{color:#923492}.noticealien{color:#00a000}.alertalien{color:#00a000;font-weight:700}.dantalion{color:#1a7d5b}.engradio{color:#a66300}.proradio{color:#e3027a}.theme-light .color-black{color:#000!important}.theme-light .color-white{color:#e6e6e6!important}.theme-light .color-red{color:#c82121!important}.theme-light .color-orange{color:#e6630d!important}.theme-light .color-yellow{color:#e5c304!important}.theme-light .color-olive{color:#a3b816!important}.theme-light .color-green{color:#1d9f3b!important}.theme-light .color-teal{color:#00a39c!important}.theme-light .color-blue{color:#1e78bb!important}.theme-light .color-violet{color:#5a30b5!important}.theme-light .color-purple{color:#932eb4!important}.theme-light .color-pink{color:#db228a!important}.theme-light .color-brown{color:#955d39!important}.theme-light .color-grey{color:#e6e6e6!important}.theme-light .color-good{color:#529923!important}.theme-light .color-average{color:#da810e!important}.theme-light .color-bad{color:#c82121!important}.theme-light .color-label{color:#353535!important}.theme-light .color-gold{color:#e39b0d!important}.theme-light .color-bg-black{background-color:#000!important}.theme-light .color-bg-white{background-color:#bfbfbf!important}.theme-light .color-bg-red{background-color:#a61c1c!important}.theme-light .color-bg-orange{background-color:#c0530b!important}.theme-light .color-bg-yellow{background-color:#bfa303!important}.theme-light .color-bg-olive{background-color:#889912!important}.theme-light .color-bg-green{background-color:#188532!important}.theme-light .color-bg-teal{background-color:#008882!important}.theme-light .color-bg-blue{background-color:#19649c!important}.theme-light .color-bg-violet{background-color:#4b2897!important}.theme-light .color-bg-purple{background-color:#7a2696!important}.theme-light .color-bg-pink{background-color:#b61d73!important}.theme-light .color-bg-brown{background-color:#7c4d2f!important}.theme-light .color-bg-grey{background-color:#bfbfbf!important}.theme-light .color-bg-good{background-color:#44801d!important}.theme-light .color-bg-average{background-color:#b56b0b!important}.theme-light .color-bg-bad{background-color:#a61c1c!important}.theme-light .color-bg-label{background-color:#2c2c2c!important}.theme-light .color-bg-gold{background-color:#bd810b!important}.theme-light .Tabs{display:flex;align-items:stretch;overflow:hidden;background-color:#fff}.theme-light .Tabs--fill{height:100%}.theme-light .Section .Tabs{background-color:rgba(0,0,0,0)}.theme-light .Section:not(.Section--fitted) .Tabs{margin:0 -.5em .5em}.theme-light .Section:not(.Section--fitted) .Tabs:first-child{margin-top:-.5em}.theme-light .Tabs--vertical{flex-direction:column;padding:.25em .25em .25em 0}.theme-light .Tabs--horizontal{margin-bottom:.5em;padding:.25em .25em 0}.theme-light .Tabs--horizontal:last-child{margin-bottom:0}.theme-light .Tabs__Tab{flex-grow:0}.theme-light .Tabs--fluid .Tabs__Tab{flex-grow:1}.theme-light .Tab{display:flex;align-items:center;justify-content:space-between;background-color:rgba(0,0,0,0);color:rgba(0,0,0,.5);min-height:2.25em;min-width:4em;transition:background-color 50ms ease-out}.theme-light .Tab:not(.Tab--selected):hover{background-color:rgba(0,0,0,.075);transition:background-color 0}.theme-light .Tab--selected{background-color:rgba(0,0,0,.125);color:#404040}.theme-light .Tab__text{flex-grow:1;margin:0 .5em}.theme-light .Tab__left{min-width:1.5em;text-align:center;margin-left:.25em}.theme-light .Tab__right{min-width:1.5em;text-align:center;margin-right:.25em}.theme-light .Tabs--horizontal .Tab{border-top:.1666666667em solid rgba(0,0,0,0);border-bottom:.1666666667em solid rgba(0,0,0,0);border-top-left-radius:.25em;border-top-right-radius:.25em}.theme-light .Tabs--horizontal .Tab--selected{border-bottom:.1666666667em solid #000}.theme-light .Tabs--vertical .Tab{min-height:2em;border-left:.1666666667em solid rgba(0,0,0,0);border-right:.1666666667em solid rgba(0,0,0,0);border-top-right-radius:.25em;border-bottom-right-radius:.25em}.theme-light .Tabs--vertical .Tab--selected{border-left:.1666666667em solid #000}.theme-light .Tab--selected.Tab--color--black{color:#404040}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--black{border-bottom-color:#000}.theme-light .Tabs--vertical .Tab--selected.Tab--color--black{border-left-color:#000}.theme-light .Tab--selected.Tab--color--white{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--white{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--white{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--red{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--red{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--red{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--orange{color:#f48942}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--orange{border-bottom-color:#e6630d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--orange{border-left-color:#e6630d}.theme-light .Tab--selected.Tab--color--yellow{color:#fcdd33}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--yellow{border-bottom-color:#e5c304}.theme-light .Tabs--vertical .Tab--selected.Tab--color--yellow{border-left-color:#e5c304}.theme-light .Tab--selected.Tab--color--olive{color:#d0e732}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--olive{border-bottom-color:#a3b816}.theme-light .Tabs--vertical .Tab--selected.Tab--color--olive{border-left-color:#a3b816}.theme-light .Tab--selected.Tab--color--green{color:#33da5a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--green{border-bottom-color:#1d9f3b}.theme-light .Tabs--vertical .Tab--selected.Tab--color--green{border-left-color:#1d9f3b}.theme-light .Tab--selected.Tab--color--teal{color:#00faef}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--teal{border-bottom-color:#00a39c}.theme-light .Tabs--vertical .Tab--selected.Tab--color--teal{border-left-color:#00a39c}.theme-light .Tab--selected.Tab--color--blue{color:#419ce1}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--blue{border-bottom-color:#1e78bb}.theme-light .Tabs--vertical .Tab--selected.Tab--color--blue{border-left-color:#1e78bb}.theme-light .Tab--selected.Tab--color--violet{color:#7f58d3}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--violet{border-bottom-color:#5a30b5}.theme-light .Tabs--vertical .Tab--selected.Tab--color--violet{border-left-color:#5a30b5}.theme-light .Tab--selected.Tab--color--purple{color:#b455d4}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--purple{border-bottom-color:#932eb4}.theme-light .Tabs--vertical .Tab--selected.Tab--color--purple{border-left-color:#932eb4}.theme-light .Tab--selected.Tab--color--pink{color:#e558a7}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--pink{border-bottom-color:#db228a}.theme-light .Tabs--vertical .Tab--selected.Tab--color--pink{border-left-color:#db228a}.theme-light .Tab--selected.Tab--color--brown{color:#c0825a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--brown{border-bottom-color:#955d39}.theme-light .Tabs--vertical .Tab--selected.Tab--color--brown{border-left-color:#955d39}.theme-light .Tab--selected.Tab--color--grey{color:#ececec}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--grey{border-bottom-color:#e6e6e6}.theme-light .Tabs--vertical .Tab--selected.Tab--color--grey{border-left-color:#e6e6e6}.theme-light .Tab--selected.Tab--color--good{color:#77d23b}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--good{border-bottom-color:#529923}.theme-light .Tabs--vertical .Tab--selected.Tab--color--good{border-left-color:#529923}.theme-light .Tab--selected.Tab--color--average{color:#f3a23a}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--average{border-bottom-color:#da810e}.theme-light .Tabs--vertical .Tab--selected.Tab--color--average{border-left-color:#da810e}.theme-light .Tab--selected.Tab--color--bad{color:#e14d4d}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--bad{border-bottom-color:#c82121}.theme-light .Tabs--vertical .Tab--selected.Tab--color--bad{border-left-color:#c82121}.theme-light .Tab--selected.Tab--color--label{color:#686868}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--label{border-bottom-color:#353535}.theme-light .Tabs--vertical .Tab--selected.Tab--color--label{border-left-color:#353535}.theme-light .Tab--selected.Tab--color--gold{color:#f4b73f}.theme-light .Tabs--horizontal .Tab--selected.Tab--color--gold{border-bottom-color:#e39b0d}.theme-light .Tabs--vertical .Tab--selected.Tab--color--gold{border-left-color:#e39b0d}.theme-light .Section{position:relative;margin-bottom:.5em;background-color:#fff;box-sizing:border-box}.theme-light .Section:last-child{margin-bottom:0}.theme-light .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #fff}.theme-light .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#000}.theme-light .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-light .Section__rest{position:relative}.theme-light .Section__content{padding:.66em .5em}.theme-light .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-light .Section--fill{display:flex;flex-direction:column;height:100%}.theme-light .Section--fill>.Section__rest{flex-grow:1}.theme-light .Section--fill>.Section__rest>.Section__content{height:100%}.theme-light .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-light .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-light .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-light .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-light .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-light .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-light .Section .Section:first-child{margin-top:-.5em}.theme-light .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-light .Section .Section .Section .Section__titleText{font-size:1em}.theme-light .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-light .Button:last-child{margin-right:0;margin-bottom:0}.theme-light .Button .fa,.theme-light .Button .fas,.theme-light .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-light .Button--hasContent .fa,.theme-light .Button--hasContent .fas,.theme-light .Button--hasContent .far{margin-right:.25em}.theme-light .Button--hasContent.Button--iconRight .fa,.theme-light .Button--hasContent.Button--iconRight .fas,.theme-light .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-light .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-light .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-light .Button--circular{border-radius:50%}.theme-light .Button--compact{padding:0 .25em;line-height:1.333em}.theme-light .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-light .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-light .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--black:hover{background-color:#101010;color:#fff}.theme-light .Button--color--white{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--white:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--red{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--red:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--orange{transition:color .1s,background-color .1s;background-color:#c0530b;color:#fff}.theme-light .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--orange:hover{background-color:#e76d1d;color:#fff}.theme-light .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#bfa303;color:#fff}.theme-light .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--yellow:hover{background-color:#e7c714;color:#fff}.theme-light .Button--color--olive{transition:color .1s,background-color .1s;background-color:#889912;color:#fff}.theme-light .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--olive:hover{background-color:#a9bc25;color:#fff}.theme-light .Button--color--green{transition:color .1s,background-color .1s;background-color:#188532;color:#fff}.theme-light .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--green:hover{background-color:#2ba648;color:#fff}.theme-light .Button--color--teal{transition:color .1s,background-color .1s;background-color:#008882;color:#fff}.theme-light .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--teal:hover{background-color:#10a9a2;color:#fff}.theme-light .Button--color--blue{transition:color .1s,background-color .1s;background-color:#19649c;color:#fff}.theme-light .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--blue:hover{background-color:#2c81c0;color:#fff}.theme-light .Button--color--violet{transition:color .1s,background-color .1s;background-color:#4b2897;color:#fff}.theme-light .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--violet:hover{background-color:#653db9;color:#fff}.theme-light .Button--color--purple{transition:color .1s,background-color .1s;background-color:#7a2696;color:#fff}.theme-light .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--purple:hover{background-color:#9a3bb9;color:#fff}.theme-light .Button--color--pink{transition:color .1s,background-color .1s;background-color:#b61d73;color:#fff}.theme-light .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--pink:hover{background-color:#d93591;color:#fff}.theme-light .Button--color--brown{transition:color .1s,background-color .1s;background-color:#7c4d2f;color:#fff}.theme-light .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--brown:hover{background-color:#9c6745;color:#fff}.theme-light .Button--color--grey{transition:color .1s,background-color .1s;background-color:#bfbfbf;color:#000}.theme-light .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--grey:hover{background-color:#e7e7e7;color:#000}.theme-light .Button--color--good{transition:color .1s,background-color .1s;background-color:#44801d;color:#fff}.theme-light .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--good:hover{background-color:#5d9f31;color:#fff}.theme-light .Button--color--average{transition:color .1s,background-color .1s;background-color:#b56b0b;color:#fff}.theme-light .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--average:hover{background-color:#dc891d;color:#fff}.theme-light .Button--color--bad{transition:color .1s,background-color .1s;background-color:#a61c1c;color:#fff}.theme-light .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--bad:hover{background-color:#cb3030;color:#fff}.theme-light .Button--color--label{transition:color .1s,background-color .1s;background-color:#2c2c2c;color:#fff}.theme-light .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--label:hover{background-color:#424242;color:#fff}.theme-light .Button--color--gold{transition:color .1s,background-color .1s;background-color:#bd810b;color:#fff}.theme-light .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--gold:hover{background-color:#e5a11c;color:#fff}.theme-light .Button--color--default{transition:color .1s,background-color .1s;background-color:#bbb;color:#000}.theme-light .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--default:hover{background-color:#e3e3e3;color:#000}.theme-light .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-light .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-light .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-light .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-light .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,0);color:rgba(0,0,0,.5)}.theme-light .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--transparent:hover{background-color:rgba(255,255,255,.81);color:#000}.theme-light .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(238,238,238,.6);color:rgba(0,0,0,.5)}.theme-light .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-light .Button--color--translucent:hover{background-color:rgba(253,253,253,.925);color:#000}.theme-light .Button--disabled{background-color:#363636!important}.theme-light .Button--selected{transition:color .1s,background-color .1s;background-color:#0668b8;color:#fff}.theme-light .Button--selected:focus{transition:color .25s,background-color .25s}.theme-light .Button--selected:hover{background-color:#1785df;color:#fff}.theme-light .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-light .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#353535;background-color:#e6e6e6;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-light .NumberInput--fluid{display:block}.theme-light .NumberInput__content{margin-left:.5em}.theme-light .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-light .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #353535;background-color:#353535}.theme-light .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#e6e6e6;color:#000;text-align:right}.theme-light .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;color:#000;background-color:#e6e6e6;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-light .Input--disabled{color:#777;border-color:#000;border-color:rgba(0,0,0,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-light .Input--fluid{display:block;width:auto}.theme-light .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-light .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#000;color:inherit}.theme-light .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-light .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-light .TextArea{position:relative;display:inline-block;border:.0833333333em solid #353535;border:.0833333333em solid rgba(53,53,53,.75);border-radius:.16em;background-color:#e6e6e6;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-light .TextArea--fluid{display:block;width:auto;height:auto}.theme-light .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-light .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-light .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-light .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-light .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-light .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-light .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-light .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-light .Knob__popupValue,.theme-light .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .Knob__popupValue--right{top:.25rem;right:-50%}.theme-light .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-light .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-light .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-light .Knob__ringFillPivot{transform:rotate(135deg)}.theme-light .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-light .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-light .Knob--color--black .Knob__ringFill{stroke:#000}.theme-light .Knob--color--white .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--red .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--orange .Knob__ringFill{stroke:#e6630d}.theme-light .Knob--color--yellow .Knob__ringFill{stroke:#e5c304}.theme-light .Knob--color--olive .Knob__ringFill{stroke:#a3b816}.theme-light .Knob--color--green .Knob__ringFill{stroke:#1d9f3b}.theme-light .Knob--color--teal .Knob__ringFill{stroke:#00a39c}.theme-light .Knob--color--blue .Knob__ringFill{stroke:#1e78bb}.theme-light .Knob--color--violet .Knob__ringFill{stroke:#5a30b5}.theme-light .Knob--color--purple .Knob__ringFill{stroke:#932eb4}.theme-light .Knob--color--pink .Knob__ringFill{stroke:#db228a}.theme-light .Knob--color--brown .Knob__ringFill{stroke:#955d39}.theme-light .Knob--color--grey .Knob__ringFill{stroke:#e6e6e6}.theme-light .Knob--color--good .Knob__ringFill{stroke:#529923}.theme-light .Knob--color--average .Knob__ringFill{stroke:#da810e}.theme-light .Knob--color--bad .Knob__ringFill{stroke:#c82121}.theme-light .Knob--color--label .Knob__ringFill{stroke:#353535}.theme-light .Knob--color--gold .Knob__ringFill{stroke:#e39b0d}.theme-light .Slider:not(.Slider__disabled){cursor:e-resize}.theme-light .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-light .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #000}.theme-light .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #000}.theme-light .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-light .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-light .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-light .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-light .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-light .ProgressBar--color--default{border:.0833333333em solid #bfbfbf}.theme-light .ProgressBar--color--default .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--disabled{border:1px solid #999}.theme-light .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-light .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-light .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-light .ProgressBar--color--white{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--white .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--red{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--red .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--orange{border:.0833333333em solid #c0530b!important}.theme-light .ProgressBar--color--orange .ProgressBar__fill{background-color:#c0530b}.theme-light .ProgressBar--color--yellow{border:.0833333333em solid #bfa303!important}.theme-light .ProgressBar--color--yellow .ProgressBar__fill{background-color:#bfa303}.theme-light .ProgressBar--color--olive{border:.0833333333em solid #889912!important}.theme-light .ProgressBar--color--olive .ProgressBar__fill{background-color:#889912}.theme-light .ProgressBar--color--green{border:.0833333333em solid #188532!important}.theme-light .ProgressBar--color--green .ProgressBar__fill{background-color:#188532}.theme-light .ProgressBar--color--teal{border:.0833333333em solid #008882!important}.theme-light .ProgressBar--color--teal .ProgressBar__fill{background-color:#008882}.theme-light .ProgressBar--color--blue{border:.0833333333em solid #19649c!important}.theme-light .ProgressBar--color--blue .ProgressBar__fill{background-color:#19649c}.theme-light .ProgressBar--color--violet{border:.0833333333em solid #4b2897!important}.theme-light .ProgressBar--color--violet .ProgressBar__fill{background-color:#4b2897}.theme-light .ProgressBar--color--purple{border:.0833333333em solid #7a2696!important}.theme-light .ProgressBar--color--purple .ProgressBar__fill{background-color:#7a2696}.theme-light .ProgressBar--color--pink{border:.0833333333em solid #b61d73!important}.theme-light .ProgressBar--color--pink .ProgressBar__fill{background-color:#b61d73}.theme-light .ProgressBar--color--brown{border:.0833333333em solid #7c4d2f!important}.theme-light .ProgressBar--color--brown .ProgressBar__fill{background-color:#7c4d2f}.theme-light .ProgressBar--color--grey{border:.0833333333em solid #bfbfbf!important}.theme-light .ProgressBar--color--grey .ProgressBar__fill{background-color:#bfbfbf}.theme-light .ProgressBar--color--good{border:.0833333333em solid #44801d!important}.theme-light .ProgressBar--color--good .ProgressBar__fill{background-color:#44801d}.theme-light .ProgressBar--color--average{border:.0833333333em solid #b56b0b!important}.theme-light .ProgressBar--color--average .ProgressBar__fill{background-color:#b56b0b}.theme-light .ProgressBar--color--bad{border:.0833333333em solid #a61c1c!important}.theme-light .ProgressBar--color--bad .ProgressBar__fill{background-color:#a61c1c}.theme-light .ProgressBar--color--label{border:.0833333333em solid #2c2c2c!important}.theme-light .ProgressBar--color--label .ProgressBar__fill{background-color:#2c2c2c}.theme-light .ProgressBar--color--gold{border:.0833333333em solid #bd810b!important}.theme-light .ProgressBar--color--gold .ProgressBar__fill{background-color:#bd810b}.theme-light .Chat{color:#000}.theme-light .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-light .Chat__badge:before{content:"x"}.theme-light .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-light .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-light .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-light .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#fff}.theme-light .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-light .Chat__highlight{color:#000}.theme-light .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-light .ChatMessage{word-wrap:break-word}.theme-light .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-light .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-light html,.theme-light body{scrollbar-color:#a7a7a7 #f2f2f2}.theme-light .Layout,.theme-light .Layout *{scrollbar-base-color:#f2f2f2;scrollbar-face-color:#d6d6d6;scrollbar-3dlight-color:#eee;scrollbar-highlight-color:#eee;scrollbar-track-color:#f2f2f2;scrollbar-arrow-color:#777;scrollbar-shadow-color:#d6d6d6}.theme-light .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-light .Layout__content--flexRow{display:flex;flex-flow:row}.theme-light .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-light .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-light .Layout__content--noMargin{margin:0}.theme-light .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#000;background-color:#eee;background-image:linear-gradient(to bottom,#eee,#eee)}.theme-light .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-light .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-light .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-light .Window__contentPadding:after{height:0}.theme-light .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-light .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(252,252,252,.25);pointer-events:none}.theme-light .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-light .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-light .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-light .TitleBar{background-color:#eee;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-light .TitleBar__clickable{color:rgba(0,0,0,.5);background-color:#eee;transition:color .25s,background-color .25s}.theme-light .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-light .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(0,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-light .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-light .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-light .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-light .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-light html,.theme-light body{padding:0;margin:0;height:100%;color:#000}.theme-light body{background:#fff;font-family:Verdana,sans-serif;font-size:13px;line-height:1.2;overflow-x:hidden;overflow-y:scroll;word-wrap:break-word}.theme-light img{margin:0;padding:0;line-height:1;-ms-interpolation-mode:nearest-neighbor;image-rendering:pixelated}.theme-light img.icon{height:1em;min-height:16px;width:auto;vertical-align:bottom}.theme-light a{color:#00f}.theme-light a.popt{text-decoration:none}.theme-light .popup{position:fixed;top:50%;left:50%;background:#ddd}.theme-light .popup .close{position:absolute;background:#aaa;top:0;right:0;color:#333;text-decoration:none;z-index:2;padding:0 10px;height:30px;line-height:30px}.theme-light .popup .close:hover{background:#999}.theme-light .popup .head{background:#999;color:#ddd;padding:0 10px;height:30px;line-height:30px;text-transform:uppercase;font-size:.9em;font-weight:700;border-bottom:2px solid green}.theme-light .popup input{border:1px solid #999;background:#fff;margin:0;padding:5px;outline:none;color:#333}.theme-light .popup input[type=text]:hover,.theme-light .popup input[type=text]:active,.theme-light .popup input[type=text]:focus{border-color:green}.theme-light .popup input[type=submit]{padding:5px 10px;background:#999;color:#ddd;text-transform:uppercase;font-size:.9em;font-weight:700}.theme-light .popup input[type=submit]:hover,.theme-light .popup input[type=submit]:focus,.theme-light .popup input[type=submit]:active{background:#aaa;cursor:pointer}.theme-light .changeFont{padding:10px}.theme-light .changeFont a{display:block;text-decoration:none;padding:3px;color:#333}.theme-light .changeFont a:hover{background:#ccc}.theme-light .highlightPopup{padding:10px;text-align:center}.theme-light .highlightPopup input[type=text]{display:block;width:215px;text-align:left;margin-top:5px}.theme-light .highlightPopup input.highlightColor{background-color:#ff0}.theme-light .highlightPopup input.highlightTermSubmit{margin-top:5px}.theme-light .contextMenu{background-color:#ddd;position:fixed;margin:2px;width:150px}.theme-light .contextMenu a{display:block;padding:2px 5px;text-decoration:none;color:#333}.theme-light .contextMenu a:hover{background-color:#ccc}.theme-light .filterMessages{padding:5px}.theme-light .filterMessages div{padding:2px 0}.theme-light .icon-stack{height:1em;line-height:1em;width:1em;vertical-align:middle;margin-top:-2px}.theme-light .motd{color:#638500;font-family:Verdana,sans-serif;white-space:normal}.theme-light .motd h1,.theme-light .motd h2,.theme-light .motd h3,.theme-light .motd h4,.theme-light .motd h5,.theme-light .motd h6{color:#638500;text-decoration:underline}.theme-light .motd a,.theme-light .motd a:link,.theme-light .motd a:active,.theme-light .motd a:hover{color:#638500}.theme-light .italic,.theme-light .italics,.theme-light .emote{font-style:italic}.theme-light .highlight{background:#ff0}.theme-light h1,.theme-light h2,.theme-light h3,.theme-light h4,.theme-light h5,.theme-light h6{color:#00f;font-family:Georgia,Verdana,sans-serif}.theme-light em{font-style:normal;font-weight:700}.theme-light .darkmblue{color:#00f}.theme-light .prefix,.theme-light .ooc{font-weight:700}.theme-light .looc{color:#69c;font-weight:700}.theme-light .adminobserverooc{color:#09c;font-weight:700}.theme-light .adminooc{color:#b82e00;font-weight:700}.theme-light .adminobserver{color:#960;font-weight:700}.theme-light .admin{color:#386aff;font-weight:700}.theme-light .adminsay{color:#9611d4;font-weight:700}.theme-light .mentorhelp{color:#07b;font-weight:700}.theme-light .adminhelp{color:#a00;font-weight:700}.theme-light .playerreply{color:#80b;font-weight:700}.theme-light .pmsend{color:#00f}.theme-light .debug{color:#6d2f83}.theme-light .name,.theme-light .yell{font-weight:700}.theme-light .siliconsay{font-family:Courier New,Courier,monospace}.theme-light .deadsay{color:#5c00e6}.theme-light .radio{color:#408010}.theme-light .deptradio{color:#939}.theme-light .comradio{color:#204090}.theme-light .syndradio{color:#6d3f40}.theme-light .dsquadradio{color:#686868}.theme-light .resteamradio{color:#18bc46}.theme-light .airadio{color:#f0f}.theme-light .centradio{color:#5c5c7c}.theme-light .secradio{color:#a30000}.theme-light .engradio{color:#a66300}.theme-light .medradio{color:#009190}.theme-light .sciradio{color:#939}.theme-light .supradio{color:#7f6539}.theme-light .srvradio{color:#80a000}.theme-light .proradio{color:#e3027a}.theme-light .admin_channel{color:#9a04d1;font-weight:700}.theme-light .all_admin_ping{color:#12a5f4;font-weight:700;font-size:120%;text-align:center}.theme-light .mentor_channel{color:#775bff;font-weight:700}.theme-light .mentor_channel_admin{color:#a35cff;font-weight:700}.theme-light .djradio{color:#630}.theme-light .binaryradio{color:#0b0050;font-family:Courier New,Courier,monospace}.theme-light .mommiradio{color:navy}.theme-light .alert{color:red}.theme-light h1.alert,.theme-light h2.alert{color:#000}.theme-light .ghostalert{color:#5c00e6;font-style:italic;font-weight:700}.theme-light .emote{font-style:italic}.theme-light .selecteddna{color:#fff;background-color:#001b1b}.theme-light .attack{color:red}.theme-light .moderate{color:#c00}.theme-light .disarm{color:#900}.theme-light .passive{color:#600}.theme-light .warning{color:red;font-style:italic}.theme-light .boldwarning{color:red;font-style:italic;font-weight:700}.theme-light .danger{color:red;font-weight:700}.theme-light .userdanger{color:red;font-weight:700;font-size:120%}.theme-light .biggerdanger{color:red;font-weight:700;font-size:150%}.theme-light .info{color:#00c}.theme-light .notice{color:#009}.theme-light .boldnotice{color:#009;font-weight:700}.theme-light .suicide{color:#ff5050;font-style:italic}.theme-light .green{color:#03bb39}.theme-light .pr_announce{color:#228b22;font-weight:700}.theme-light .boldannounceic,.theme-light .boldannounceooc{color:red;font-weight:700}.theme-light .greenannounce{color:#0f0;font-weight:700}.theme-light .alien{color:#543354}.theme-light .noticealien{color:#00c000}.theme-light .alertalien{color:#00c000;font-weight:700}.theme-light .terrorspider{color:#320e32}.theme-light .chaosverygood{color:#19e0c0;font-weight:700;font-size:120%}.theme-light .chaosgood{color:#19e0c0;font-weight:700}.theme-light .chaosneutral{color:#479ac0;font-weight:700}.theme-light .chaosbad{color:#9047c0;font-weight:700}.theme-light .chaosverybad{color:#9047c0;font-weight:700;font-size:120%}.theme-light .sinister{color:purple;font-weight:700;font-style:italic}.theme-light .confirm{color:#00af3b}.theme-light .rose{color:#ff5050}.theme-light .sans{font-family:Comic Sans MS,cursive,sans-serif}.theme-light .wingdings{font-family:Wingdings,Webdings}.theme-light .robot{font-family:OCR-A,monospace;font-size:1.15em;font-weight:700}.theme-light .ancient{color:#008b8b;font-style:italic}.theme-light .newscaster{color:maroon}.theme-light .mod{color:#735638;font-weight:700}.theme-light .modooc{color:#184880;font-weight:700}.theme-light .adminmod{color:#402a14;font-weight:700}.theme-light .tajaran{color:#803b56}.theme-light .skrell{color:#00ced1}.theme-light .solcom{color:#22228b}.theme-light .com_srus{color:#7c4848}.theme-light .zombie{color:red}.theme-light .soghun{color:#228b22}.theme-light .changeling{color:purple}.theme-light .vox{color:#a0a}.theme-light .diona{color:#804000;font-weight:700}.theme-light .trinary{color:#727272}.theme-light .kidan{color:#664205}.theme-light .slime{color:#07a}.theme-light .drask{color:#a3d4eb;font-family:Arial Black}.theme-light .moth{color:#869b29;font-family:Copperplate}.theme-light .clown{color:red}.theme-light .vulpkanin{color:#b97a57}.theme-light .abductor{color:purple;font-style:italic}.theme-light .mind_control{color:#a00d6f;font-size:3;font-weight:700;font-style:italic}.theme-light .rough{font-family:Trebuchet MS,cursive,sans-serif}.theme-light .say_quote{font-family:Georgia,Verdana,sans-serif}.theme-light .cult{color:purple;font-weight:700;font-style:italic}.theme-light .cultspeech{color:#7f0000;font-style:italic}.theme-light .cultitalic{color:#960000;font-style:italic}.theme-light .cultlarge{color:#960000;font-weight:700;font-size:120%}.theme-light .narsie{color:#960000;font-weight:700;font-size:300%}.theme-light .narsiesmall{color:#960000;font-weight:700;font-size:200%}.theme-light .interface{color:#303}.theme-light .big{font-size:150%}.theme-light .reallybig{font-size:175%}.theme-light .greentext{color:#0f0;font-size:150%}.theme-light .redtext{color:red;font-size:150%}.theme-light .bold{font-weight:700}.theme-light .his_grace{color:#15d512;font-family:Courier New,cursive,sans-serif;font-style:italic}.theme-light .center{text-align:center}.theme-light .red{color:red}.theme-light .purple{color:#5e2d79}.theme-light .skeleton{color:#585858;font-weight:700;font-style:italic}.theme-light .gutter{color:#7092be;font-family:Trebuchet MS,cursive,sans-serif}.theme-light .orange{color:orange}.theme-light .orangei{color:orange;font-style:italic}.theme-light .orangeb{color:orange;font-weight:700}.theme-light .resonate{color:#298f85}.theme-light .healthscan_oxy{color:#0074bd}.theme-light .revennotice{color:#1d2953}.theme-light .revenboldnotice{color:#1d2953;font-weight:700}.theme-light .revenbignotice{color:#1d2953;font-weight:700;font-size:120%}.theme-light .revenminor{color:#823abb}.theme-light .revenwarning{color:#760fbb;font-style:italic}.theme-light .revendanger{color:#760fbb;font-weight:700;font-size:120%}.theme-light .specialnoticebold{color:#36525e;font-weight:700;font-size:120%}.theme-light .specialnotice{color:#36525e;font-size:120%}.theme-light .medal{font-weight:700}.theme-light .good{color:green}.theme-light .average{color:#ff8000}.theme-light .bad{color:red}.theme-light .italics,.theme-light .talkinto{font-style:italic}.theme-light .whisper{font-style:italic;color:#333}.theme-light .recruit{color:#5c00e6;font-weight:700;font-style:italic}.theme-light .memo{color:#638500;text-align:center}.theme-light .memoedit{text-align:center;font-size:75%}.theme-light .connectionClosed,.theme-light .fatalError{background:red;color:#fff;padding:5px}.theme-light .connectionClosed.restored{background:green}.theme-light .internal.boldnshit{color:#00f;font-weight:700}.theme-light .rebooting{background:#2979af;color:#fff;padding:5px}.theme-light .rebooting a{color:#fff!important;text-decoration-color:#fff!important}.theme-light .text-normal{font-weight:400;font-style:normal}.theme-light .hidden{display:none;visibility:hidden}.theme-light .colossus{color:#7f282a;font-size:175%}.theme-light .hierophant{color:#609;font-weight:700;font-style:italic}.theme-light .hierophant_warning{color:#609;font-style:italic}.theme-light .emoji{max-height:16px;max-width:16px}.theme-light .adminticket{color:#3e7336;font-weight:700}.theme-light .adminticketalt{color:#014c8a;font-weight:700}.theme-light span.body .codephrases{color:#00f}.theme-light span.body .coderesponses{color:red}.theme-light .announcement h1,.theme-light .announcement h2{color:#000;margin:8pt 0;line-height:1.2}.theme-light .announcement p{color:#d82020;line-height:1.3}.theme-light .announcement.minor h1{font-size:180%}.theme-light .announcement.minor h2{font-size:170%}.theme-light .announcement.sec h1{color:red;font-size:180%;font-family:Verdana,sans-serif}.theme-light .bolditalics{font-style:italic;font-weight:700}.theme-light .boxed_message{background:#f7fcff;border:1px solid #111a26;margin:.5em;padding:.5em .75em;text-align:center}.theme-light .boxed_message.left_align_text{text-align:left}.theme-light .boxed_message.red_border{background:#fff7f7;border-color:#a00}.theme-light .boxed_message.green_border{background:#f7fff7;border-color:#0f0}.theme-light .boxed_message.purple_border{background:#fdf7ff;border-color:#a0f}.theme-light .boxed_message.notice_border{background:#f7fdff;border-color:#0000bf}.theme-light .boxed_message.thick_border{border-width:thick}.theme-light .oxygen{color:#006adb}.theme-light .nitrogen{color:#d00a06}.theme-light .carbon_dioxide{color:#1f1f1f}.theme-light .plasma{color:#853c00}.theme-light .sleeping_agent{color:#e82f2c}.theme-light .agent_b{color:#004d4d}.theme-light .spyradio{color:#776f96}.theme-light .sovradio{color:#f7941d}.theme-light .taipan{color:#998e54}.theme-light .syndiecom{color:#8f4242}.theme-light .spider_clan{color:#044a1b}.theme-light .event_alpha{color:#88910f}.theme-light .event_beta{color:#1d83f7}.theme-light .event_gamma{color:#d46549}.theme-light .blob{color:#006221;font-weight:700;font-style:italic}.theme-light .blobteslium_paste{color:#412968;font-weight:700;font-style:italic}.theme-light .blobradioactive_gel{color:#2476f0;font-weight:700;font-style:italic}.theme-light .blobb_sorium{color:olive;font-weight:700;font-style:italic}.theme-light .blobcryogenic_liquid{color:#8ba6e9;font-weight:700;font-style:italic}.theme-light .blobkinetic{color:orange;font-weight:700;font-style:italic}.theme-light .bloblexorin_jelly{color:#00ffc5;font-weight:700;font-style:italic}.theme-light .blobenvenomed_filaments{color:#9acd32;font-weight:700;font-style:italic}.theme-light .blobboiling_oil{color:#b68d00;font-weight:700;font-style:italic}.theme-light .blobripping_tendrils{color:#7f0000;font-weight:700;font-style:italic}.theme-light .shadowling{color:#3b2769}.theme-light .clock{color:#bd8700;font-weight:700;font-style:italic}.theme-light .clockspeech{color:#996e00;font-style:italic}.theme-light .clockitalic{color:#bd8700;font-style:italic}.theme-light .clocklarge{color:#bd8700;font-weight:700;font-size:120%}.theme-light .ratvar{color:#bd8700;font-weight:700;font-size:300%}.theme-light .examine{border:1px solid #000;padding:5px;margin:2px 10px;background:#d3d3d3}.theme-light .dantalion{color:#1a7d5b}.theme-ntos .color-black{color:#1a1a1a!important}.theme-ntos .color-white{color:#fff!important}.theme-ntos .color-red{color:#df3e3e!important}.theme-ntos .color-orange{color:#f37f33!important}.theme-ntos .color-yellow{color:#fbda21!important}.theme-ntos .color-olive{color:#cbe41c!important}.theme-ntos .color-green{color:#25ca4c!important}.theme-ntos .color-teal{color:#00d6cc!important}.theme-ntos .color-blue{color:#2e93de!important}.theme-ntos .color-violet{color:#7349cf!important}.theme-ntos .color-purple{color:#ad45d0!important}.theme-ntos .color-pink{color:#e34da1!important}.theme-ntos .color-brown{color:#b97447!important}.theme-ntos .color-grey{color:#848484!important}.theme-ntos .color-good{color:#68c22d!important}.theme-ntos .color-average{color:#f29a29!important}.theme-ntos .color-bad{color:#df3e3e!important}.theme-ntos .color-label{color:#8b9bb0!important}.theme-ntos .color-gold{color:#f3b22f!important}.theme-ntos .color-bg-black{background-color:#000!important}.theme-ntos .color-bg-white{background-color:#d9d9d9!important}.theme-ntos .color-bg-red{background-color:#bd2020!important}.theme-ntos .color-bg-orange{background-color:#d95e0c!important}.theme-ntos .color-bg-yellow{background-color:#d9b804!important}.theme-ntos .color-bg-olive{background-color:#9aad14!important}.theme-ntos .color-bg-green{background-color:#1b9638!important}.theme-ntos .color-bg-teal{background-color:#009a93!important}.theme-ntos .color-bg-blue{background-color:#1c71b1!important}.theme-ntos .color-bg-violet{background-color:#552dab!important}.theme-ntos .color-bg-purple{background-color:#8b2baa!important}.theme-ntos .color-bg-pink{background-color:#cf2082!important}.theme-ntos .color-bg-brown{background-color:#8c5836!important}.theme-ntos .color-bg-grey{background-color:#646464!important}.theme-ntos .color-bg-good{background-color:#4d9121!important}.theme-ntos .color-bg-average{background-color:#cd7a0d!important}.theme-ntos .color-bg-bad{background-color:#bd2020!important}.theme-ntos .color-bg-label{background-color:#657a94!important}.theme-ntos .color-bg-gold{background-color:#d6920c!important}.theme-ntos .Section{position:relative;margin-bottom:.5em;background-color:#121922;box-sizing:border-box}.theme-ntos .Section:last-child{margin-bottom:0}.theme-ntos .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #4972a1}.theme-ntos .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-ntos .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-ntos .Section__rest{position:relative}.theme-ntos .Section__content{padding:.66em .5em}.theme-ntos .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-ntos .Section--fill{display:flex;flex-direction:column;height:100%}.theme-ntos .Section--fill>.Section__rest{flex-grow:1}.theme-ntos .Section--fill>.Section__rest>.Section__content{height:100%}.theme-ntos .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-ntos .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-ntos .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-ntos .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-ntos .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-ntos .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-ntos .Section .Section:first-child{margin-top:-.5em}.theme-ntos .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-ntos .Section .Section .Section .Section__titleText{font-size:1em}.theme-ntos .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-ntos .Button:last-child{margin-right:0;margin-bottom:0}.theme-ntos .Button .fa,.theme-ntos .Button .fas,.theme-ntos .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-ntos .Button--hasContent .fa,.theme-ntos .Button--hasContent .fas,.theme-ntos .Button--hasContent .far{margin-right:.25em}.theme-ntos .Button--hasContent.Button--iconRight .fa,.theme-ntos .Button--hasContent.Button--iconRight .fas,.theme-ntos .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-ntos .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-ntos .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-ntos .Button--circular{border-radius:50%}.theme-ntos .Button--compact{padding:0 .25em;line-height:1.333em}.theme-ntos .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-ntos .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-ntos .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--black:hover{background-color:#101010;color:#fff}.theme-ntos .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-ntos .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-ntos .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-ntos .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-ntos .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-ntos .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-ntos .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-ntos .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-ntos .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-ntos .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-ntos .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-ntos .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-ntos .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-ntos .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-ntos .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-ntos .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-ntos .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-ntos .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-ntos .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-ntos .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-ntos .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-ntos .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-ntos .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-ntos .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-ntos .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--label{transition:color .1s,background-color .1s;background-color:#657a94;color:#fff}.theme-ntos .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--label:hover{background-color:#8a9aae;color:#fff}.theme-ntos .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-ntos .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-ntos .Button--color--default{transition:color .1s,background-color .1s;background-color:#384e68;color:#fff}.theme-ntos .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--default:hover{background-color:#4f6885;color:#fff}.theme-ntos .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-ntos .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-ntos .Button--color--danger{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-ntos .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--danger:hover{background-color:#d93f3f;color:#fff}.theme-ntos .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,0);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--transparent:hover{background-color:rgba(44,57,73,.81);color:#fff}.theme-ntos .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(27,38,51,.6);color:rgba(255,255,255,.5)}.theme-ntos .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--color--translucent:hover{background-color:rgba(48,61,76,.925);color:#fff}.theme-ntos .Button--disabled{background-color:#999!important}.theme-ntos .Button--selected{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-ntos .Button--selected:focus{transition:color .25s,background-color .25s}.theme-ntos .Button--selected:hover{background-color:#2fb94f;color:#fff}.theme-ntos .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-ntos .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;color:#88bfff;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-ntos .NumberInput--fluid{display:block}.theme-ntos .NumberInput__content{margin-left:.5em}.theme-ntos .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-ntos .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #88bfff;background-color:#88bfff}.theme-ntos .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-ntos .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-ntos .Input--disabled{color:#777;border-color:#848484;border-color:rgba(132,132,132,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-ntos .Input--fluid{display:block;width:auto}.theme-ntos .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-ntos .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-ntos .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-ntos .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-ntos .TextArea{position:relative;display:inline-block;border:.0833333333em solid #88bfff;border:.0833333333em solid rgba(136,191,255,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-ntos .TextArea--fluid{display:block;width:auto;height:auto}.theme-ntos .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-ntos .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-ntos .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-ntos .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-ntos .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-ntos .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-ntos .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-ntos .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-ntos .Knob__popupValue,.theme-ntos .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .Knob__popupValue--right{top:.25rem;right:-50%}.theme-ntos .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-ntos .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-ntos .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-ntos .Knob__ringFillPivot{transform:rotate(135deg)}.theme-ntos .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-ntos .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-ntos .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-ntos .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-ntos .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-ntos .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-ntos .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-ntos .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-ntos .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-ntos .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-ntos .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-ntos .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-ntos .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-ntos .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-ntos .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-ntos .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-ntos .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-ntos .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-ntos .Knob--color--label .Knob__ringFill{stroke:#8b9bb0}.theme-ntos .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-ntos .Slider:not(.Slider__disabled){cursor:e-resize}.theme-ntos .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-ntos .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-ntos .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-ntos .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-ntos .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-ntos .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-ntos .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-ntos .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-ntos .ProgressBar--color--default{border:.0833333333em solid #3e6189}.theme-ntos .ProgressBar--color--default .ProgressBar__fill{background-color:#3e6189}.theme-ntos .ProgressBar--color--disabled{border:1px solid #999}.theme-ntos .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-ntos .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-ntos .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-ntos .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-ntos .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-ntos .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-ntos .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-ntos .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-ntos .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-ntos .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-ntos .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-ntos .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-ntos .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-ntos .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-ntos .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-ntos .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-ntos .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-ntos .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-ntos .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-ntos .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-ntos .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-ntos .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-ntos .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-ntos .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-ntos .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-ntos .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-ntos .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-ntos .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-ntos .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-ntos .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-ntos .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-ntos .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-ntos .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-ntos .ProgressBar--color--label{border:.0833333333em solid #657a94!important}.theme-ntos .ProgressBar--color--label .ProgressBar__fill{background-color:#657a94}.theme-ntos .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-ntos .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-ntos .Chat{color:#abc6ec}.theme-ntos .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-ntos .Chat__badge:before{content:"x"}.theme-ntos .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-ntos .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-ntos .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-ntos .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#121922}.theme-ntos .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-ntos .Chat__highlight{color:#000}.theme-ntos .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-ntos .ChatMessage{word-wrap:break-word}.theme-ntos .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-ntos .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-ntos html,.theme-ntos body{scrollbar-color:#2a3b4f #141d26}.theme-ntos .Layout,.theme-ntos .Layout *{scrollbar-base-color:#141d26;scrollbar-face-color:#2a3b4f;scrollbar-3dlight-color:#1b2633;scrollbar-highlight-color:#1b2633;scrollbar-track-color:#141d26;scrollbar-arrow-color:#7290b4;scrollbar-shadow-color:#2a3b4f}.theme-ntos .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-ntos .Layout__content--flexRow{display:flex;flex-flow:row}.theme-ntos .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-ntos .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-ntos .Layout__content--noMargin{margin:0}.theme-ntos .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#1b2633;background-image:linear-gradient(to bottom,#1b2633,#1b2633)}.theme-ntos .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-ntos .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-ntos .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-ntos .Window__contentPadding:after{height:0}.theme-ntos .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-ntos .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(50,63,78,.25);pointer-events:none}.theme-ntos .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-ntos .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-ntos .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-ntos .TitleBar{background-color:#1b2633;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-ntos .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#1b2633;transition:color .25s,background-color .25s}.theme-ntos .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-ntos .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-ntos .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-ntos .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-ntos .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-ntos .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-ntos .boxed_message{background:#1c242e;border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-ntos .boxed_message.left_align_text{text-align:left}.theme-ntos .boxed_message.red_border{background:#2e1c1c;border-color:#a00}.theme-ntos .boxed_message.green_border{background:#1c2e22;border-color:#0f0}.theme-ntos .boxed_message.purple_border{background:#221c2e;border-color:#8000ff}.theme-ntos .boxed_message.notice_border{background:#1f2633;border-color:#6685f5}.theme-ntos .boxed_message.thick_border{border-width:thick}.theme-syndicate .color-black{color:#1a1a1a!important}.theme-syndicate .color-white{color:#fff!important}.theme-syndicate .color-red{color:#df3e3e!important}.theme-syndicate .color-orange{color:#f37f33!important}.theme-syndicate .color-yellow{color:#fbda21!important}.theme-syndicate .color-olive{color:#cbe41c!important}.theme-syndicate .color-green{color:#25ca4c!important}.theme-syndicate .color-teal{color:#00d6cc!important}.theme-syndicate .color-blue{color:#2e93de!important}.theme-syndicate .color-violet{color:#7349cf!important}.theme-syndicate .color-purple{color:#ad45d0!important}.theme-syndicate .color-pink{color:#e34da1!important}.theme-syndicate .color-brown{color:#b97447!important}.theme-syndicate .color-grey{color:#848484!important}.theme-syndicate .color-good{color:#68c22d!important}.theme-syndicate .color-average{color:#f29a29!important}.theme-syndicate .color-bad{color:#df3e3e!important}.theme-syndicate .color-label{color:#b89797!important}.theme-syndicate .color-gold{color:#f3b22f!important}.theme-syndicate .color-bg-black{background-color:#000!important}.theme-syndicate .color-bg-white{background-color:#d9d9d9!important}.theme-syndicate .color-bg-red{background-color:#bd2020!important}.theme-syndicate .color-bg-orange{background-color:#d95e0c!important}.theme-syndicate .color-bg-yellow{background-color:#d9b804!important}.theme-syndicate .color-bg-olive{background-color:#9aad14!important}.theme-syndicate .color-bg-green{background-color:#1b9638!important}.theme-syndicate .color-bg-teal{background-color:#009a93!important}.theme-syndicate .color-bg-blue{background-color:#1c71b1!important}.theme-syndicate .color-bg-violet{background-color:#552dab!important}.theme-syndicate .color-bg-purple{background-color:#8b2baa!important}.theme-syndicate .color-bg-pink{background-color:#cf2082!important}.theme-syndicate .color-bg-brown{background-color:#8c5836!important}.theme-syndicate .color-bg-grey{background-color:#646464!important}.theme-syndicate .color-bg-good{background-color:#4d9121!important}.theme-syndicate .color-bg-average{background-color:#cd7a0d!important}.theme-syndicate .color-bg-bad{background-color:#bd2020!important}.theme-syndicate .color-bg-label{background-color:#9d6f6f!important}.theme-syndicate .color-bg-gold{background-color:#d6920c!important}.theme-syndicate .Section{position:relative;margin-bottom:.5em;background-color:#2b0101;box-sizing:border-box}.theme-syndicate .Section:last-child{margin-bottom:0}.theme-syndicate .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #397439}.theme-syndicate .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-syndicate .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-syndicate .Section__rest{position:relative}.theme-syndicate .Section__content{padding:.66em .5em}.theme-syndicate .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-syndicate .Section--fill{display:flex;flex-direction:column;height:100%}.theme-syndicate .Section--fill>.Section__rest{flex-grow:1}.theme-syndicate .Section--fill>.Section__rest>.Section__content{height:100%}.theme-syndicate .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-syndicate .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-syndicate .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-syndicate .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-syndicate .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-syndicate .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-syndicate .Section .Section:first-child{margin-top:-.5em}.theme-syndicate .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-syndicate .Section .Section .Section .Section__titleText{font-size:1em}.theme-syndicate .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-syndicate .Button:last-child{margin-right:0;margin-bottom:0}.theme-syndicate .Button .fa,.theme-syndicate .Button .fas,.theme-syndicate .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-syndicate .Button--hasContent .fa,.theme-syndicate .Button--hasContent .fas,.theme-syndicate .Button--hasContent .far{margin-right:.25em}.theme-syndicate .Button--hasContent.Button--iconRight .fa,.theme-syndicate .Button--hasContent.Button--iconRight .fas,.theme-syndicate .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-syndicate .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-syndicate .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-syndicate .Button--circular{border-radius:50%}.theme-syndicate .Button--compact{padding:0 .25em;line-height:1.333em}.theme-syndicate .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-syndicate .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-syndicate .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--black:hover{background-color:#101010;color:#fff}.theme-syndicate .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-syndicate .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-syndicate .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-syndicate .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-syndicate .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-syndicate .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-syndicate .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-syndicate .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-syndicate .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-syndicate .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-syndicate .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-syndicate .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-syndicate .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-syndicate .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-syndicate .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-syndicate .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-syndicate .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-syndicate .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-syndicate .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-syndicate .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-syndicate .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-syndicate .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-syndicate .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-syndicate .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-syndicate .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-syndicate .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-syndicate .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-syndicate .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-syndicate .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-syndicate .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-syndicate .Button--color--label{transition:color .1s,background-color .1s;background-color:#9d6f6f;color:#fff}.theme-syndicate .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--label:hover{background-color:#b89696;color:#fff}.theme-syndicate .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-syndicate .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-syndicate .Button--color--default{transition:color .1s,background-color .1s;background-color:#397439;color:#fff}.theme-syndicate .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--default:hover{background-color:#509350;color:#fff}.theme-syndicate .Button--color--caution{transition:color .1s,background-color .1s;background-color:#be6209;color:#fff}.theme-syndicate .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--caution:hover{background-color:#e67f1a;color:#fff}.theme-syndicate .Button--color--danger{transition:color .1s,background-color .1s;background-color:#9a9d00;color:#fff}.theme-syndicate .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--danger:hover{background-color:#bec110;color:#fff}.theme-syndicate .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,0);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--transparent:hover{background-color:rgba(103,14,14,.81);color:#fff}.theme-syndicate .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(77,2,2,.6);color:rgba(255,255,255,.5)}.theme-syndicate .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--color--translucent:hover{background-color:rgba(105,20,20,.925);color:#fff}.theme-syndicate .Button--disabled{background-color:#363636!important}.theme-syndicate .Button--selected{transition:color .1s,background-color .1s;background-color:#9d0808;color:#fff}.theme-syndicate .Button--selected:focus{transition:color .25s,background-color .25s}.theme-syndicate .Button--selected:hover{background-color:#c11919;color:#fff}.theme-syndicate .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-syndicate .NoticeBox{padding:.33em .5em;margin-bottom:.5em;box-shadow:none;font-weight:700;font-style:italic;color:#fff;background-color:#910101;background-image:repeating-linear-gradient(-45deg,transparent,transparent .8333333333em,rgba(0,0,0,.1) .8333333333em,rgba(0,0,0,.1) 1.6666666667em)}.theme-syndicate .NoticeBox--color--black{color:#fff;background-color:#000}.theme-syndicate .NoticeBox--color--white{color:#000;background-color:#b3b3b3}.theme-syndicate .NoticeBox--color--red{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--orange{color:#fff;background-color:#854114}.theme-syndicate .NoticeBox--color--yellow{color:#000;background-color:#83710d}.theme-syndicate .NoticeBox--color--olive{color:#000;background-color:#576015}.theme-syndicate .NoticeBox--color--green{color:#fff;background-color:#174e24}.theme-syndicate .NoticeBox--color--teal{color:#fff;background-color:#064845}.theme-syndicate .NoticeBox--color--blue{color:#fff;background-color:#1b4565}.theme-syndicate .NoticeBox--color--violet{color:#fff;background-color:#3b2864}.theme-syndicate .NoticeBox--color--purple{color:#fff;background-color:#542663}.theme-syndicate .NoticeBox--color--pink{color:#fff;background-color:#802257}.theme-syndicate .NoticeBox--color--brown{color:#fff;background-color:#4c3729}.theme-syndicate .NoticeBox--color--grey{color:#fff;background-color:#3e3e3e}.theme-syndicate .NoticeBox--color--good{color:#fff;background-color:#2e4b1a}.theme-syndicate .NoticeBox--color--average{color:#fff;background-color:#7b4e13}.theme-syndicate .NoticeBox--color--bad{color:#fff;background-color:#701f1f}.theme-syndicate .NoticeBox--color--label{color:#fff;background-color:#635c5c}.theme-syndicate .NoticeBox--color--gold{color:#fff;background-color:#825d13}.theme-syndicate .NoticeBox--type--info{color:#fff;background-color:#235982}.theme-syndicate .NoticeBox--type--success{color:#fff;background-color:#1e662f}.theme-syndicate .NoticeBox--type--warning{color:#fff;background-color:#a95219}.theme-syndicate .NoticeBox--type--danger{color:#fff;background-color:#8f2828}.theme-syndicate .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;color:#87ce87;background-color:#0a0a0a;padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-syndicate .NumberInput--fluid{display:block}.theme-syndicate .NumberInput__content{margin-left:.5em}.theme-syndicate .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-syndicate .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #87ce87;background-color:#87ce87}.theme-syndicate .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:#0a0a0a;color:#fff;text-align:right}.theme-syndicate .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-syndicate .Input--disabled{color:#777;border-color:#6b6b6b;border-color:rgba(107,107,107,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-syndicate .Input--fluid{display:block;width:auto}.theme-syndicate .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-syndicate .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-syndicate .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-syndicate .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-syndicate .TextArea{position:relative;display:inline-block;border:.0833333333em solid #87ce87;border:.0833333333em solid rgba(135,206,135,.75);border-radius:.16em;background-color:#0a0a0a;margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-syndicate .TextArea--fluid{display:block;width:auto;height:auto}.theme-syndicate .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-syndicate .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-syndicate .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-syndicate .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-syndicate .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-syndicate .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-syndicate .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-syndicate .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-syndicate .Knob__popupValue,.theme-syndicate .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .Knob__popupValue--right{top:.25rem;right:-50%}.theme-syndicate .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-syndicate .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-syndicate .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-syndicate .Knob__ringFillPivot{transform:rotate(135deg)}.theme-syndicate .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-syndicate .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-syndicate .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-syndicate .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-syndicate .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-syndicate .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-syndicate .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-syndicate .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-syndicate .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-syndicate .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-syndicate .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-syndicate .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-syndicate .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-syndicate .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-syndicate .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-syndicate .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-syndicate .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-syndicate .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-syndicate .Knob--color--label .Knob__ringFill{stroke:#b89797}.theme-syndicate .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-syndicate .Slider:not(.Slider__disabled){cursor:e-resize}.theme-syndicate .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-syndicate .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-syndicate .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-syndicate .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-syndicate .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,.5);transition:border-color .5s}.theme-syndicate .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-syndicate .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-syndicate .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-syndicate .ProgressBar--color--default{border:.0833333333em solid #306330}.theme-syndicate .ProgressBar--color--default .ProgressBar__fill{background-color:#306330}.theme-syndicate .ProgressBar--color--disabled{border:1px solid #999}.theme-syndicate .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-syndicate .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-syndicate .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-syndicate .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-syndicate .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-syndicate .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-syndicate .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-syndicate .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-syndicate .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-syndicate .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-syndicate .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-syndicate .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-syndicate .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-syndicate .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-syndicate .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-syndicate .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-syndicate .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-syndicate .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-syndicate .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-syndicate .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-syndicate .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-syndicate .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-syndicate .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-syndicate .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-syndicate .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-syndicate .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-syndicate .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-syndicate .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-syndicate .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-syndicate .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-syndicate .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-syndicate .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-syndicate .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-syndicate .ProgressBar--color--label{border:.0833333333em solid #9d6f6f!important}.theme-syndicate .ProgressBar--color--label .ProgressBar__fill{background-color:#9d6f6f}.theme-syndicate .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-syndicate .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-syndicate .Chat{color:#abc6ec}.theme-syndicate .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-syndicate .Chat__badge:before{content:"x"}.theme-syndicate .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-syndicate .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-syndicate .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-syndicate .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#db2828;background-color:#2b0101}.theme-syndicate .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-syndicate .Chat__highlight{color:#000}.theme-syndicate .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-syndicate .ChatMessage{word-wrap:break-word}.theme-syndicate .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-syndicate .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-syndicate html,.theme-syndicate body{scrollbar-color:#770303 #3a0202}.theme-syndicate .Layout,.theme-syndicate .Layout *{scrollbar-base-color:#3a0202;scrollbar-face-color:#770303;scrollbar-3dlight-color:#4d0202;scrollbar-highlight-color:#4d0202;scrollbar-track-color:#3a0202;scrollbar-arrow-color:#fa2d2d;scrollbar-shadow-color:#770303}.theme-syndicate .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-syndicate .Layout__content--flexRow{display:flex;flex-flow:row}.theme-syndicate .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-syndicate .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-syndicate .Layout__content--noMargin{margin:0}.theme-syndicate .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#4d0202;background-image:linear-gradient(to bottom,#4d0202,#4d0202)}.theme-syndicate .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-syndicate .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-syndicate .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-syndicate .Window__contentPadding:after{height:0}.theme-syndicate .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-syndicate .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(108,22,22,.25);pointer-events:none}.theme-syndicate .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-syndicate .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-syndicate .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-syndicate .TitleBar{background-color:#910101;border-bottom:1px solid #161616;box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-syndicate .TitleBar__clickable{color:rgba(255,255,255,.5);background-color:#910101;transition:color .25s,background-color .25s}.theme-syndicate .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-syndicate .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,255,255,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-syndicate .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-syndicate .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-syndicate .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-syndicate .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-syndicate .adminooc{color:#29ccbe}.theme-syndicate .debug{color:#8f39e6}.theme-syndicate .boxed_message{background:rgba(20,20,35,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-syndicate .boxed_message.left_align_text{text-align:left}.theme-syndicate .boxed_message.red_border{background:rgba(0,0,0,.2);border-color:red}.theme-syndicate .boxed_message.green_border{background:rgba(0,75,0,.25);border-color:#0f0}.theme-syndicate .boxed_message.purple_border{background:rgba(25,0,50,.25);border-color:#8000ff}.theme-syndicate .boxed_message.notice_border{background:rgba(0,0,75,.25);border-color:#6685f5}.theme-syndicate .boxed_message.thick_border{border-width:thick}.theme-paradise .color-black{color:#1a1a1a!important}.theme-paradise .color-white{color:#fff!important}.theme-paradise .color-red{color:#df3e3e!important}.theme-paradise .color-orange{color:#f37f33!important}.theme-paradise .color-yellow{color:#fbda21!important}.theme-paradise .color-olive{color:#cbe41c!important}.theme-paradise .color-green{color:#25ca4c!important}.theme-paradise .color-teal{color:#00d6cc!important}.theme-paradise .color-blue{color:#2e93de!important}.theme-paradise .color-violet{color:#7349cf!important}.theme-paradise .color-purple{color:#ad45d0!important}.theme-paradise .color-pink{color:#e34da1!important}.theme-paradise .color-brown{color:#b97447!important}.theme-paradise .color-grey{color:#848484!important}.theme-paradise .color-good{color:#68c22d!important}.theme-paradise .color-average{color:#f29a29!important}.theme-paradise .color-bad{color:#df3e3e!important}.theme-paradise .color-label{color:#b8a497!important}.theme-paradise .color-gold{color:#f3b22f!important}.theme-paradise .color-bg-black{background-color:#000!important}.theme-paradise .color-bg-white{background-color:#d9d9d9!important}.theme-paradise .color-bg-red{background-color:#bd2020!important}.theme-paradise .color-bg-orange{background-color:#d95e0c!important}.theme-paradise .color-bg-yellow{background-color:#d9b804!important}.theme-paradise .color-bg-olive{background-color:#9aad14!important}.theme-paradise .color-bg-green{background-color:#1b9638!important}.theme-paradise .color-bg-teal{background-color:#009a93!important}.theme-paradise .color-bg-blue{background-color:#1c71b1!important}.theme-paradise .color-bg-violet{background-color:#552dab!important}.theme-paradise .color-bg-purple{background-color:#8b2baa!important}.theme-paradise .color-bg-pink{background-color:#cf2082!important}.theme-paradise .color-bg-brown{background-color:#8c5836!important}.theme-paradise .color-bg-grey{background-color:#646464!important}.theme-paradise .color-bg-good{background-color:#4d9121!important}.theme-paradise .color-bg-average{background-color:#cd7a0d!important}.theme-paradise .color-bg-bad{background-color:#bd2020!important}.theme-paradise .color-bg-label{background-color:#9d826f!important}.theme-paradise .color-bg-gold{background-color:#d6920c!important}.theme-paradise .Section{position:relative;margin-bottom:.5em;background-color:#40071a;background-color:rgba(0,0,0,.5);box-sizing:border-box}.theme-paradise .Section:last-child{margin-bottom:0}.theme-paradise .Section__title{position:relative;padding:.5em;border-bottom:.1666666667em solid #208080}.theme-paradise .Section__titleText{font-size:1.1666666667em;font-weight:700;color:#fff}.theme-paradise .Section__buttons{position:absolute;display:inline-block;right:.5em;margin-top:-.0833333333em}.theme-paradise .Section__rest{position:relative}.theme-paradise .Section__content{padding:.66em .5em}.theme-paradise .Section--fitted>.Section__rest>.Section__content{padding:0}.theme-paradise .Section--fill{display:flex;flex-direction:column;height:100%}.theme-paradise .Section--fill>.Section__rest{flex-grow:1}.theme-paradise .Section--fill>.Section__rest>.Section__content{height:100%}.theme-paradise .Section--fill.Section--scrollable>.Section__rest>.Section__content{position:absolute;top:0;left:0;right:0;bottom:0}.theme-paradise .Section--fill.Section--iefix{display:table!important;width:100%!important;height:100%!important;border-collapse:collapse;border-spacing:0}.theme-paradise .Section--fill.Section--iefix>.Section__rest{display:table-row!important;height:100%!important}.theme-paradise .Section--scrollable{overflow-x:hidden;overflow-y:hidden}.theme-paradise .Section--scrollable>.Section__rest>.Section__content{overflow-y:auto;overflow-x:hidden}.theme-paradise .Section .Section{background-color:rgba(0,0,0,0);margin-left:-.5em;margin-right:-.5em}.theme-paradise .Section .Section:first-child{margin-top:-.5em}.theme-paradise .Section .Section .Section__titleText{font-size:1.0833333333em}.theme-paradise .Section .Section .Section .Section__titleText{font-size:1em}.theme-paradise .Button{position:relative;display:inline-block;line-height:1.667em;padding:0 .5em;margin-right:.1666666667em;white-space:nowrap;outline:0;border-radius:.16em;margin-bottom:.1666666667em;user-select:none;-ms-user-select:none}.theme-paradise .Button:last-child{margin-right:0;margin-bottom:0}.theme-paradise .Button .fa,.theme-paradise .Button .fas,.theme-paradise .Button .far{margin-left:-.25em;margin-right:-.25em;min-width:1.333em;text-align:center}.theme-paradise .Button--hasContent .fa,.theme-paradise .Button--hasContent .fas,.theme-paradise .Button--hasContent .far{margin-right:.25em}.theme-paradise .Button--hasContent.Button--iconRight .fa,.theme-paradise .Button--hasContent.Button--iconRight .fas,.theme-paradise .Button--hasContent.Button--iconRight .far{margin-right:0;margin-left:.25em}.theme-paradise .Button--ellipsis{overflow:hidden;text-overflow:ellipsis}.theme-paradise .Button--fluid{display:block;margin-left:0;margin-right:0}.theme-paradise .Button--circular{border-radius:50%}.theme-paradise .Button--compact{padding:0 .25em;line-height:1.333em}.theme-paradise .Button--multiLine{white-space:normal;word-wrap:break-word}.theme-paradise .Button--color--black{transition:color .1s,background-color .1s;background-color:#000;color:#fff}.theme-paradise .Button--color--black:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--black:hover{background-color:#101010;color:#fff}.theme-paradise .Button--color--white{transition:color .1s,background-color .1s;background-color:#d9d9d9;color:#000}.theme-paradise .Button--color--white:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--white:hover{background-color:#f8f8f8;color:#000}.theme-paradise .Button--color--red{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--red:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--red:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--orange{transition:color .1s,background-color .1s;background-color:#d95e0c;color:#fff}.theme-paradise .Button--color--orange:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--orange:hover{background-color:#ef7e33;color:#fff}.theme-paradise .Button--color--yellow{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--yellow:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--yellow:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--olive{transition:color .1s,background-color .1s;background-color:#9aad14;color:#fff}.theme-paradise .Button--color--olive:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--olive:hover{background-color:#bdd327;color:#fff}.theme-paradise .Button--color--green{transition:color .1s,background-color .1s;background-color:#1b9638;color:#fff}.theme-paradise .Button--color--green:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--green:hover{background-color:#2fb94f;color:#fff}.theme-paradise .Button--color--teal{transition:color .1s,background-color .1s;background-color:#009a93;color:#fff}.theme-paradise .Button--color--teal:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--teal:hover{background-color:#10bdb6;color:#fff}.theme-paradise .Button--color--blue{transition:color .1s,background-color .1s;background-color:#1c71b1;color:#fff}.theme-paradise .Button--color--blue:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--blue:hover{background-color:#308fd6;color:#fff}.theme-paradise .Button--color--violet{transition:color .1s,background-color .1s;background-color:#552dab;color:#fff}.theme-paradise .Button--color--violet:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--violet:hover{background-color:#7249ca;color:#fff}.theme-paradise .Button--color--purple{transition:color .1s,background-color .1s;background-color:#8b2baa;color:#fff}.theme-paradise .Button--color--purple:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--purple:hover{background-color:#aa46ca;color:#fff}.theme-paradise .Button--color--pink{transition:color .1s,background-color .1s;background-color:#cf2082;color:#fff}.theme-paradise .Button--color--pink:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--pink:hover{background-color:#e04ca0;color:#fff}.theme-paradise .Button--color--brown{transition:color .1s,background-color .1s;background-color:#8c5836;color:#fff}.theme-paradise .Button--color--brown:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--brown:hover{background-color:#ae724c;color:#fff}.theme-paradise .Button--color--grey{transition:color .1s,background-color .1s;background-color:#646464;color:#fff}.theme-paradise .Button--color--grey:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--grey:hover{background-color:#818181;color:#fff}.theme-paradise .Button--color--good{transition:color .1s,background-color .1s;background-color:#4d9121;color:#fff}.theme-paradise .Button--color--good:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--good:hover{background-color:#67b335;color:#fff}.theme-paradise .Button--color--average{transition:color .1s,background-color .1s;background-color:#cd7a0d;color:#fff}.theme-paradise .Button--color--average:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--average:hover{background-color:#eb972b;color:#fff}.theme-paradise .Button--color--bad{transition:color .1s,background-color .1s;background-color:#bd2020;color:#fff}.theme-paradise .Button--color--bad:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--bad:hover{background-color:#d93f3f;color:#fff}.theme-paradise .Button--color--label{transition:color .1s,background-color .1s;background-color:#9d826f;color:#fff}.theme-paradise .Button--color--label:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--label:hover{background-color:#b8a396;color:#fff}.theme-paradise .Button--color--gold{transition:color .1s,background-color .1s;background-color:#d6920c;color:#fff}.theme-paradise .Button--color--gold:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--gold:hover{background-color:#eeaf30;color:#fff}.theme-paradise .Button--color--default{transition:color .1s,background-color .1s;background-color:#208080;color:#fff}.theme-paradise .Button--color--default:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--default:hover{background-color:#34a0a0;color:#fff}.theme-paradise .Button--color--caution{transition:color .1s,background-color .1s;background-color:#d9b804;color:#000}.theme-paradise .Button--color--caution:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--caution:hover{background-color:#f5d523;color:#000}.theme-paradise .Button--color--danger{transition:color .1s,background-color .1s;background-color:#8c1eff;color:#fff}.theme-paradise .Button--color--danger:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--danger:hover{background-color:#ae61ff;color:#fff}.theme-paradise .Button--color--transparent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,0);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--transparent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--transparent:hover{background-color:rgba(164,27,73,.81);color:#fff}.theme-paradise .Button--color--translucent{transition:color .1s,background-color .1s;background-color:rgba(128,13,51,.6);color:rgba(255,255,255,.5)}.theme-paradise .Button--color--translucent:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--color--translucent:hover{background-color:rgba(164,32,76,.925);color:#fff}.theme-paradise .Button--disabled{background-color:#999!important}.theme-paradise .Button--selected{transition:color .1s,background-color .1s;background-color:#bf6030;color:#fff}.theme-paradise .Button--selected:focus{transition:color .25s,background-color .25s}.theme-paradise .Button--selected:hover{background-color:#d4835a;color:#fff}.theme-paradise .Button--modal{float:right;z-index:1;margin-top:-.5rem}.theme-paradise .NumberInput{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;color:#e65c2e;background-color:rgba(0,0,0,.25);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;text-align:right;overflow:visible;cursor:n-resize}.theme-paradise .NumberInput--fluid{display:block}.theme-paradise .NumberInput__content{margin-left:.5em}.theme-paradise .NumberInput__barContainer{position:absolute;top:.1666666667em;bottom:.1666666667em;left:.1666666667em}.theme-paradise .NumberInput__bar{position:absolute;bottom:0;left:0;width:.25em;box-sizing:border-box;border-bottom:.0833333333em solid #e65c2e;background-color:#e65c2e}.theme-paradise .NumberInput__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,.25);color:#fff;text-align:right}.theme-paradise .Input{position:relative;display:inline-block;width:10em;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);color:#fff;background-color:#000;background-color:rgba(0,0,0,.75);padding:0 .3333333333em;margin-right:.1666666667em;line-height:1.4166666667em;overflow:visible;white-space:nowrap}.theme-paradise .Input--disabled{color:#777;border-color:#4a4a4a;border-color:rgba(74,74,74,.75);background-color:#333;background-color:rgba(0,0,0,.25)}.theme-paradise .Input--fluid{display:block;width:auto}.theme-paradise .Input__baseline{display:inline-block;color:rgba(0,0,0,0)}.theme-paradise .Input__input{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;font-size:1em;line-height:1.4166666667em;height:1.4166666667em;margin:0;padding:0 .5em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit}.theme-paradise .Input__input::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__input:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea{border:0;width:calc(100% + 4px);font-size:1em;line-height:1.4166666667em;margin-left:-.3333333333em;font-family:Verdana,sans-serif;background-color:rgba(0,0,0,0);color:#fff;color:inherit;resize:both;overflow:auto;white-space:pre-wrap}.theme-paradise .Input__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input__textarea:-ms-input-placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .Input--monospace .Input__input{font-family:Consolas,monospace}.theme-paradise .TextArea{position:relative;display:inline-block;border:.0833333333em solid #e65c2e;border:.0833333333em solid rgba(230,92,46,.75);border-radius:.16em;background-color:rgba(0,0,0,.25);margin-right:.1666666667em;line-height:1.4166666667em;box-sizing:border-box;width:100%}.theme-paradise .TextArea--fluid{display:block;width:auto;height:auto}.theme-paradise .TextArea__textarea{display:block;position:absolute;top:0;bottom:0;left:0;right:0;border:0;outline:0;width:100%;height:100%;font-size:1em;line-height:1.4166666667em;min-height:1.4166666667em;margin:0;padding:0 .5em;font-family:inherit;background-color:rgba(0,0,0,0);color:inherit;box-sizing:border-box;word-wrap:break-word;overflow:hidden}.theme-paradise .TextArea__textarea::placeholder{font-style:italic;color:#777;color:rgba(255,255,255,.45)}.theme-paradise .TextArea__textarea:-ms-input-placeholder{font-style:italic;color:rgba(125,125,125,.75)}.theme-paradise .Knob{position:relative;font-size:1rem;width:2.6em;height:2.6em;margin:0 auto -.2em;cursor:n-resize}.theme-paradise .Knob:after{content:".";color:rgba(0,0,0,0);line-height:2.5em}.theme-paradise .Knob__circle{position:absolute;top:.1em;bottom:.1em;left:.1em;right:.1em;margin:.3em;background-color:#333;background-image:linear-gradient(to bottom,rgba(255,255,255,.15),rgba(255,255,255,0));border-radius:50%;box-shadow:0 .05em .5em rgba(0,0,0,.5)}.theme-paradise .Knob__cursorBox{position:absolute;top:0;bottom:0;left:0;right:0}.theme-paradise .Knob__cursor{position:relative;top:.05em;margin:0 auto;width:.2em;height:.8em;background-color:rgba(255,255,255,.9)}.theme-paradise .Knob__popupValue,.theme-paradise .Knob__popupValue--right{position:absolute;top:-2rem;right:50%;font-size:1rem;text-align:center;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .Knob__popupValue--right{top:.25rem;right:-50%}.theme-paradise .Knob__ring{position:absolute;top:0;bottom:0;left:0;right:0;padding:.1em}.theme-paradise .Knob__ringTrackPivot{transform:rotate(135deg)}.theme-paradise .Knob__ringTrack{fill:rgba(0,0,0,0);stroke:rgba(255,255,255,.1);stroke-width:8;stroke-linecap:round;stroke-dasharray:235.62}.theme-paradise .Knob__ringFillPivot{transform:rotate(135deg)}.theme-paradise .Knob--bipolar .Knob__ringFillPivot{transform:rotate(270deg)}.theme-paradise .Knob__ringFill{fill:rgba(0,0,0,0);stroke:#6a96c9;stroke-width:8;stroke-linecap:round;stroke-dasharray:314.16;transition:stroke 50ms}.theme-paradise .Knob--color--black .Knob__ringFill{stroke:#1a1a1a}.theme-paradise .Knob--color--white .Knob__ringFill{stroke:#fff}.theme-paradise .Knob--color--red .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--orange .Knob__ringFill{stroke:#f37f33}.theme-paradise .Knob--color--yellow .Knob__ringFill{stroke:#fbda21}.theme-paradise .Knob--color--olive .Knob__ringFill{stroke:#cbe41c}.theme-paradise .Knob--color--green .Knob__ringFill{stroke:#25ca4c}.theme-paradise .Knob--color--teal .Knob__ringFill{stroke:#00d6cc}.theme-paradise .Knob--color--blue .Knob__ringFill{stroke:#2e93de}.theme-paradise .Knob--color--violet .Knob__ringFill{stroke:#7349cf}.theme-paradise .Knob--color--purple .Knob__ringFill{stroke:#ad45d0}.theme-paradise .Knob--color--pink .Knob__ringFill{stroke:#e34da1}.theme-paradise .Knob--color--brown .Knob__ringFill{stroke:#b97447}.theme-paradise .Knob--color--grey .Knob__ringFill{stroke:#848484}.theme-paradise .Knob--color--good .Knob__ringFill{stroke:#68c22d}.theme-paradise .Knob--color--average .Knob__ringFill{stroke:#f29a29}.theme-paradise .Knob--color--bad .Knob__ringFill{stroke:#df3e3e}.theme-paradise .Knob--color--label .Knob__ringFill{stroke:#b8a497}.theme-paradise .Knob--color--gold .Knob__ringFill{stroke:#f3b22f}.theme-paradise .Slider:not(.Slider__disabled){cursor:e-resize}.theme-paradise .Slider__cursorOffset{position:absolute;top:0;left:0;bottom:0;transition:none!important}.theme-paradise .Slider__cursor{position:absolute;top:0;right:-.0833333333em;bottom:0;width:0;border-left:.1666666667em solid #fff}.theme-paradise .Slider__pointer{position:absolute;right:-.4166666667em;bottom:-.3333333333em;width:0;height:0;border-left:.4166666667em solid rgba(0,0,0,0);border-right:.4166666667em solid rgba(0,0,0,0);border-bottom:.4166666667em solid #fff}.theme-paradise .Slider__popupValue{position:absolute;right:0;top:-2rem;font-size:1rem;padding:.25rem .5rem;color:#fff;background-color:#000;transform:translate(50%);white-space:nowrap}.theme-paradise .ProgressBar{display:inline-block;position:relative;width:100%;padding:0 .5em;border-radius:.16em;background-color:rgba(0,0,0,0);transition:border-color .5s}.theme-paradise .ProgressBar__fill{position:absolute;top:-.5px;left:0;bottom:-.5px}.theme-paradise .ProgressBar__fill--animated{transition:background-color .5s,width .5s}.theme-paradise .ProgressBar__content{position:relative;line-height:1.4166666667em;width:100%;text-align:right}.theme-paradise .ProgressBar--color--default{border:.0833333333em solid #1b6d6d}.theme-paradise .ProgressBar--color--default .ProgressBar__fill{background-color:#1b6d6d}.theme-paradise .ProgressBar--color--disabled{border:1px solid #999}.theme-paradise .ProgressBar--color--disabled .ProgressBar__fill{background-color:#999}.theme-paradise .ProgressBar--color--black{border:.0833333333em solid #000!important}.theme-paradise .ProgressBar--color--black .ProgressBar__fill{background-color:#000}.theme-paradise .ProgressBar--color--white{border:.0833333333em solid #d9d9d9!important}.theme-paradise .ProgressBar--color--white .ProgressBar__fill{background-color:#d9d9d9}.theme-paradise .ProgressBar--color--red{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--red .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--orange{border:.0833333333em solid #d95e0c!important}.theme-paradise .ProgressBar--color--orange .ProgressBar__fill{background-color:#d95e0c}.theme-paradise .ProgressBar--color--yellow{border:.0833333333em solid #d9b804!important}.theme-paradise .ProgressBar--color--yellow .ProgressBar__fill{background-color:#d9b804}.theme-paradise .ProgressBar--color--olive{border:.0833333333em solid #9aad14!important}.theme-paradise .ProgressBar--color--olive .ProgressBar__fill{background-color:#9aad14}.theme-paradise .ProgressBar--color--green{border:.0833333333em solid #1b9638!important}.theme-paradise .ProgressBar--color--green .ProgressBar__fill{background-color:#1b9638}.theme-paradise .ProgressBar--color--teal{border:.0833333333em solid #009a93!important}.theme-paradise .ProgressBar--color--teal .ProgressBar__fill{background-color:#009a93}.theme-paradise .ProgressBar--color--blue{border:.0833333333em solid #1c71b1!important}.theme-paradise .ProgressBar--color--blue .ProgressBar__fill{background-color:#1c71b1}.theme-paradise .ProgressBar--color--violet{border:.0833333333em solid #552dab!important}.theme-paradise .ProgressBar--color--violet .ProgressBar__fill{background-color:#552dab}.theme-paradise .ProgressBar--color--purple{border:.0833333333em solid #8b2baa!important}.theme-paradise .ProgressBar--color--purple .ProgressBar__fill{background-color:#8b2baa}.theme-paradise .ProgressBar--color--pink{border:.0833333333em solid #cf2082!important}.theme-paradise .ProgressBar--color--pink .ProgressBar__fill{background-color:#cf2082}.theme-paradise .ProgressBar--color--brown{border:.0833333333em solid #8c5836!important}.theme-paradise .ProgressBar--color--brown .ProgressBar__fill{background-color:#8c5836}.theme-paradise .ProgressBar--color--grey{border:.0833333333em solid #646464!important}.theme-paradise .ProgressBar--color--grey .ProgressBar__fill{background-color:#646464}.theme-paradise .ProgressBar--color--good{border:.0833333333em solid #4d9121!important}.theme-paradise .ProgressBar--color--good .ProgressBar__fill{background-color:#4d9121}.theme-paradise .ProgressBar--color--average{border:.0833333333em solid #cd7a0d!important}.theme-paradise .ProgressBar--color--average .ProgressBar__fill{background-color:#cd7a0d}.theme-paradise .ProgressBar--color--bad{border:.0833333333em solid #bd2020!important}.theme-paradise .ProgressBar--color--bad .ProgressBar__fill{background-color:#bd2020}.theme-paradise .ProgressBar--color--label{border:.0833333333em solid #9d826f!important}.theme-paradise .ProgressBar--color--label .ProgressBar__fill{background-color:#9d826f}.theme-paradise .ProgressBar--color--gold{border:.0833333333em solid #d6920c!important}.theme-paradise .ProgressBar--color--gold .ProgressBar__fill{background-color:#d6920c}.theme-paradise .Chat{color:#abc6ec}.theme-paradise .Chat__badge{display:inline-block;min-width:.5em;font-size:.7em;padding:.2em .3em;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#dc143c;border-radius:10px;transition:font-size .2s}.theme-paradise .Chat__badge:before{content:"x"}.theme-paradise .Chat__badge--animate{font-size:.9em;transition:font-size 0ms}.theme-paradise .Chat__scrollButton{position:fixed;right:2em;bottom:1em}.theme-paradise .Chat__reconnected{font-size:.85em;text-align:center;margin:1em 0 2em}.theme-paradise .Chat__reconnected:before{content:"Reconnected";display:inline-block;border-radius:1em;padding:0 .7em;color:#fff;background-color:#db2828}.theme-paradise .Chat__reconnected:after{content:"";display:block;margin-top:-.75em;border-bottom:.1666666667em solid #db2828}.theme-paradise .Chat__highlight{color:#000}.theme-paradise .Chat__highlight--restricted{color:#fff;background-color:#a00;font-weight:700}.theme-paradise .ChatMessage{word-wrap:break-word}.theme-paradise .ChatMessage--highlighted{position:relative;border-left:.1666666667em solid #fd4;padding-left:.5em}.theme-paradise .ChatMessage--highlighted:after{content:"";position:absolute;top:0;bottom:0;left:0;right:0;background-color:rgba(255,221,68,.1);pointer-events:none}.theme-paradise html,.theme-paradise body{scrollbar-color:#cb1551 #680b29}.theme-paradise .Layout,.theme-paradise .Layout *{scrollbar-base-color:#680b29;scrollbar-face-color:#99103d;scrollbar-3dlight-color:#800d33;scrollbar-highlight-color:#800d33;scrollbar-track-color:#680b29;scrollbar-arrow-color:#ea2e6c;scrollbar-shadow-color:#99103d}.theme-paradise .Layout__content{position:absolute;top:0;bottom:0;left:0;right:0;overflow:hidden}.theme-paradise .Layout__content--flexRow{display:flex;flex-flow:row}.theme-paradise .Layout__content--flexColumn{display:flex;flex-flow:column}.theme-paradise .Layout__content--scrollable{overflow-y:auto;margin-bottom:0}.theme-paradise .Layout__content--noMargin{margin:0}.theme-paradise .Window{position:fixed;top:0;bottom:0;left:0;right:0;color:#fff;background-color:#800d33;background-image:linear-gradient(to bottom,#80014b,#80460d)}.theme-paradise .Window__titleBar{position:fixed;z-index:1;top:0;left:0;width:100%;height:32px;height:2.6666666667rem}.theme-paradise .Window__rest{position:fixed;top:32px;top:2.6666666667rem;bottom:0;left:0;right:0}.theme-paradise .Window__contentPadding{margin:.5rem;height:100%;height:calc(100% - 1.01rem)}.theme-paradise .Window__contentPadding:after{height:0}.theme-paradise .Layout__content--scrollable .Window__contentPadding:after{display:block;content:"";height:.5rem}.theme-paradise .Window__dimmer{position:fixed;top:0;bottom:0;left:0;right:0;background-color:rgba(166,34,78,.25);pointer-events:none}.theme-paradise .Window__resizeHandle__se{position:fixed;bottom:0;right:0;width:20px;width:1.6666666667rem;height:20px;height:1.6666666667rem;cursor:se-resize}.theme-paradise .Window__resizeHandle__s{position:fixed;bottom:0;left:0;right:0;height:6px;height:.5rem;cursor:s-resize}.theme-paradise .Window__resizeHandle__e{position:fixed;top:0;bottom:0;right:0;width:3px;width:.25rem;cursor:e-resize}.theme-paradise .TitleBar{background-color:#800d33;border-bottom:1px solid rgba(0,0,0,.25);box-shadow:0 2px 2px rgba(0,0,0,.1);box-shadow:0 .1666666667rem .1666666667rem rgba(0,0,0,.1);user-select:none;-ms-user-select:none}.theme-paradise .TitleBar__clickable{color:rgba(255,0,0,.5);background-color:#800d33;transition:color .25s,background-color .25s}.theme-paradise .TitleBar__clickable:hover{color:#fff;background-color:#c00;transition:color 0ms,background-color 0ms}.theme-paradise .TitleBar__title{position:absolute;top:0;left:46px;left:3.8333333333rem;color:rgba(255,0,0,.75);font-size:14px;font-size:1.1666666667rem;line-height:31px;line-height:2.5833333333rem;white-space:nowrap}.theme-paradise .TitleBar__dragZone{position:absolute;top:0;left:0;right:0;height:32px;height:2.6666666667rem}.theme-paradise .TitleBar__statusIcon{position:absolute;top:0;left:12px;left:1rem;transition:color .5s;font-size:20px;font-size:1.6666666667rem;line-height:32px!important;line-height:2.6666666667rem!important}.theme-paradise .TitleBar__close{position:absolute;top:-1px;right:0;width:45px;width:3.75rem;height:32px;height:2.6666666667rem;font-size:20px;font-size:1.6666666667rem;line-height:31px;line-height:2.5833333333rem;text-align:center}.theme-paradise .TitleBar__devBuildIndicator{position:absolute;top:6px;top:.5rem;right:52px;right:4.3333333333rem;min-width:20px;min-width:1.6666666667rem;padding:2px 4px;padding:.1666666667rem .3333333333rem;background-color:rgba(91,170,39,.75);color:#fff;text-align:center}.theme-paradise .adminooc{color:#29ccbe}.theme-paradise .debug{color:#8f39e6}.theme-paradise .boxed_message{background:rgba(0,0,0,.25);border:1px solid #a3b9d9;margin:.5em;padding:.5em .75em;text-align:center}.theme-paradise .boxed_message.left_align_text{text-align:left}.theme-paradise .boxed_message.red_border{background:rgba(0,0,0,.25);border-color:#a00}.theme-paradise .boxed_message.green_border{background:rgba(0,0,0,.25);border-color:#0f0}.theme-paradise .boxed_message.purple_border{background:rgba(0,0,0,.25);border-color:#8000ff}.theme-paradise .boxed_message.notice_border{background:rgba(0,0,0,.25);border-color:#6685f5}.theme-paradise .boxed_message.thick_border{border-width:thick} diff --git a/tgui/public/tgui-panel.bundle.js b/tgui/public/tgui-panel.bundle.js index 3c44e650054..2ba29938fbf 100644 --- a/tgui/public/tgui-panel.bundle.js +++ b/tgui/public/tgui-panel.bundle.js @@ -1,30 +1,30 @@ -(function(){(function(){var xn={96376:function(y,n,t){"use strict";n.__esModule=!0,n.createPopper=void 0,n.popperGenerator=p;var e=u(t(74758)),r=u(t(28811)),o=u(t(98309)),a=u(t(44896)),s=u(t(33118)),i=u(t(10579)),c=u(t(56500)),g=u(t(17633));n.detectOverflow=g.default;var f=t(75573);function u(h){return h&&h.__esModule?h:{default:h}}var v={placement:"bottom",modifiers:[],strategy:"absolute"};function l(){for(var h=arguments.length,m=new Array(h),E=0;E0&&(0,r.round)(u.width)/c.offsetWidth||1,l=c.offsetHeight>0&&(0,r.round)(u.height)/c.offsetHeight||1);var p=(0,e.isElement)(c)?(0,o.default)(c):window,d=p.visualViewport,h=!(0,a.default)()&&f,m=(u.left+(h&&d?d.offsetLeft:0))/v,E=(u.top+(h&&d?d.offsetTop:0))/l,A=u.width/v,I=u.height/l;return{width:A,height:I,top:E,right:m+A,bottom:E+I,left:m,x:m,y:E}}},49035:function(y,n,t){"use strict";n.__esModule=!0,n.default=I;var e=t(46206),r=h(t(87991)),o=h(t(79752)),a=h(t(98309)),s=h(t(44896)),i=h(t(40600)),c=h(t(16599)),g=t(75573),f=h(t(37786)),u=h(t(57819)),v=h(t(4206)),l=h(t(12972)),p=h(t(81666)),d=t(63618);function h(T){return T&&T.__esModule?T:{default:T}}function m(T,C){var S=(0,f.default)(T,!1,C==="fixed");return S.top=S.top+T.clientTop,S.left=S.left+T.clientLeft,S.bottom=S.top+T.clientHeight,S.right=S.left+T.clientWidth,S.width=T.clientWidth,S.height=T.clientHeight,S.x=S.left,S.y=S.top,S}function E(T,C,S){return C===e.viewport?(0,p.default)((0,r.default)(T,S)):(0,g.isElement)(C)?m(C,S):(0,p.default)((0,o.default)((0,i.default)(T)))}function A(T){var C=(0,a.default)((0,u.default)(T)),S=["absolute","fixed"].indexOf((0,c.default)(T).position)>=0,b=S&&(0,g.isHTMLElement)(T)?(0,s.default)(T):T;return(0,g.isElement)(b)?C.filter(function(N){return(0,g.isElement)(N)&&(0,v.default)(N,b)&&(0,l.default)(N)!=="body"}):[]}function I(T,C,S,b){var N=C==="clippingParents"?A(T):[].concat(C),M=[].concat(N,[S]),R=M[0],L=M.reduce(function(B,U){var x=E(T,U,b);return B.top=(0,d.max)(x.top,B.top),B.right=(0,d.min)(x.right,B.right),B.bottom=(0,d.min)(x.bottom,B.bottom),B.left=(0,d.max)(x.left,B.left),B},E(T,R,b));return L.width=L.right-L.left,L.height=L.bottom-L.top,L.x=L.left,L.y=L.top,L}},74758:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=f(t(37786)),r=f(t(13390)),o=f(t(12972)),a=t(75573),s=f(t(79697)),i=f(t(40600)),c=f(t(10798)),g=t(63618);function f(l){return l&&l.__esModule?l:{default:l}}function u(l){var p=l.getBoundingClientRect(),d=(0,g.round)(p.width)/l.offsetWidth||1,h=(0,g.round)(p.height)/l.offsetHeight||1;return d!==1||h!==1}function v(l,p,d){d===void 0&&(d=!1);var h=(0,a.isHTMLElement)(p),m=(0,a.isHTMLElement)(p)&&u(p),E=(0,i.default)(p),A=(0,e.default)(l,m,d),I={scrollLeft:0,scrollTop:0},T={x:0,y:0};return(h||!h&&!d)&&(((0,o.default)(p)!=="body"||(0,c.default)(E))&&(I=(0,r.default)(p)),(0,a.isHTMLElement)(p)?(T=(0,e.default)(p,!0),T.x+=p.clientLeft,T.y+=p.clientTop):E&&(T.x=(0,s.default)(E))),{x:A.left+I.scrollLeft-T.x,y:A.top+I.scrollTop-T.y,width:A.width,height:A.height}}},16599:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return(0,e.default)(a).getComputedStyle(a)}},40600:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(75573);function r(o){return(((0,e.isElement)(o)?o.ownerDocument:o.document)||window.document).documentElement}},79752:function(y,n,t){"use strict";n.__esModule=!0,n.default=c;var e=i(t(40600)),r=i(t(16599)),o=i(t(79697)),a=i(t(43750)),s=t(63618);function i(g){return g&&g.__esModule?g:{default:g}}function c(g){var f,u=(0,e.default)(g),v=(0,a.default)(g),l=(f=g.ownerDocument)==null?void 0:f.body,p=(0,s.max)(u.scrollWidth,u.clientWidth,l?l.scrollWidth:0,l?l.clientWidth:0),d=(0,s.max)(u.scrollHeight,u.clientHeight,l?l.scrollHeight:0,l?l.clientHeight:0),h=-v.scrollLeft+(0,o.default)(g),m=-v.scrollTop;return(0,r.default)(l||u).direction==="rtl"&&(h+=(0,s.max)(u.clientWidth,l?l.clientWidth:0)-p),{width:p,height:d,x:h,y:m}}},3073:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}}},28811:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(37786));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=a.offsetWidth,c=a.offsetHeight;return Math.abs(s.width-i)<=1&&(i=s.width),Math.abs(s.height-c)<=1&&(c=s.height),{x:a.offsetLeft,y:a.offsetTop,width:i,height:c}}},12972:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e?(e.nodeName||"").toLowerCase():null}},13390:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(43750)),r=s(t(95115)),o=t(75573),a=s(t(3073));function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return c===(0,r.default)(c)||!(0,o.isHTMLElement)(c)?(0,e.default)(c):(0,a.default)(c)}},44896:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=g(t(95115)),r=g(t(12972)),o=g(t(16599)),a=t(75573),s=g(t(87031)),i=g(t(57819)),c=g(t(35366));function g(l){return l&&l.__esModule?l:{default:l}}function f(l){return!(0,a.isHTMLElement)(l)||(0,o.default)(l).position==="fixed"?null:l.offsetParent}function u(l){var p=/firefox/i.test((0,c.default)()),d=/Trident/i.test((0,c.default)());if(d&&(0,a.isHTMLElement)(l)){var h=(0,o.default)(l);if(h.position==="fixed")return null}var m=(0,i.default)(l);for((0,a.isShadowRoot)(m)&&(m=m.host);(0,a.isHTMLElement)(m)&&["html","body"].indexOf((0,r.default)(m))<0;){var E=(0,o.default)(m);if(E.transform!=="none"||E.perspective!=="none"||E.contain==="paint"||["transform","perspective"].indexOf(E.willChange)!==-1||p&&E.willChange==="filter"||p&&E.filter&&E.filter!=="none")return m;m=m.parentNode}return null}function v(l){for(var p=(0,e.default)(l),d=f(l);d&&(0,s.default)(d)&&(0,o.default)(d).position==="static";)d=f(d);return d&&((0,r.default)(d)==="html"||(0,r.default)(d)==="body"&&(0,o.default)(d).position==="static")?p:d||u(l)||p}},57819:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(12972)),r=a(t(40600)),o=t(75573);function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)(i)==="html"?i:i.assignedSlot||i.parentNode||((0,o.isShadowRoot)(i)?i.host:null)||(0,r.default)(i)}},24426:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(57819)),r=s(t(10798)),o=s(t(12972)),a=t(75573);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){return["html","body","#document"].indexOf((0,o.default)(c))>=0?c.ownerDocument.body:(0,a.isHTMLElement)(c)&&(0,r.default)(c)?c:i((0,e.default)(c))}},87991:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(95115)),r=s(t(40600)),o=s(t(79697)),a=s(t(89331));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){var f=(0,e.default)(c),u=(0,r.default)(c),v=f.visualViewport,l=u.clientWidth,p=u.clientHeight,d=0,h=0;if(v){l=v.width,p=v.height;var m=(0,a.default)();(m||!m&&g==="fixed")&&(d=v.offsetLeft,h=v.offsetTop)}return{width:l,height:p,x:d+(0,o.default)(c),y:h}}},95115:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){if(e==null)return window;if(e.toString()!=="[object Window]"){var r=e.ownerDocument;return r&&r.defaultView||window}return e}},43750:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(95115));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.pageXOffset,c=s.pageYOffset;return{scrollLeft:i,scrollTop:c}}},79697:function(y,n,t){"use strict";n.__esModule=!0,n.default=s;var e=a(t(37786)),r=a(t(40600)),o=a(t(43750));function a(i){return i&&i.__esModule?i:{default:i}}function s(i){return(0,e.default)((0,r.default)(i)).left+(0,o.default)(i).scrollLeft}},75573:function(y,n,t){"use strict";n.__esModule=!0,n.isElement=o,n.isHTMLElement=a,n.isShadowRoot=s;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}function o(i){var c=(0,e.default)(i).Element;return i instanceof c||i instanceof Element}function a(i){var c=(0,e.default)(i).HTMLElement;return i instanceof c||i instanceof HTMLElement}function s(i){if(typeof ShadowRoot=="undefined")return!1;var c=(0,e.default)(i).ShadowRoot;return i instanceof c||i instanceof ShadowRoot}},89331:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(35366));function r(a){return a&&a.__esModule?a:{default:a}}function o(){return!/^((?!chrome|android).)*safari/i.test((0,e.default)())}},10798:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(16599));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){var s=(0,e.default)(a),i=s.overflow,c=s.overflowX,g=s.overflowY;return/auto|scroll|overlay|hidden/.test(i+g+c)}},87031:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(12972));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return["table","td","th"].indexOf((0,e.default)(a))>=0}},98309:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(24426)),r=s(t(57819)),o=s(t(95115)),a=s(t(10798));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){var f;g===void 0&&(g=[]);var u=(0,e.default)(c),v=u===((f=c.ownerDocument)==null?void 0:f.body),l=(0,o.default)(u),p=v?[l].concat(l.visualViewport||[],(0,a.default)(u)?u:[]):u,d=g.concat(p);return v?d:d.concat(i((0,r.default)(p)))}},46206:function(y,n){"use strict";n.__esModule=!0,n.write=n.viewport=n.variationPlacements=n.top=n.start=n.right=n.reference=n.read=n.popper=n.placements=n.modifierPhases=n.main=n.left=n.end=n.clippingParents=n.bottom=n.beforeWrite=n.beforeRead=n.beforeMain=n.basePlacements=n.auto=n.afterWrite=n.afterRead=n.afterMain=void 0;var t=n.top="top",e=n.bottom="bottom",r=n.right="right",o=n.left="left",a=n.auto="auto",s=n.basePlacements=[t,e,r,o],i=n.start="start",c=n.end="end",g=n.clippingParents="clippingParents",f=n.viewport="viewport",u=n.popper="popper",v=n.reference="reference",l=n.variationPlacements=s.reduce(function(N,M){return N.concat([M+"-"+i,M+"-"+c])},[]),p=n.placements=[].concat(s,[a]).reduce(function(N,M){return N.concat([M,M+"-"+i,M+"-"+c])},[]),d=n.beforeRead="beforeRead",h=n.read="read",m=n.afterRead="afterRead",E=n.beforeMain="beforeMain",A=n.main="main",I=n.afterMain="afterMain",T=n.beforeWrite="beforeWrite",C=n.write="write",S=n.afterWrite="afterWrite",b=n.modifierPhases=[d,h,m,E,A,I,T,C,S]},95996:function(y,n,t){"use strict";n.__esModule=!0;var e={popperGenerator:!0,detectOverflow:!0,createPopperBase:!0,createPopper:!0,createPopperLite:!0};n.popperGenerator=n.detectOverflow=n.createPopperLite=n.createPopperBase=n.createPopper=void 0;var r=t(46206);Object.keys(r).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===r[c]||(n[c]=r[c])});var o=t(39805);Object.keys(o).forEach(function(c){c==="default"||c==="__esModule"||Object.prototype.hasOwnProperty.call(e,c)||c in n&&n[c]===o[c]||(n[c]=o[c])});var a=t(96376);n.popperGenerator=a.popperGenerator,n.detectOverflow=a.detectOverflow,n.createPopperBase=a.createPopper;var s=t(83312);n.createPopper=s.createPopper;var i=t(2473);n.createPopperLite=i.createPopper},19975:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=o(t(12972)),r=t(75573);function o(c){return c&&c.__esModule?c:{default:c}}function a(c){var g=c.state;Object.keys(g.elements).forEach(function(f){var u=g.styles[f]||{},v=g.attributes[f]||{},l=g.elements[f];!(0,r.isHTMLElement)(l)||!(0,e.default)(l)||(Object.assign(l.style,u),Object.keys(v).forEach(function(p){var d=v[p];d===!1?l.removeAttribute(p):l.setAttribute(p,d===!0?"":d)}))})}function s(c){var g=c.state,f={popper:{position:g.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(g.elements.popper.style,f.popper),g.styles=f,g.elements.arrow&&Object.assign(g.elements.arrow.style,f.arrow),function(){Object.keys(g.elements).forEach(function(u){var v=g.elements[u],l=g.attributes[u]||{},p=Object.keys(g.styles.hasOwnProperty(u)?g.styles[u]:f[u]),d=p.reduce(function(h,m){return h[m]="",h},{});!(0,r.isHTMLElement)(v)||!(0,e.default)(v)||(Object.assign(v.style,d),Object.keys(l).forEach(function(h){v.removeAttribute(h)}))})}}var i=n.default={name:"applyStyles",enabled:!0,phase:"write",fn:a,effect:s,requires:["computeStyles"]}},52744:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=u(t(83104)),r=u(t(28811)),o=u(t(4206)),a=u(t(44896)),s=u(t(41199)),i=t(28595),c=u(t(43286)),g=u(t(81447)),f=t(46206);function u(h){return h&&h.__esModule?h:{default:h}}var v=function(){function h(m,E){return m=typeof m=="function"?m(Object.assign({},E.rects,{placement:E.placement})):m,(0,c.default)(typeof m!="number"?m:(0,g.default)(m,f.basePlacements))}return h}();function l(h){var m,E=h.state,A=h.name,I=h.options,T=E.elements.arrow,C=E.modifiersData.popperOffsets,S=(0,e.default)(E.placement),b=(0,s.default)(S),N=[f.left,f.right].indexOf(S)>=0,M=N?"height":"width";if(!(!T||!C)){var R=v(I.padding,E),L=(0,r.default)(T),B=b==="y"?f.top:f.left,U=b==="y"?f.bottom:f.right,x=E.rects.reference[M]+E.rects.reference[b]-C[b]-E.rects.popper[M],G=C[b]-E.rects.reference[b],Y=(0,a.default)(T),D=Y?b==="y"?Y.clientHeight||0:Y.clientWidth||0:0,V=x/2-G/2,j=R[B],K=D-L[M]-R[U],$=D/2-L[M]/2+V,W=(0,i.within)(j,$,K),et=b;E.modifiersData[A]=(m={},m[et]=W,m.centerOffset=W-$,m)}}function p(h){var m=h.state,E=h.options,A=E.element,I=A===void 0?"[data-popper-arrow]":A;I!=null&&(typeof I=="string"&&(I=m.elements.popper.querySelector(I),!I)||(0,o.default)(m.elements.popper,I)&&(m.elements.arrow=I))}var d=n.default={name:"arrow",enabled:!0,phase:"main",fn:l,effect:p,requires:["popperOffsets"],requiresIfExists:["preventOverflow"]}},59894:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.mapToStyles=l;var e=t(46206),r=f(t(44896)),o=f(t(95115)),a=f(t(40600)),s=f(t(16599)),i=f(t(83104)),c=f(t(45)),g=t(63618);function f(h){return h&&h.__esModule?h:{default:h}}var u={top:"auto",right:"auto",bottom:"auto",left:"auto"};function v(h,m){var E=h.x,A=h.y,I=m.devicePixelRatio||1;return{x:(0,g.round)(E*I)/I||0,y:(0,g.round)(A*I)/I||0}}function l(h){var m,E=h.popper,A=h.popperRect,I=h.placement,T=h.variation,C=h.offsets,S=h.position,b=h.gpuAcceleration,N=h.adaptive,M=h.roundOffsets,R=h.isFixed,L=C.x,B=L===void 0?0:L,U=C.y,x=U===void 0?0:U,G=typeof M=="function"?M({x:B,y:x}):{x:B,y:x};B=G.x,x=G.y;var Y=C.hasOwnProperty("x"),D=C.hasOwnProperty("y"),V=e.left,j=e.top,K=window;if(N){var $=(0,r.default)(E),W="clientHeight",et="clientWidth";if($===(0,o.default)(E)&&($=(0,a.default)(E),(0,s.default)($).position!=="static"&&S==="absolute"&&(W="scrollHeight",et="scrollWidth")),$=$,I===e.top||(I===e.left||I===e.right)&&T===e.end){j=e.bottom;var ft=R&&$===K&&K.visualViewport?K.visualViewport.height:$[W];x-=ft-A.height,x*=b?1:-1}if(I===e.left||(I===e.top||I===e.bottom)&&T===e.end){V=e.right;var dt=R&&$===K&&K.visualViewport?K.visualViewport.width:$[et];B-=dt-A.width,B*=b?1:-1}}var z=Object.assign({position:S},N&&u),J=M===!0?v({x:B,y:x},(0,o.default)(E)):{x:B,y:x};if(B=J.x,x=J.y,b){var nt;return Object.assign({},z,(nt={},nt[j]=D?"0":"",nt[V]=Y?"0":"",nt.transform=(K.devicePixelRatio||1)<=1?"translate("+B+"px, "+x+"px)":"translate3d("+B+"px, "+x+"px, 0)",nt))}return Object.assign({},z,(m={},m[j]=D?x+"px":"",m[V]=Y?B+"px":"",m.transform="",m))}function p(h){var m=h.state,E=h.options,A=E.gpuAcceleration,I=A===void 0?!0:A,T=E.adaptive,C=T===void 0?!0:T,S=E.roundOffsets,b=S===void 0?!0:S,N={placement:(0,i.default)(m.placement),variation:(0,c.default)(m.placement),popper:m.elements.popper,popperRect:m.rects.popper,gpuAcceleration:I,isFixed:m.options.strategy==="fixed"};m.modifiersData.popperOffsets!=null&&(m.styles.popper=Object.assign({},m.styles.popper,l(Object.assign({},N,{offsets:m.modifiersData.popperOffsets,position:m.options.strategy,adaptive:C,roundOffsets:b})))),m.modifiersData.arrow!=null&&(m.styles.arrow=Object.assign({},m.styles.arrow,l(Object.assign({},N,{offsets:m.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:b})))),m.attributes.popper=Object.assign({},m.attributes.popper,{"data-popper-placement":m.placement})}var d=n.default={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:p,data:{}}},36692:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(95115));function r(i){return i&&i.__esModule?i:{default:i}}var o={passive:!0};function a(i){var c=i.state,g=i.instance,f=i.options,u=f.scroll,v=u===void 0?!0:u,l=f.resize,p=l===void 0?!0:l,d=(0,e.default)(c.elements.popper),h=[].concat(c.scrollParents.reference,c.scrollParents.popper);return v&&h.forEach(function(m){m.addEventListener("scroll",g.update,o)}),p&&d.addEventListener("resize",g.update,o),function(){v&&h.forEach(function(m){m.removeEventListener("scroll",g.update,o)}),p&&d.removeEventListener("resize",g.update,o)}}var s=n.default={name:"eventListeners",enabled:!0,phase:"write",fn:function(){function i(){}return i}(),effect:a,data:{}}},23798:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=g(t(71376)),r=g(t(83104)),o=g(t(86459)),a=g(t(17633)),s=g(t(9041)),i=t(46206),c=g(t(45));function g(l){return l&&l.__esModule?l:{default:l}}function f(l){if((0,r.default)(l)===i.auto)return[];var p=(0,e.default)(l);return[(0,o.default)(l),p,(0,o.default)(p)]}function u(l){var p=l.state,d=l.options,h=l.name;if(!p.modifiersData[h]._skip){for(var m=d.mainAxis,E=m===void 0?!0:m,A=d.altAxis,I=A===void 0?!0:A,T=d.fallbackPlacements,C=d.padding,S=d.boundary,b=d.rootBoundary,N=d.altBoundary,M=d.flipVariations,R=M===void 0?!0:M,L=d.allowedAutoPlacements,B=p.options.placement,U=(0,r.default)(B),x=U===B,G=T||(x||!R?[(0,e.default)(B)]:f(B)),Y=[B].concat(G).reduce(function(rt,ht){return rt.concat((0,r.default)(ht)===i.auto?(0,s.default)(p,{placement:ht,boundary:S,rootBoundary:b,padding:C,flipVariations:R,allowedAutoPlacements:L}):ht)},[]),D=p.rects.reference,V=p.rects.popper,j=new Map,K=!0,$=Y[0],W=0;W=0,J=z?"width":"height",nt=(0,a.default)(p,{placement:et,boundary:S,rootBoundary:b,altBoundary:N,padding:C}),st=z?dt?i.right:i.left:dt?i.bottom:i.top;D[J]>V[J]&&(st=(0,e.default)(st));var it=(0,e.default)(st),pt=[];if(E&&pt.push(nt[ft]<=0),I&&pt.push(nt[st]<=0,nt[it]<=0),pt.every(function(rt){return rt})){$=et,K=!1;break}j.set(et,pt)}if(K)for(var Ot=R?3:1,Pt=function(){function rt(ht){var q=Y.find(function(_){var tt=j.get(_);if(tt)return tt.slice(0,ht).every(function(mt){return mt})});if(q)return $=q,"break"}return rt}(),Nt=Ot;Nt>0;Nt--){var vt=Pt(Nt);if(vt==="break")break}p.placement!==$&&(p.modifiersData[h]._skip=!0,p.placement=$,p.reset=!0)}}var v=n.default={name:"flip",enabled:!0,phase:"main",fn:u,requiresIfExists:["offset"],data:{_skip:!1}}},83761:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=o(t(17633));function o(g){return g&&g.__esModule?g:{default:g}}function a(g,f,u){return u===void 0&&(u={x:0,y:0}),{top:g.top-f.height-u.y,right:g.right-f.width+u.x,bottom:g.bottom-f.height+u.y,left:g.left-f.width-u.x}}function s(g){return[e.top,e.right,e.bottom,e.left].some(function(f){return g[f]>=0})}function i(g){var f=g.state,u=g.name,v=f.rects.reference,l=f.rects.popper,p=f.modifiersData.preventOverflow,d=(0,r.default)(f,{elementContext:"reference"}),h=(0,r.default)(f,{altBoundary:!0}),m=a(d,v),E=a(h,l,p),A=s(m),I=s(E);f.modifiersData[u]={referenceClippingOffsets:m,popperEscapeOffsets:E,isReferenceHidden:A,hasPopperEscaped:I},f.attributes.popper=Object.assign({},f.attributes.popper,{"data-popper-reference-hidden":A,"data-popper-escaped":I})}var c=n.default={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:i}},39805:function(y,n,t){"use strict";n.__esModule=!0,n.preventOverflow=n.popperOffsets=n.offset=n.hide=n.flip=n.eventListeners=n.computeStyles=n.arrow=n.applyStyles=void 0;var e=u(t(19975));n.applyStyles=e.default;var r=u(t(52744));n.arrow=r.default;var o=u(t(59894));n.computeStyles=o.default;var a=u(t(36692));n.eventListeners=a.default;var s=u(t(23798));n.flip=s.default;var i=u(t(83761));n.hide=i.default;var c=u(t(61410));n.offset=c.default;var g=u(t(40107));n.popperOffsets=g.default;var f=u(t(75137));n.preventOverflow=f.default;function u(v){return v&&v.__esModule?v:{default:v}}},61410:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0,n.distanceAndSkiddingToXY=a;var e=o(t(83104)),r=t(46206);function o(c){return c&&c.__esModule?c:{default:c}}function a(c,g,f){var u=(0,e.default)(c),v=[r.left,r.top].indexOf(u)>=0?-1:1,l=typeof f=="function"?f(Object.assign({},g,{placement:c})):f,p=l[0],d=l[1];return p=p||0,d=(d||0)*v,[r.left,r.right].indexOf(u)>=0?{x:d,y:p}:{x:p,y:d}}function s(c){var g=c.state,f=c.options,u=c.name,v=f.offset,l=v===void 0?[0,0]:v,p=r.placements.reduce(function(E,A){return E[A]=a(A,g.rects,l),E},{}),d=p[g.placement],h=d.x,m=d.y;g.modifiersData.popperOffsets!=null&&(g.modifiersData.popperOffsets.x+=h,g.modifiersData.popperOffsets.y+=m),g.modifiersData[u]=p}var i=n.default={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:s}},40107:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=r(t(89951));function r(s){return s&&s.__esModule?s:{default:s}}function o(s){var i=s.state,c=s.name;i.modifiersData[c]=(0,e.default)({reference:i.rects.reference,element:i.rects.popper,strategy:"absolute",placement:i.placement})}var a=n.default={name:"popperOffsets",enabled:!0,phase:"read",fn:o,data:{}}},75137:function(y,n,t){"use strict";n.__esModule=!0,n.default=void 0;var e=t(46206),r=l(t(83104)),o=l(t(41199)),a=l(t(28066)),s=t(28595),i=l(t(28811)),c=l(t(44896)),g=l(t(17633)),f=l(t(45)),u=l(t(34780)),v=t(63618);function l(h){return h&&h.__esModule?h:{default:h}}function p(h){var m=h.state,E=h.options,A=h.name,I=E.mainAxis,T=I===void 0?!0:I,C=E.altAxis,S=C===void 0?!1:C,b=E.boundary,N=E.rootBoundary,M=E.altBoundary,R=E.padding,L=E.tether,B=L===void 0?!0:L,U=E.tetherOffset,x=U===void 0?0:U,G=(0,g.default)(m,{boundary:b,rootBoundary:N,padding:R,altBoundary:M}),Y=(0,r.default)(m.placement),D=(0,f.default)(m.placement),V=!D,j=(0,o.default)(Y),K=(0,a.default)(j),$=m.modifiersData.popperOffsets,W=m.rects.reference,et=m.rects.popper,ft=typeof x=="function"?x(Object.assign({},m.rects,{placement:m.placement})):x,dt=typeof ft=="number"?{mainAxis:ft,altAxis:ft}:Object.assign({mainAxis:0,altAxis:0},ft),z=m.modifiersData.offset?m.modifiersData.offset[m.placement]:null,J={x:0,y:0};if($){if(T){var nt,st=j==="y"?e.top:e.left,it=j==="y"?e.bottom:e.right,pt=j==="y"?"height":"width",Ot=$[j],Pt=Ot+G[st],Nt=Ot-G[it],vt=B?-et[pt]/2:0,rt=D===e.start?W[pt]:et[pt],ht=D===e.start?-et[pt]:-W[pt],q=m.elements.arrow,_=B&&q?(0,i.default)(q):{width:0,height:0},tt=m.modifiersData["arrow#persistent"]?m.modifiersData["arrow#persistent"].padding:(0,u.default)(),mt=tt[st],at=tt[it],ot=(0,s.within)(0,W[pt],_[pt]),At=V?W[pt]/2-vt-ot-mt-dt.mainAxis:rt-ot-mt-dt.mainAxis,X=V?-W[pt]/2+vt+ot+at+dt.mainAxis:ht+ot+at+dt.mainAxis,ut=m.elements.arrow&&(0,c.default)(m.elements.arrow),yt=ut?j==="y"?ut.clientTop||0:ut.clientLeft||0:0,Tt=(nt=z==null?void 0:z[j])!=null?nt:0,wt=Ot+At-Tt-yt,jt=Ot+X-Tt,Ct=(0,s.within)(B?(0,v.min)(Pt,wt):Pt,Ot,B?(0,v.max)(Nt,jt):Nt);$[j]=Ct,J[j]=Ct-Ot}if(S){var lt,gt=j==="x"?e.top:e.left,bt=j==="x"?e.bottom:e.right,St=$[K],It=K==="y"?"height":"width",Ft=St+G[gt],Vt=St-G[bt],Gt=[e.top,e.left].indexOf(Y)!==-1,Ht=(lt=z==null?void 0:z[K])!=null?lt:0,Kt=Gt?Ft:St-W[It]-et[It]-Ht+dt.altAxis,Wt=Gt?St+W[It]+et[It]-Ht-dt.altAxis:Vt,Qt=B&&Gt?(0,s.withinMaxClamp)(Kt,St,Wt):(0,s.within)(B?Kt:Ft,St,B?Wt:Vt);$[K]=Qt,J[K]=Qt-St}m.modifiersData[A]=J}}var d=n.default={name:"preventOverflow",enabled:!0,phase:"main",fn:p,requiresIfExists:["offset"]}},2473:function(y,n,t){"use strict";n.__esModule=!0,n.defaultModifiers=n.createPopper=void 0;var e=t(96376);n.popperGenerator=e.popperGenerator,n.detectOverflow=e.detectOverflow;var r=i(t(36692)),o=i(t(40107)),a=i(t(59894)),s=i(t(19975));function i(f){return f&&f.__esModule?f:{default:f}}var c=n.defaultModifiers=[r.default,o.default,a.default,s.default],g=n.createPopper=(0,e.popperGenerator)({defaultModifiers:c})},83312:function(y,n,t){"use strict";n.__esModule=!0;var e={createPopper:!0,createPopperLite:!0,defaultModifiers:!0,popperGenerator:!0,detectOverflow:!0};n.defaultModifiers=n.createPopperLite=n.createPopper=void 0;var r=t(96376);n.popperGenerator=r.popperGenerator,n.detectOverflow=r.detectOverflow;var o=d(t(36692)),a=d(t(40107)),s=d(t(59894)),i=d(t(19975)),c=d(t(61410)),g=d(t(23798)),f=d(t(75137)),u=d(t(52744)),v=d(t(83761)),l=t(2473);n.createPopperLite=l.createPopper;var p=t(39805);Object.keys(p).forEach(function(E){E==="default"||E==="__esModule"||Object.prototype.hasOwnProperty.call(e,E)||E in n&&n[E]===p[E]||(n[E]=p[E])});function d(E){return E&&E.__esModule?E:{default:E}}var h=n.defaultModifiers=[o.default,a.default,s.default,i.default,c.default,g.default,f.default,u.default,v.default],m=n.createPopperLite=n.createPopper=(0,r.popperGenerator)({defaultModifiers:h})},9041:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(45)),r=t(46206),o=s(t(17633)),a=s(t(83104));function s(c){return c&&c.__esModule?c:{default:c}}function i(c,g){g===void 0&&(g={});var f=g,u=f.placement,v=f.boundary,l=f.rootBoundary,p=f.padding,d=f.flipVariations,h=f.allowedAutoPlacements,m=h===void 0?r.placements:h,E=(0,e.default)(u),A=E?d?r.variationPlacements:r.variationPlacements.filter(function(C){return(0,e.default)(C)===E}):r.basePlacements,I=A.filter(function(C){return m.indexOf(C)>=0});I.length===0&&(I=A);var T=I.reduce(function(C,S){return C[S]=(0,o.default)(c,{placement:S,boundary:v,rootBoundary:l,padding:p})[(0,a.default)(S)],C},{});return Object.keys(T).sort(function(C,S){return T[C]-T[S]})}},89951:function(y,n,t){"use strict";n.__esModule=!0,n.default=i;var e=s(t(83104)),r=s(t(45)),o=s(t(41199)),a=t(46206);function s(c){return c&&c.__esModule?c:{default:c}}function i(c){var g=c.reference,f=c.element,u=c.placement,v=u?(0,e.default)(u):null,l=u?(0,r.default)(u):null,p=g.x+g.width/2-f.width/2,d=g.y+g.height/2-f.height/2,h;switch(v){case a.top:h={x:p,y:g.y-f.height};break;case a.bottom:h={x:p,y:g.y+g.height};break;case a.right:h={x:g.x+g.width,y:d};break;case a.left:h={x:g.x-f.width,y:d};break;default:h={x:g.x,y:g.y}}var m=v?(0,o.default)(v):null;if(m!=null){var E=m==="y"?"height":"width";switch(l){case a.start:h[m]=h[m]-(g[E]/2-f[E]/2);break;case a.end:h[m]=h[m]+(g[E]/2-f[E]/2);break;default:}}return h}},10579:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r;return function(){return r||(r=new Promise(function(o){Promise.resolve().then(function(){r=void 0,o(e())})})),r}}},17633:function(y,n,t){"use strict";n.__esModule=!0,n.default=v;var e=u(t(49035)),r=u(t(40600)),o=u(t(37786)),a=u(t(89951)),s=u(t(81666)),i=t(46206),c=t(75573),g=u(t(43286)),f=u(t(81447));function u(l){return l&&l.__esModule?l:{default:l}}function v(l,p){p===void 0&&(p={});var d=p,h=d.placement,m=h===void 0?l.placement:h,E=d.strategy,A=E===void 0?l.strategy:E,I=d.boundary,T=I===void 0?i.clippingParents:I,C=d.rootBoundary,S=C===void 0?i.viewport:C,b=d.elementContext,N=b===void 0?i.popper:b,M=d.altBoundary,R=M===void 0?!1:M,L=d.padding,B=L===void 0?0:L,U=(0,g.default)(typeof B!="number"?B:(0,f.default)(B,i.basePlacements)),x=N===i.popper?i.reference:i.popper,G=l.rects.popper,Y=l.elements[R?x:N],D=(0,e.default)((0,c.isElement)(Y)?Y:Y.contextElement||(0,r.default)(l.elements.popper),T,S,A),V=(0,o.default)(l.elements.reference),j=(0,a.default)({reference:V,element:G,strategy:"absolute",placement:m}),K=(0,s.default)(Object.assign({},G,j)),$=N===i.popper?K:V,W={top:D.top-$.top+U.top,bottom:$.bottom-D.bottom+U.bottom,left:D.left-$.left+U.left,right:$.right-D.right+U.right},et=l.modifiersData.offset;if(N===i.popper&&et){var ft=et[m];Object.keys(W).forEach(function(dt){var z=[i.right,i.bottom].indexOf(dt)>=0?1:-1,J=[i.top,i.bottom].indexOf(dt)>=0?"y":"x";W[dt]+=ft[J]*z})}return W}},81447:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e,r){return r.reduce(function(o,a){return o[a]=e,o},{})}},28066:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e==="x"?"y":"x"}},83104:function(y,n,t){"use strict";n.__esModule=!0,n.default=r;var e=t(46206);function r(o){return o.split("-")[0]}},34780:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){return{top:0,right:0,bottom:0,left:0}}},41199:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return["top","bottom"].indexOf(e)>=0?"x":"y"}},71376:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={left:"right",right:"left",bottom:"top",top:"bottom"};function e(r){return r.replace(/left|right|bottom|top/g,function(o){return t[o]})}},86459:function(y,n){"use strict";n.__esModule=!0,n.default=e;var t={start:"end",end:"start"};function e(r){return r.replace(/start|end/g,function(o){return t[o]})}},45:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return e.split("-")[1]}},63618:function(y,n){"use strict";n.__esModule=!0,n.round=n.min=n.max=void 0;var t=n.max=Math.max,e=n.min=Math.min,r=n.round=Math.round},56500:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){var r=e.reduce(function(o,a){var s=o[a.name];return o[a.name]=s?Object.assign({},s,a,{options:Object.assign({},s.options,a.options),data:Object.assign({},s.data,a.data)}):a,o},{});return Object.keys(r).map(function(o){return r[o]})}},43286:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=r(t(34780));function r(a){return a&&a.__esModule?a:{default:a}}function o(a){return Object.assign({},(0,e.default)(),a)}},33118:function(y,n,t){"use strict";n.__esModule=!0,n.default=o;var e=t(46206);function r(a){var s=new Map,i=new Set,c=[];a.forEach(function(f){s.set(f.name,f)});function g(f){i.add(f.name);var u=[].concat(f.requires||[],f.requiresIfExists||[]);u.forEach(function(v){if(!i.has(v)){var l=s.get(v);l&&g(l)}}),c.push(f)}return a.forEach(function(f){i.has(f.name)||g(f)}),c}function o(a){var s=r(a);return e.modifierPhases.reduce(function(i,c){return i.concat(s.filter(function(g){return g.phase===c}))},[])}},81666:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(e){return Object.assign({},e,{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}},35366:function(y,n){"use strict";n.__esModule=!0,n.default=t;function t(){var e=navigator.userAgentData;return e!=null&&e.brands&&Array.isArray(e.brands)?e.brands.map(function(r){return r.brand+"/"+r.version}).join(" "):navigator.userAgent}},28595:function(y,n,t){"use strict";n.__esModule=!0,n.within=r,n.withinMaxClamp=o;var e=t(63618);function r(a,s,i){return(0,e.max)(a,(0,e.min)(s,i))}function o(a,s,i){var c=r(a,s,i);return c>i?i:c}},22734:function(y){"use strict";/*! @license DOMPurify 2.5.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.5.0/LICENSE */(function(n,t){y.exports=t()})(void 0,function(){"use strict";function n(X){"@babel/helpers - typeof";return n=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(ut){return typeof ut}:function(ut){return ut&&typeof Symbol=="function"&&ut.constructor===Symbol&&ut!==Symbol.prototype?"symbol":typeof ut},n(X)}function t(X,ut){return t=Object.setPrototypeOf||function(){function yt(Tt,wt){return Tt.__proto__=wt,Tt}return yt}(),t(X,ut)}function e(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){})),!0}catch(X){return!1}}function r(X,ut,yt){return e()?r=Reflect.construct:r=function(){function Tt(wt,jt,Ct){var lt=[null];lt.push.apply(lt,jt);var gt=Function.bind.apply(wt,lt),bt=new gt;return Ct&&t(bt,Ct.prototype),bt}return Tt}(),r.apply(null,arguments)}function o(X){return a(X)||s(X)||i(X)||g()}function a(X){if(Array.isArray(X))return c(X)}function s(X){if(typeof Symbol!="undefined"&&X[Symbol.iterator]!=null||X["@@iterator"]!=null)return Array.from(X)}function i(X,ut){if(X){if(typeof X=="string")return c(X,ut);var yt=Object.prototype.toString.call(X).slice(8,-1);if(yt==="Object"&&X.constructor&&(yt=X.constructor.name),yt==="Map"||yt==="Set")return Array.from(X);if(yt==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(yt))return c(X,ut)}}function c(X,ut){(ut==null||ut>X.length)&&(ut=X.length);for(var yt=0,Tt=new Array(ut);yt1?yt-1:0),wt=1;wt/gm),Pt=h(/\${[\w\W]*}/gm),Nt=h(/^data-[\-\w.\u00B7-\uFFFF]/),vt=h(/^aria-[\-\w]+$/),rt=h(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),ht=h(/^(?:\w+script|data):/i),q=h(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),_=h(/^html$/i),tt=h(/^[a-z][.\w]*(-[.\w]+)+$/i),mt=function(){function X(){return typeof window=="undefined"?null:window}return X}(),at=function(){function X(ut,yt){if(n(ut)!=="object"||typeof ut.createPolicy!="function")return null;var Tt=null,wt="data-tt-policy-suffix";yt.currentScript&&yt.currentScript.hasAttribute(wt)&&(Tt=yt.currentScript.getAttribute(wt));var jt="dompurify"+(Tt?"#"+Tt:"");try{return ut.createPolicy(jt,{createHTML:function(){function Ct(lt){return lt}return Ct}(),createScriptURL:function(){function Ct(lt){return lt}return Ct}()})}catch(Ct){return null}}return X}();function ot(){var X=arguments.length>0&&arguments[0]!==void 0?arguments[0]:mt(),ut=function(){function O(P){return ot(P)}return O}();if(ut.version="2.5.0",ut.removed=[],!X||!X.document||X.document.nodeType!==9)return ut.isSupported=!1,ut;var yt=X.document,Tt=X.document,wt=X.DocumentFragment,jt=X.HTMLTemplateElement,Ct=X.Node,lt=X.Element,gt=X.NodeFilter,bt=X.NamedNodeMap,St=bt===void 0?X.NamedNodeMap||X.MozNamedAttrMap:bt,It=X.HTMLFormElement,Ft=X.DOMParser,Vt=X.trustedTypes,Gt=lt.prototype,Ht=j(Gt,"cloneNode"),Kt=j(Gt,"nextSibling"),Wt=j(Gt,"childNodes"),Qt=j(Gt,"parentNode");if(typeof jt=="function"){var Le=Tt.createElement("template");Le.content&&Le.content.ownerDocument&&(Tt=Le.content.ownerDocument)}var _t=at(Vt,yt),Pe=_t?_t.createHTML(""):"",Ne=Tt,me=Ne.implementation,ye=Ne.createNodeIterator,an=Ne.createDocumentFragment,un=Ne.getElementsByTagName,Tn=yt.importNode,Ye={};try{Ye=V(Tt).documentMode?Tt.documentMode:{}}catch(O){}var re={};ut.isSupported=typeof Qt=="function"&&me&&me.createHTMLDocument!==void 0&&Ye!==9;var $e=pt,We=Ot,Be=Pt,sn=Nt,An=vt,cn=ht,ln=q,In=tt,Se=rt,zt=null,te=D({},[].concat(o(K),o($),o(W),o(ft),o(z))),Yt=null,Ee=D({},[].concat(o(J),o(nt),o(st),o(it))),kt=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),le=null,Ce=null,He=!0,ze=!0,fn=!1,dn=!0,be=!1,De=!0,fe=!1,Fe=!1,xe=!1,de=!1,Xt=!1,Ve=!1,vn=!0,ke=!1,hn="user-content-",ue=!0,Me=!1,Te={},Ae=null,Xe=D({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]),Je=null,gn=D({},["audio","video","img","source","image","track"]),je=null,pn=D({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ee="http://www.w3.org/1998/Math/MathML",Ue="http://www.w3.org/2000/svg",se="http://www.w3.org/1999/xhtml",Ie=se,Ze=!1,Qe=null,On=D({},[ee,Ue,se],N),ce,Pn=["application/xhtml+xml","text/html"],mn="text/html",Jt,Oe=null,Nn=Tt.createElement("form"),yn=function(){function O(P){return P instanceof RegExp||P instanceof Function}return O}(),qe=function(){function O(P){Oe&&Oe===P||((!P||n(P)!=="object")&&(P={}),P=V(P),ce=Pn.indexOf(P.PARSER_MEDIA_TYPE)===-1?ce=mn:ce=P.PARSER_MEDIA_TYPE,Jt=ce==="application/xhtml+xml"?N:b,zt="ALLOWED_TAGS"in P?D({},P.ALLOWED_TAGS,Jt):te,Yt="ALLOWED_ATTR"in P?D({},P.ALLOWED_ATTR,Jt):Ee,Qe="ALLOWED_NAMESPACES"in P?D({},P.ALLOWED_NAMESPACES,N):On,je="ADD_URI_SAFE_ATTR"in P?D(V(pn),P.ADD_URI_SAFE_ATTR,Jt):pn,Je="ADD_DATA_URI_TAGS"in P?D(V(gn),P.ADD_DATA_URI_TAGS,Jt):gn,Ae="FORBID_CONTENTS"in P?D({},P.FORBID_CONTENTS,Jt):Xe,le="FORBID_TAGS"in P?D({},P.FORBID_TAGS,Jt):{},Ce="FORBID_ATTR"in P?D({},P.FORBID_ATTR,Jt):{},Te="USE_PROFILES"in P?P.USE_PROFILES:!1,He=P.ALLOW_ARIA_ATTR!==!1,ze=P.ALLOW_DATA_ATTR!==!1,fn=P.ALLOW_UNKNOWN_PROTOCOLS||!1,dn=P.ALLOW_SELF_CLOSE_IN_ATTR!==!1,be=P.SAFE_FOR_TEMPLATES||!1,De=P.SAFE_FOR_XML!==!1,fe=P.WHOLE_DOCUMENT||!1,de=P.RETURN_DOM||!1,Xt=P.RETURN_DOM_FRAGMENT||!1,Ve=P.RETURN_TRUSTED_TYPE||!1,xe=P.FORCE_BODY||!1,vn=P.SANITIZE_DOM!==!1,ke=P.SANITIZE_NAMED_PROPS||!1,ue=P.KEEP_CONTENT!==!1,Me=P.IN_PLACE||!1,Se=P.ALLOWED_URI_REGEXP||Se,Ie=P.NAMESPACE||se,kt=P.CUSTOM_ELEMENT_HANDLING||{},P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(kt.tagNameCheck=P.CUSTOM_ELEMENT_HANDLING.tagNameCheck),P.CUSTOM_ELEMENT_HANDLING&&yn(P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(kt.attributeNameCheck=P.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),P.CUSTOM_ELEMENT_HANDLING&&typeof P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements=="boolean"&&(kt.allowCustomizedBuiltInElements=P.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),be&&(ze=!1),Xt&&(de=!0),Te&&(zt=D({},o(z)),Yt=[],Te.html===!0&&(D(zt,K),D(Yt,J)),Te.svg===!0&&(D(zt,$),D(Yt,nt),D(Yt,it)),Te.svgFilters===!0&&(D(zt,W),D(Yt,nt),D(Yt,it)),Te.mathMl===!0&&(D(zt,ft),D(Yt,st),D(Yt,it))),P.ADD_TAGS&&(zt===te&&(zt=V(zt)),D(zt,P.ADD_TAGS,Jt)),P.ADD_ATTR&&(Yt===Ee&&(Yt=V(Yt)),D(Yt,P.ADD_ATTR,Jt)),P.ADD_URI_SAFE_ATTR&&D(je,P.ADD_URI_SAFE_ATTR,Jt),P.FORBID_CONTENTS&&(Ae===Xe&&(Ae=V(Ae)),D(Ae,P.FORBID_CONTENTS,Jt)),ue&&(zt["#text"]=!0),fe&&D(zt,["html","head","body"]),zt.table&&(D(zt,["tbody"]),delete le.tbody),d&&d(P),Oe=P)}return O}(),Sn=D({},["mi","mo","mn","ms","mtext"]),oe=D({},["foreignobject","desc","title","annotation-xml"]),Ge=D({},["title","style","font","a","script"]),Re=D({},$);D(Re,W),D(Re,et);var _e=D({},ft);D(_e,dt);var Mn=function(){function O(P){var w=Qt(P);(!w||!w.tagName)&&(w={namespaceURI:Ie,tagName:"template"});var F=b(P.tagName),H=b(w.tagName);return Qe[P.namespaceURI]?P.namespaceURI===Ue?w.namespaceURI===se?F==="svg":w.namespaceURI===ee?F==="svg"&&(H==="annotation-xml"||Sn[H]):!!Re[F]:P.namespaceURI===ee?w.namespaceURI===se?F==="math":w.namespaceURI===Ue?F==="math"&&oe[H]:!!_e[F]:P.namespaceURI===se?w.namespaceURI===Ue&&!oe[H]||w.namespaceURI===ee&&!Sn[H]?!1:!_e[F]&&(Ge[F]||!Re[F]):!!(ce==="application/xhtml+xml"&&Qe[P.namespaceURI]):!1}return O}(),ne=function(){function O(P){S(ut.removed,{element:P});try{P.parentNode.removeChild(P)}catch(w){try{P.outerHTML=Pe}catch(F){P.remove()}}}return O}(),Ke=function(){function O(P,w){try{S(ut.removed,{attribute:w.getAttributeNode(P),from:w})}catch(F){S(ut.removed,{attribute:null,from:w})}if(w.removeAttribute(P),P==="is"&&!Yt[P])if(de||Xt)try{ne(w)}catch(F){}else try{w.setAttribute(P,"")}catch(F){}}return O}(),En=function(){function O(P){var w,F;if(xe)P=""+P;else{var H=M(P,/^[\r\n\t ]+/);F=H&&H[0]}ce==="application/xhtml+xml"&&Ie===se&&(P=''+P+"");var Z=_t?_t.createHTML(P):P;if(Ie===se)try{w=new Ft().parseFromString(Z,ce)}catch(ct){}if(!w||!w.documentElement){w=me.createDocument(Ie,"template",null);try{w.documentElement.innerHTML=Ze?Pe:Z}catch(ct){}}var Q=w.body||w.documentElement;return P&&F&&Q.insertBefore(Tt.createTextNode(F),Q.childNodes[0]||null),Ie===se?un.call(w,fe?"html":"body")[0]:fe?w.documentElement:Q}return O}(),we=function(){function O(P){return ye.call(P.ownerDocument||P,P,gt.SHOW_ELEMENT|gt.SHOW_COMMENT|gt.SHOW_TEXT|gt.SHOW_PROCESSING_INSTRUCTION|gt.SHOW_CDATA_SECTION,null,!1)}return O}(),Rn=function(){function O(P){return P instanceof It&&(typeof P.nodeName!="string"||typeof P.textContent!="string"||typeof P.removeChild!="function"||!(P.attributes instanceof St)||typeof P.removeAttribute!="function"||typeof P.setAttribute!="function"||typeof P.namespaceURI!="string"||typeof P.insertBefore!="function"||typeof P.hasChildNodes!="function")}return O}(),he=function(){function O(P){return n(Ct)==="object"?P instanceof Ct:P&&n(P)==="object"&&typeof P.nodeType=="number"&&typeof P.nodeName=="string"}return O}(),ae=function(){function O(P,w,F){re[P]&&T(re[P],function(H){H.call(ut,w,F,Oe)})}return O}(),Cn=function(){function O(P){var w;if(ae("beforeSanitizeElements",P,null),Rn(P)||U(/[\u0080-\uFFFF]/,P.nodeName))return ne(P),!0;var F=Jt(P.nodeName);if(ae("uponSanitizeElement",P,{tagName:F,allowedTags:zt}),P.hasChildNodes()&&!he(P.firstElementChild)&&(!he(P.content)||!he(P.content.firstElementChild))&&U(/<[/\w]/g,P.innerHTML)&&U(/<[/\w]/g,P.textContent)||F==="select"&&U(/